1 /* -*- Mode: C; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 3 -*- */
2
3 /*
4 * gimv_dlist.c - Pair of reorderable stack list widget.
5 * Copyright (C) 2001-2002 Takuro Ashie
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 3S30, Boston, MA 02111-1307, USA.
20 *
21 * $Id: gimv_dlist.c,v 1.6 2004/09/22 15:37:11 makeinu Exp $
22 */
23
24 #include "gimv_dlist.h"
25
26 #include <string.h>
27 #include <stdlib.h>
28
29 #include "gtk2-compat.h"
30 #include "intl.h" /* FIXME */
31
32
33 enum {
34 ENABLED_LIST_UPDATED_SIGNAL,
35 LAST_SIGNAL
36 };
37
38
39 #if (GTK_MAJOR_VERSION >= 2)
40 #define list_widget_get_row_num(widget) \
41 gtk_tree_model_iter_n_children (gtk_tree_view_get_model (GTK_TREE_VIEW (widget)), NULL);
42 #else
43 #define list_widget_get_row_num(widget) GTK_CLIST (widget)->rows;
44 #endif /* (GTK_MAJOR_VERSION >= 2) */
45
46
47 static void gimv_dlist_init (GimvDList *dslist);
48 static void gimv_dlist_class_init (GimvDListClass *klass);
49
50 /* object class functions */
51 #if (GTK_MAJOR_VERSION >= 2)
52 static void gimv_dlist_finalize (GObject *object);
53 #else /* (GTK_MAJOR_VERSION >= 2) */
54 static void gimv_dlist_finalize (GtkObject *object);
55 #endif /* (GTK_MAJOR_VERSION >= 2) */
56
57 /* private functions */
58 static void gimv_dlist_enabled_list_updated (GimvDList *dslist);
59 static void gimv_dlist_set_sensitive (GimvDList *dslist);
60 static GtkWidget *gimv_dlist_create_list_widget (GimvDList *dslist,
61 gboolean reorderble);
62
63
64 static GtkHBoxClass *parent_class = NULL;
65 static gint gimv_dlist_signals[LAST_SIGNAL] = {0};
66
67
68 GtkType
gimv_dlist_get_type(void)69 gimv_dlist_get_type (void)
70 {
71 static GtkType gimv_dlist_type = 0;
72
73 #if (GTK_MAJOR_VERSION >= 2)
74 if (!gimv_dlist_type) {
75 static const GTypeInfo gimv_dlist_info = {
76 sizeof (GimvDListClass),
77 NULL, /* base_init */
78 NULL, /* base_finalize */
79 (GClassInitFunc) gimv_dlist_class_init,
80 NULL, /* class_finalize */
81 NULL, /* class_data */
82 sizeof (GimvDList),
83 0, /* n_preallocs */
84 (GInstanceInitFunc) gimv_dlist_init,
85 };
86
87 gimv_dlist_type = g_type_register_static (GTK_TYPE_HBOX,
88 "GimvDList",
89 &gimv_dlist_info,
90 0);
91 }
92 #else /* (GTK_MAJOR_VERSION >= 2) */
93 if (!gimv_dlist_type) {
94 static const GtkTypeInfo gimv_dlist_info = {
95 "GimvDList",
96 sizeof (GimvDList),
97 sizeof (GimvDListClass),
98 (GtkClassInitFunc) gimv_dlist_class_init,
99 (GtkObjectInitFunc) gimv_dlist_init,
100 NULL,
101 NULL,
102 (GtkClassInitFunc) NULL,
103 };
104
105 gimv_dlist_type = gtk_type_unique (gtk_hbox_get_type (),
106 &gimv_dlist_info);
107 }
108 #endif /* (GTK_MAJOR_VERSION >= 2) */
109
110 return gimv_dlist_type;
111 }
112
113
114 static void
gimv_dlist_init(GimvDList * dslist)115 gimv_dlist_init (GimvDList *dslist)
116 {
117 dslist->clist1 = NULL;
118 dslist->clist2 = NULL;
119 dslist->add_button = NULL;
120 dslist->del_button = NULL;
121 dslist->up_button = NULL;
122 dslist->down_button = NULL;
123
124 dslist->clist1_rows = 0;
125 dslist->clist1_selected = -1;
126 dslist->clist1_dest_row = -1;
127 dslist->clist2_rows = 0;
128 dslist->clist2_selected = -1;
129 dslist->clist2_dest_row = -1;
130 dslist->available_list = NULL;
131 }
132
133
134 static void
gimv_dlist_class_init(GimvDListClass * klass)135 gimv_dlist_class_init (GimvDListClass *klass)
136 {
137 GtkObjectClass *object_class;
138
139 object_class = (GtkObjectClass *) klass;
140 parent_class = gtk_type_class (gtk_hbox_get_type ());
141
142 gimv_dlist_signals[ENABLED_LIST_UPDATED_SIGNAL]
143 = gtk_signal_new ("enabled-list-updated",
144 GTK_RUN_FIRST,
145 GTK_CLASS_TYPE(object_class),
146 GTK_SIGNAL_OFFSET (GimvDListClass, enabled_list_updated),
147 gtk_signal_default_marshaller,
148 GTK_TYPE_NONE, 0);
149
150 gtk_object_class_add_signals (object_class, gimv_dlist_signals, LAST_SIGNAL);
151
152 OBJECT_CLASS_SET_FINALIZE_FUNC (klass, gimv_dlist_finalize);
153 }
154
155
156
157 /*******************************************************************************
158 *
159 * Object Class functions.
160 *
161 *******************************************************************************/
162 static void
163 #ifdef USE_GTK2
gimv_dlist_finalize(GObject * object)164 gimv_dlist_finalize (GObject *object)
165 #else
166 gimv_dlist_finalize (GtkObject *object)
167 #endif
168 {
169 GimvDList *dslist = GIMV_DLIST (object);
170
171 g_list_foreach (dslist->available_list, (GFunc) g_free, NULL);
172 g_list_free (dslist->available_list);
173 dslist->available_list = NULL;
174
175 OBJECT_CLASS_FINALIZE_SUPER (parent_class, object);
176 }
177
178
179
180 /*******************************************************************************
181 *
182 * Callback functions for child widget.
183 *
184 *******************************************************************************/
185
186 #if (GTK_MAJOR_VERSION >= 2)
187
188 static void
cb_gimv_dlist_cursor_changed(GtkTreeView * treeview,gpointer data)189 cb_gimv_dlist_cursor_changed (GtkTreeView *treeview, gpointer data)
190 {
191 GimvDList *dslist = data;
192 GtkTreeSelection *selection;
193 GtkTreeModel *model = NULL;
194 GtkTreeIter iter;
195 gint selected;
196 gboolean success;
197
198 g_return_if_fail (treeview);
199 g_return_if_fail (dslist);
200
201 selection = gtk_tree_view_get_selection (treeview);
202 success = gtk_tree_selection_get_selected (selection, &model, &iter);
203
204 if (success) {
205 GtkTreePath *treepath = gtk_tree_model_get_path (model, &iter);
206 gchar *path = gtk_tree_path_to_string (treepath);
207
208 selected = atoi (path);
209
210 gtk_tree_path_free (treepath);
211 g_free (path);
212 } else {
213 selected = -1;
214 }
215
216 if (treeview == GTK_TREE_VIEW (dslist->clist1))
217 dslist->clist1_selected = selected;
218 else if (treeview == GTK_TREE_VIEW (dslist->clist2))
219 dslist->clist2_selected = selected;
220
221 gimv_dlist_set_sensitive (dslist);
222 }
223
224
225 static void
cb_gimv_dlist_row_changed(GtkTreeModel * model,GtkTreePath * treepath,GtkTreeIter * iter,gpointer data)226 cb_gimv_dlist_row_changed (GtkTreeModel *model,
227 GtkTreePath *treepath,
228 GtkTreeIter *iter,
229 gpointer data)
230 {
231 GimvDList *dslist = data;
232
233 gimv_dlist_enabled_list_updated (dslist);
234 }
235
236
237 static void
cb_gimv_dlist_row_deleted(GtkTreeModel * model,GtkTreePath * treepath,gpointer data)238 cb_gimv_dlist_row_deleted (GtkTreeModel *model,
239 GtkTreePath *treepath,
240 gpointer data)
241 {
242 GimvDList *dslist = data;
243
244 gimv_dlist_enabled_list_updated (dslist);
245 if (dslist->clist2_dest_row < 0) {
246 dslist->clist2_selected = -1;
247 gimv_dlist_set_sensitive (dslist);
248 }
249 dslist->clist2_dest_row = -1;
250 }
251
252 #else /* (GTK_MAJOR_VERSION >= 2) */
253
254 static void
cb_gimv_dlist_select_row(GtkCList * clist,gint row,gint col,GdkEventButton * event,gpointer data)255 cb_gimv_dlist_select_row (GtkCList *clist, gint row, gint col,
256 GdkEventButton *event, gpointer data)
257 {
258 GimvDList *dslist = data;
259
260 g_return_if_fail (clist);
261 g_return_if_fail (data);
262
263 if (GTK_WIDGET (clist) == dslist->clist1)
264 dslist->clist1_selected = row;
265 else if (GTK_WIDGET (clist) == dslist->clist2)
266 dslist->clist2_selected = row;
267
268 gimv_dlist_set_sensitive (dslist);
269 }
270
271
272 static void
cb_gimv_dlist_unselect_row(GtkCList * clist,gint row,gint col,GdkEventButton * event,gpointer data)273 cb_gimv_dlist_unselect_row (GtkCList *clist, gint row, gint col,
274 GdkEventButton *event, gpointer data)
275 {
276 GimvDList *dslist = data;
277
278 g_return_if_fail (clist);
279 g_return_if_fail (data);
280
281 if (GTK_WIDGET (clist) == dslist->clist1)
282 dslist->clist1_selected = -1;
283 else if (GTK_WIDGET (clist) == dslist->clist2)
284 dslist->clist2_selected = -1;
285
286 gimv_dlist_set_sensitive (dslist);
287 }
288
289
290 static gint
idle_gimv_dlist_row_move(gpointer data)291 idle_gimv_dlist_row_move (gpointer data)
292 {
293 GimvDList *dslist = data;
294
295 gimv_dlist_enabled_list_updated (dslist);
296 gimv_dlist_set_sensitive (dslist);
297
298 return FALSE;
299 }
300
301
302 static void
cb_gimv_dlist_row_move(GtkCList * clist,gint arg1,gint arg2,gpointer data)303 cb_gimv_dlist_row_move (GtkCList *clist, gint arg1, gint arg2, gpointer data)
304 {
305 GimvDList *dslist = data;
306 gint src, dest = dslist->clist2_dest_row;
307 gint selected = dslist->clist2_selected;
308
309 if (dslist->clist2_dest_row >= 0) {
310 dest = dslist->clist2_dest_row;
311 src = arg1 == dest ? arg2 : arg1;
312 } else {
313 src = arg1;
314 dest = arg2;
315 }
316
317 if (selected >= 0) {
318 if (selected == src) {
319 dslist->clist2_selected = dest;
320 } else if (selected >= MIN (src, dest) && selected <= MAX (src, dest)) {
321 if (src < dest)
322 dslist->clist2_selected--;
323 else
324 dslist->clist2_selected++;
325 }
326 }
327
328 dslist->clist2_dest_row = -1;
329
330 gtk_idle_add (idle_gimv_dlist_row_move, dslist);
331 }
332 #endif /* (GTK_MAJOR_VERSION >= 2) */
333
334
335 static void
cb_gimv_dlist_add_button_pressed(GtkButton * button,gpointer data)336 cb_gimv_dlist_add_button_pressed (GtkButton *button, gpointer data)
337 {
338 GimvDList *dslist = data;
339 gint idx;
340
341 if (dslist->clist1_selected < 0) return;
342
343 #if (GTK_MAJOR_VERSION >= 2)
344 {
345 GtkTreeView *treeview = GTK_TREE_VIEW (dslist->clist1);
346 GtkTreeModel *model = gtk_tree_view_get_model (treeview);
347 GtkTreeSelection *selection;
348 GtkTreeIter iter;
349 gboolean success;
350
351 selection = gtk_tree_view_get_selection (treeview);
352 success = gtk_tree_selection_get_selected (selection, &model, &iter);
353 if (!success) return;
354
355 gtk_tree_model_get (model, &iter, 2, &idx, -1);
356 }
357 #else /* (GTK_MAJOR_VERSION >= 2) */
358 {
359 gpointer rowdata;
360 rowdata = gtk_clist_get_row_data (GTK_CLIST (dslist->clist1),
361 dslist->clist1_selected);
362
363 if (!rowdata) return;
364 idx = g_list_index (dslist->available_list, rowdata);
365 }
366 #endif /* (GTK_MAJOR_VERSION >= 2) */
367
368 gimv_dlist_column_add (dslist, idx);
369
370 gimv_dlist_enabled_list_updated (dslist);
371 gimv_dlist_set_sensitive (dslist);
372 }
373
374
375 static void
cb_gimv_dlist_del_button_pressed(GtkButton * button,gpointer data)376 cb_gimv_dlist_del_button_pressed (GtkButton *button, gpointer data)
377 {
378 GimvDList *dslist = data;
379 gint idx;
380
381 if (dslist->clist2_selected < 0) return;
382
383 #if (GTK_MAJOR_VERSION >= 2)
384 {
385 GtkTreeView *treeview = GTK_TREE_VIEW (dslist->clist2);
386 GtkTreeModel *model = gtk_tree_view_get_model (treeview);
387 GtkTreeSelection *selection;
388 GtkTreeIter iter;
389 gboolean success;
390
391 selection = gtk_tree_view_get_selection (treeview);
392 success = gtk_tree_selection_get_selected (selection, &model, &iter);
393 if (!success) return;
394
395 gtk_tree_model_get (model, &iter, 2, &idx, -1);
396 }
397 #else /* (GTK_MAJOR_VERSION >= 2) */
398 {
399 gpointer rowdata;
400 rowdata = gtk_clist_get_row_data (GTK_CLIST (dslist->clist2),
401 dslist->clist2_selected);
402 if (!rowdata) return;
403
404 idx = g_list_index (dslist->available_list, rowdata);
405 }
406 #endif /* (GTK_MAJOR_VERSION >= 2) */
407
408 gimv_dlist_column_del (dslist, idx);
409
410 gimv_dlist_enabled_list_updated (dslist);
411 gimv_dlist_set_sensitive (dslist);
412 }
413
414
415 static void
cb_gimv_dlist_up_button_pressed(GtkButton * button,gpointer data)416 cb_gimv_dlist_up_button_pressed (GtkButton *button, gpointer data)
417 {
418 GimvDList *dslist = data;
419 gint selected = dslist->clist2_selected;
420 gint rows = dslist->clist2_rows;
421
422 g_return_if_fail (button && dslist);
423
424 if (selected < 1 || selected > rows - 1) return;
425
426 dslist->clist2_dest_row = dslist->clist2_selected - 1;
427
428 #if (GTK_MAJOR_VERSION >= 2)
429 {
430 GtkTreeView *treeview = GTK_TREE_VIEW (dslist->clist2);
431 GtkTreeModel *model = gtk_tree_view_get_model (treeview);
432 GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview);
433 GtkTreePath *treepath;
434 GtkTreeIter iter, prev_iter, dest_iter;
435 gboolean success;
436
437 GValue *values;
438 gint i, colnum = gtk_tree_model_get_n_columns (model);
439
440 /* get src row */
441 selection = gtk_tree_view_get_selection (treeview);
442 success = gtk_tree_selection_get_selected (selection, &model, &iter);
443 if (!success) return;
444 treepath = gtk_tree_model_get_path (model, &iter);
445
446 /* get prev row */
447 success = gtk_tree_path_prev (treepath);
448 if (!success) {
449 gtk_tree_path_free (treepath);
450 return;
451 }
452 gtk_tree_model_get_iter (model, &prev_iter, treepath);
453
454 /* get src data */
455 values = g_new0 (GValue, colnum);
456 for (i = 0; i < colnum; i++) {
457 gtk_tree_model_get_value (model, &iter, i, &values[i]);
458 }
459
460 /* insert dest row before prev */
461 gtk_list_store_insert_before (GTK_LIST_STORE (model),
462 &dest_iter, &prev_iter);
463 for (i = 0; i < colnum; i++) {
464 gtk_list_store_set_value (GTK_LIST_STORE (model), &dest_iter,
465 i, &values[i]);
466 g_value_unset (&values[i]);
467 }
468 g_free (values);
469
470 /* delete src */
471 gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
472
473 /* select dest */
474 gtk_tree_path_free (treepath);
475 treepath = gtk_tree_model_get_path (model, &dest_iter);
476 gtk_tree_view_set_cursor (treeview, treepath, NULL, FALSE);
477
478 /* clean */
479 gtk_tree_path_free (treepath);
480 }
481 #else /* (GTK_MAJOR_VERSION >= 2) */
482 {
483 gtk_clist_swap_rows (GTK_CLIST (dslist->clist2), selected, selected - 1);
484 gtk_clist_moveto (GTK_CLIST (dslist->clist2), selected - 1, 0, 0, 0);
485 }
486 #endif /* (GTK_MAJOR_VERSION >= 2) */
487 }
488
489
490 static void
cb_gimv_dlist_down_button_pressed(GtkButton * button,gpointer data)491 cb_gimv_dlist_down_button_pressed (GtkButton *button, gpointer data)
492 {
493 GimvDList *dslist = data;
494 gint selected = dslist->clist2_selected;
495 gint rows = dslist->clist2_rows;
496
497 g_return_if_fail (button && dslist);
498
499 if (selected < 0 || selected > rows - 2) return;
500
501 dslist->clist2_dest_row = dslist->clist2_selected + 1;
502
503 #if (GTK_MAJOR_VERSION >= 2)
504 {
505 GtkTreeView *treeview = GTK_TREE_VIEW (dslist->clist2);
506 GtkTreeModel *model = gtk_tree_view_get_model (treeview);
507 GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview);
508 GtkTreeIter iter, next_iter, dest_iter;
509 GtkTreePath *treepath;
510 gboolean success;
511
512 GValue *values;
513 gint i, colnum = gtk_tree_model_get_n_columns (model);
514
515 /* get src row */
516 selection = gtk_tree_view_get_selection (treeview);
517 success = gtk_tree_selection_get_selected (selection, &model, &iter);
518 if (!success) return;
519
520 /* get prev row */
521 next_iter = iter;
522 success = gtk_tree_model_iter_next (model, &next_iter);
523 if (!success) return;
524
525 /* get src data */
526 values = g_new0 (GValue, colnum);
527 for (i = 0; i < colnum; i++) {
528 gtk_tree_model_get_value (model, &iter, i, &values[i]);
529 }
530
531 /* insert dest row before prev */
532 gtk_list_store_insert_after (GTK_LIST_STORE (model),
533 &dest_iter, &next_iter);
534 for (i = 0; i < colnum; i++) {
535 gtk_list_store_set_value (GTK_LIST_STORE (model), &dest_iter,
536 i, &values[i]);
537 g_value_unset (&values[i]);
538 }
539 g_free (values);
540
541 /* delete src */
542 gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
543
544 /* select dest */
545 treepath = gtk_tree_model_get_path (model, &dest_iter);
546 gtk_tree_view_set_cursor (treeview, treepath, NULL, FALSE);
547
548 /* clean */
549 gtk_tree_path_free (treepath);
550 }
551 #else /* (GTK_MAJOR_VERSION >= 2) */
552 {
553 gtk_clist_swap_rows (GTK_CLIST (dslist->clist2), selected, selected + 1);
554 gtk_clist_moveto (GTK_CLIST (dslist->clist2), selected + 1, 0, 0, 0);
555 }
556 #endif /* (GTK_MAJOR_VERSION >= 2) */
557 }
558
559
560
561 /*******************************************************************************
562 *
563 * Private functions.
564 *
565 *******************************************************************************/
566 static void
gimv_dlist_enabled_list_updated(GimvDList * dslist)567 gimv_dlist_enabled_list_updated (GimvDList *dslist)
568 {
569 g_return_if_fail (GIMV_IS_DLIST (dslist));
570
571 gtk_signal_emit (GTK_OBJECT (dslist),
572 gimv_dlist_signals[ENABLED_LIST_UPDATED_SIGNAL]);
573 }
574
575
576 static void
gimv_dlist_set_sensitive(GimvDList * dslist)577 gimv_dlist_set_sensitive (GimvDList *dslist)
578 {
579 gint selected1 = dslist->clist1_selected;
580 gint selected2 = dslist->clist2_selected;
581 gint rows1 = dslist->clist1_rows;
582 gint rows2 = dslist->clist2_rows;
583
584 /* g_return_if_fail (dslist); */
585
586 /* add button */
587 if (selected1 < 0 || selected1 >= rows1) {
588 gtk_widget_set_sensitive (dslist->add_button, FALSE);
589 } else {
590 gtk_widget_set_sensitive (dslist->add_button, TRUE);
591 }
592
593 /* delete button */
594 if (selected2 < 0 || selected2 >= rows2) {
595 gtk_widget_set_sensitive (dslist->del_button, FALSE);
596 } else {
597 gtk_widget_set_sensitive (dslist->del_button, TRUE);
598 }
599
600 /* up button */
601 if (selected2 > 0 && selected2 < rows2) {
602 gtk_widget_set_sensitive (dslist->up_button, TRUE);
603 } else {
604 gtk_widget_set_sensitive (dslist->up_button, FALSE);
605 }
606
607 /* down button */
608 if (selected2 >= 0 && selected2 < rows2 - 1) {
609 gtk_widget_set_sensitive (dslist->down_button, TRUE);
610 } else {
611 gtk_widget_set_sensitive (dslist->down_button, FALSE);
612 }
613 }
614
615
616 static GtkWidget *
gimv_dlist_create_list_widget(GimvDList * dslist,gboolean reorderble)617 gimv_dlist_create_list_widget (GimvDList *dslist, gboolean reorderble)
618 {
619 GtkWidget *clist;
620
621 #if (GTK_MAJOR_VERSION >= 2)
622
623 GtkListStore *store;
624 GtkTreeViewColumn *col;
625 GtkCellRenderer *render;
626
627 store = gtk_list_store_new (3,
628 G_TYPE_STRING,
629 G_TYPE_STRING,
630 G_TYPE_INT);
631 clist = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
632 dslist->clist2 = clist;
633 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (clist), TRUE);
634 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (clist), FALSE);
635
636 if (reorderble)
637 gtk_tree_view_set_reorderable (GTK_TREE_VIEW (clist), TRUE);
638
639 g_signal_connect (G_OBJECT (store), "row_changed",
640 G_CALLBACK (cb_gimv_dlist_row_changed),
641 dslist);
642 g_signal_connect (G_OBJECT (store), "row_deleted",
643 G_CALLBACK (cb_gimv_dlist_row_deleted),
644 dslist);
645 g_signal_connect (G_OBJECT (clist),"cursor_changed",
646 G_CALLBACK (cb_gimv_dlist_cursor_changed),
647 dslist);
648
649 /* set column */
650 col = gtk_tree_view_column_new();
651 render = gtk_cell_renderer_text_new ();
652 gtk_tree_view_column_pack_start (col, render, TRUE);
653 gtk_tree_view_column_add_attribute (col, render, "text", 0);
654 gtk_tree_view_append_column (GTK_TREE_VIEW (clist), col);
655
656 #else /* (GTK_MAJOR_VERSION >= 2) */
657
658 clist = dslist->clist2 = gtk_clist_new (1);
659 gtk_clist_set_selection_mode (GTK_CLIST (clist), GTK_SELECTION_SINGLE);
660 gtk_clist_set_column_auto_resize (GTK_CLIST (clist), 0, TRUE);
661
662 gtk_signal_connect (GTK_OBJECT (clist),"select_row",
663 GTK_SIGNAL_FUNC (cb_gimv_dlist_select_row),
664 dslist);
665 gtk_signal_connect (GTK_OBJECT (clist),"unselect_row",
666 GTK_SIGNAL_FUNC (cb_gimv_dlist_unselect_row),
667 dslist);
668
669 if (reorderble) {
670 gtk_clist_set_reorderable (GTK_CLIST (clist), TRUE);
671 gtk_clist_set_use_drag_icons (GTK_CLIST (clist), FALSE);
672 gtk_signal_connect (GTK_OBJECT (clist),"row_move",
673 GTK_SIGNAL_FUNC (cb_gimv_dlist_row_move),
674 dslist);
675 }
676
677 #endif /* (GTK_MAJOR_VERSION >= 2) */
678
679 return clist;
680 }
681
682
683
684 /*******************************************************************************
685 *
686 * Public functions.
687 *
688 *******************************************************************************/
689 GtkWidget *
gimv_dlist_new(const gchar * clist1_title,const gchar * clist2_title)690 gimv_dlist_new (const gchar *clist1_title,
691 const gchar *clist2_title)
692 {
693 #if (GTK_MAJOR_VERSION >= 2)
694 GimvDList *dslist = g_object_new (gimv_dlist_get_type (), NULL);
695 #else /* (GTK_MAJOR_VERSION >= 2) */
696 GimvDList *dslist = gtk_type_new (gimv_dlist_get_type ());
697 #endif /* (GTK_MAJOR_VERSION >= 2) */
698
699 GtkWidget *hbox = GTK_WIDGET (dslist);
700 GtkWidget *vbox, *vbox1, *vbox2, *vbox3, *hseparator;
701 GtkWidget *label, *scrollwin1, *scrollwin2, *clist, *button, *arrow;
702
703 /* possible columns */
704 vbox1 = gtk_vbox_new (FALSE, 0);
705 gtk_box_pack_start (GTK_BOX (hbox), vbox1, TRUE, TRUE, 0);
706 gtk_widget_show (vbox1);
707
708 label = gtk_label_new (clist1_title);
709 gtk_box_pack_start (GTK_BOX (vbox1), label, FALSE, FALSE, 0);
710 gtk_widget_show (label);
711
712 scrollwin1 = gtk_scrolled_window_new (NULL, NULL);
713 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrollwin1),
714 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
715 #ifdef USE_GTK2
716 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrollwin1),
717 GTK_SHADOW_IN);
718 #endif /* USE_GTK2 */
719 gtk_container_set_border_width(GTK_CONTAINER(scrollwin1), 5);
720 gtk_box_pack_start (GTK_BOX (vbox1), scrollwin1, TRUE, TRUE, 0);
721 gtk_widget_show (scrollwin1);
722
723 clist = dslist->clist1 = gimv_dlist_create_list_widget (dslist, FALSE);
724 gtk_container_add (GTK_CONTAINER (scrollwin1), clist);
725 gtk_widget_show (clist);
726
727
728 /* add/delete buttons */
729 vbox3 = gtk_vbox_new (TRUE, 0);
730 gtk_box_pack_start (GTK_BOX (hbox), vbox3, FALSE, FALSE, 0);
731 gtk_widget_show (vbox3);
732
733 vbox = gtk_vbox_new (TRUE, 0);
734 gtk_box_pack_start (GTK_BOX (vbox3), vbox, FALSE, FALSE, 0);
735 gtk_widget_show (vbox);
736
737 button = dslist->add_button = gtk_button_new ();
738 #ifdef USE_ARROW
739 arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
740 #else /* USE_ARROW */
741 arrow = gtk_label_new (_("Add"));
742 gtk_misc_set_alignment (GTK_MISC (arrow), 0.5, 0.5);
743 #endif /* USE_ARROW */
744 gtk_container_add (GTK_CONTAINER (button), arrow);
745 gtk_widget_show (arrow);
746 gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 2);
747 gtk_widget_show (button);
748
749 button = dslist->del_button = gtk_button_new ();
750 #ifdef USE_ARROW
751 arrow = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_NONE);
752 #else /* USE_ARROW */
753 arrow = gtk_label_new (_("Delete"));
754 gtk_misc_set_alignment (GTK_MISC (arrow), 0.5, 0.5);
755 #endif /* USE_ARROW */
756 gtk_container_add (GTK_CONTAINER (button), arrow);
757 gtk_widget_show (arrow);
758 gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 2);
759 gtk_widget_show (button);
760
761 hseparator = gtk_hseparator_new ();
762 gtk_box_pack_start (GTK_BOX (vbox), hseparator, FALSE, FALSE, 2);
763 gtk_widget_show (hseparator);
764
765
766 /* move buttons */
767 button = dslist->up_button = gtk_button_new ();
768 #ifdef USE_ARROW
769 arrow = gtk_arrow_new (GTK_ARROW_UP, GTK_SHADOW_NONE);
770 #else /* USE_ARROW */
771 arrow = gtk_label_new (_("Up"));
772 gtk_misc_set_alignment (GTK_MISC (arrow), 0.5, 0.5);
773 #endif /* USE_ARROW */
774 gtk_container_add (GTK_CONTAINER (button), arrow);
775 gtk_widget_show (arrow);
776 gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 2);
777 gtk_widget_show (button);
778
779 button = dslist->down_button = gtk_button_new ();
780 #ifdef USE_ARROW
781 arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
782 #else /* USE_ARROW */
783 arrow = gtk_label_new (_("Down"));
784 gtk_misc_set_alignment (GTK_MISC (arrow), 0.5, 0.5);
785 #endif /* USE_ARROW */
786 gtk_container_add (GTK_CONTAINER (button), arrow);
787 gtk_widget_show (arrow);
788 gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 2);
789 gtk_widget_show (button);
790
791
792 /* Use list */
793 vbox2 = gtk_vbox_new (FALSE, 0);
794 gtk_box_pack_start (GTK_BOX (hbox), vbox2, TRUE, TRUE, 0);
795 gtk_widget_show (vbox2);
796
797 label = gtk_label_new (clist2_title);
798 gtk_box_pack_start (GTK_BOX (vbox2), label, FALSE, FALSE, 0);
799 gtk_widget_show (label);
800
801 scrollwin2 = gtk_scrolled_window_new (NULL, NULL);
802 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrollwin2),
803 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
804 #ifdef USE_GTK2
805 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrollwin2),
806 GTK_SHADOW_IN);
807 #endif /* USE_GTK2 */
808 gtk_container_set_border_width(GTK_CONTAINER(scrollwin2), 5);
809 gtk_box_pack_start (GTK_BOX (vbox2), scrollwin2, TRUE, TRUE, 0);
810 gtk_widget_show (scrollwin2);
811
812 clist = dslist->clist2 = gimv_dlist_create_list_widget (dslist, TRUE);
813 gtk_container_add (GTK_CONTAINER (scrollwin2), clist);
814 dslist->clist2_rows = list_widget_get_row_num (clist);
815 gtk_widget_show (clist);
816
817
818 #if (GTK_MAJOR_VERSION >= 2)
819
820 gtk_widget_set_size_request (scrollwin1, -1, 200);
821 gtk_widget_set_size_request (scrollwin2, -1, 200);
822
823 #ifdef USE_ARROW
824 gtk_widget_set_size_request (dslist->add_button, 20, 20);
825 gtk_widget_set_size_request (dslist->del_button, 20, 20);
826 gtk_widget_set_size_request (dslist->up_button, 20, 20);
827 gtk_widget_set_size_request (dslist->down_button, 20, 20);
828 #endif /* USE_ARROW */
829
830 g_signal_connect (G_OBJECT (dslist->add_button), "clicked",
831 G_CALLBACK (cb_gimv_dlist_add_button_pressed),
832 dslist);
833 g_signal_connect (G_OBJECT (dslist->del_button), "clicked",
834 G_CALLBACK (cb_gimv_dlist_del_button_pressed),
835 dslist);
836 g_signal_connect (G_OBJECT (dslist->up_button), "clicked",
837 G_CALLBACK (cb_gimv_dlist_up_button_pressed),
838 dslist);
839 g_signal_connect (G_OBJECT (dslist->down_button), "clicked",
840 G_CALLBACK (cb_gimv_dlist_down_button_pressed),
841 dslist);
842
843 #else /* (GTK_MAJOR_VERSION >= 2) */
844
845 gtk_widget_set_usize (scrollwin1, -1, 200);
846 gtk_widget_set_usize (scrollwin2, -1, 200);
847
848 #ifdef USE_ARROW
849 gtk_widget_set_usize (dslist->add_button, 20, 20);
850 gtk_widget_set_usize (dslist->del_button, 20, 20);
851 gtk_widget_set_usize (dslist->up_button, 20, 20);
852 gtk_widget_set_usize (dslist->down_button, 20, 20);
853 #endif /* USE_ARROW */
854
855 gtk_signal_connect (GTK_OBJECT (dslist->add_button), "clicked",
856 GTK_SIGNAL_FUNC (cb_gimv_dlist_add_button_pressed),
857 dslist);
858 gtk_signal_connect (GTK_OBJECT (dslist->del_button), "clicked",
859 GTK_SIGNAL_FUNC (cb_gimv_dlist_del_button_pressed),
860 dslist);
861 gtk_signal_connect (GTK_OBJECT (dslist->up_button), "clicked",
862 GTK_SIGNAL_FUNC (cb_gimv_dlist_up_button_pressed),
863 dslist);
864 gtk_signal_connect (GTK_OBJECT (dslist->down_button), "clicked",
865 GTK_SIGNAL_FUNC (cb_gimv_dlist_down_button_pressed),
866 dslist);
867
868 #endif /* (GTK_MAJOR_VERSION >= 2) */
869
870 return hbox;
871 }
872
873
874 gint
gimv_dlist_append_available_item(GimvDList * dslist,const gchar * item)875 gimv_dlist_append_available_item (GimvDList *dslist, const gchar *item)
876 {
877 GtkWidget *clist = dslist->clist1;
878 gchar *text = g_strdup (item);
879 gchar *i18n_text = _(text);
880 gint idx;
881
882 g_return_val_if_fail (GIMV_IS_DLIST (dslist), -1);
883
884 dslist->available_list = g_list_append (dslist->available_list, text);
885 idx = g_list_index (dslist->available_list, text);
886
887 #if (GTK_MAJOR_VERSION >= 2)
888 {
889 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (clist));
890 GtkListStore *store = GTK_LIST_STORE (model);
891 GtkTreeIter iter;
892
893 gtk_list_store_append (store, &iter);
894 gtk_list_store_set (store, &iter,
895 0, i18n_text, 1, text, 2, idx, -1);
896 }
897 #else /* (GTK_MAJOR_VERSION >= 2) */
898 {
899 gint row;
900
901 row = gtk_clist_append (GTK_CLIST (clist), &i18n_text);
902 gtk_clist_set_row_data (GTK_CLIST (clist), row, text);
903 }
904 #endif /* (GTK_MAJOR_VERSION >= 2) */
905
906 dslist->clist1_rows = list_widget_get_row_num (dslist->clist1);
907 gimv_dlist_set_sensitive (dslist);
908
909 return idx;
910 }
911
912
913 void
gimv_dlist_column_add(GimvDList * dslist,gint idx)914 gimv_dlist_column_add (GimvDList *dslist, gint idx)
915 {
916 gchar *text, *i18n_text;
917 GList *list;
918
919 list = g_list_nth (dslist->available_list, idx);
920 g_return_if_fail (list);
921 text = list->data;
922 i18n_text = _(text);
923 g_return_if_fail (text);
924
925 #if (GTK_MAJOR_VERSION >= 2)
926 {
927 GtkTreeView *treeview1 = GTK_TREE_VIEW (dslist->clist1);
928 GtkTreeView *treeview2 = GTK_TREE_VIEW (dslist->clist2);
929 GtkTreeModel *model1 = gtk_tree_view_get_model (treeview1);
930 GtkTreeModel *model2 = gtk_tree_view_get_model (treeview2);
931 GtkTreeIter iter1, iter2, next;
932 gboolean go_next;
933 gchar *str = NULL;
934
935 /* find row from clist1 */
936 go_next = gtk_tree_model_get_iter_first (model1, &iter1);
937 for (; go_next; go_next = gtk_tree_model_iter_next (model1, &iter1)) {
938 gtk_tree_model_get (model1, &iter1, 1, &str, -1);
939 if (str && !strcmp (text, str)) break;
940 g_free (str);
941 str = NULL;
942 }
943 if (!str) return;
944
945 /* append to clist2 */
946 gtk_list_store_append (GTK_LIST_STORE (model2), &iter2);
947 gtk_list_store_set (GTK_LIST_STORE (model2), &iter2,
948 0, i18n_text, 1, text, 2, idx, -1);
949
950 /* set cursor of clist1 */
951 next = iter1;
952 if (gtk_tree_model_iter_next (model1, &next)) {
953 GtkTreePath *path = gtk_tree_model_get_path (model1, &next);
954 gtk_tree_view_set_cursor (treeview1, path, NULL, FALSE);
955 gtk_tree_path_free (path);
956 } else {
957 GtkTreePath *path = gtk_tree_model_get_path (model1, &iter1);
958 if (gtk_tree_path_prev (path))
959 gtk_tree_view_set_cursor (treeview1, path, NULL, FALSE);
960 gtk_tree_path_free (path);
961 }
962
963 /* remove from clist1 */
964 gtk_list_store_remove (GTK_LIST_STORE (model1), &iter1);
965
966 /* clean :-) */
967 g_free (str);
968
969 /* reset */
970 dslist->clist1_rows = gtk_tree_model_iter_n_children (model1, NULL);
971 dslist->clist2_rows = gtk_tree_model_iter_n_children (model2, NULL);
972
973 g_signal_emit_by_name (G_OBJECT (treeview1), "cursor-changed");
974 }
975 #else /* (GTK_MAJOR_VERSION >= 2) */
976 {
977 gint row1, row2;
978
979 row1 = gtk_clist_find_row_from_data (GTK_CLIST (dslist->clist1), text);
980 if (row1 < 0) return;
981
982 row2 = gtk_clist_append (GTK_CLIST (dslist->clist2), &i18n_text);
983 gtk_clist_set_row_data (GTK_CLIST (dslist->clist2), row2, text);
984 gtk_clist_remove (GTK_CLIST (dslist->clist1), row1);
985
986 dslist->clist1_rows = GTK_CLIST (dslist->clist1)->rows;
987 dslist->clist2_rows = GTK_CLIST (dslist->clist2)->rows;
988 }
989 #endif /* (GTK_MAJOR_VERSION >= 2) */
990 }
991
992
993 void
gimv_dlist_column_del(GimvDList * dslist,gint idx)994 gimv_dlist_column_del (GimvDList *dslist, gint idx)
995 {
996 gchar *text, *i18n_text;
997 GList *list;
998
999 list = g_list_nth (dslist->available_list, idx);
1000 g_return_if_fail (list);
1001 text = list->data;
1002 i18n_text = _(text);
1003 g_return_if_fail (text);
1004
1005 #if (GTK_MAJOR_VERSION >= 2)
1006 {
1007 GtkTreeView *treeview1 = GTK_TREE_VIEW (dslist->clist1);
1008 GtkTreeView *treeview2 = GTK_TREE_VIEW (dslist->clist2);
1009 GtkTreeModel *model1 = gtk_tree_view_get_model (treeview1);
1010 GtkTreeModel *model2 = gtk_tree_view_get_model (treeview2);
1011 GtkTreeIter iter, iter1, iter2, next;
1012 gboolean go_next;
1013 gchar *str = NULL;
1014
1015 /* find row from clist2 */
1016 go_next = gtk_tree_model_get_iter_first (model2, &iter2);
1017 for (; go_next; go_next = gtk_tree_model_iter_next (model2, &iter2)) {
1018 gtk_tree_model_get (model2, &iter2, 1, &str, -1);
1019 if (str && !strcmp (text, str)) break;
1020 g_free (str);
1021 str = NULL;
1022 }
1023 if (!str) return;
1024
1025 /* append to clist1 */
1026 go_next = gtk_tree_model_get_iter_first (model1, &iter1);
1027 for (; go_next; go_next = gtk_tree_model_iter_next (model1, &iter1)) {
1028 gint idx1;
1029 gtk_tree_model_get (model1, &iter1, 2, &idx1, -1);
1030 if (idx < idx1) break;
1031 }
1032
1033 if (go_next)
1034 gtk_list_store_insert_before (GTK_LIST_STORE (model1), &iter, &iter1);
1035 else
1036 gtk_list_store_append (GTK_LIST_STORE (model1), &iter);
1037 gtk_list_store_set (GTK_LIST_STORE (model1), &iter,
1038 0, i18n_text, 1, text, 2, idx, -1);
1039
1040 /* set cursor of clist2 */
1041 next = iter2;
1042 if (gtk_tree_model_iter_next (model2, &next)) {
1043 GtkTreePath *path = gtk_tree_model_get_path (model2, &next);
1044 gtk_tree_view_set_cursor (treeview2, path, NULL, FALSE);
1045 gtk_tree_path_free (path);
1046 } else {
1047 GtkTreePath *path = gtk_tree_model_get_path (model2, &iter2);
1048 if (gtk_tree_path_prev (path))
1049 gtk_tree_view_set_cursor (treeview2, path, NULL, FALSE);
1050 gtk_tree_path_free (path);
1051 }
1052
1053 /* remove from clist2 */
1054 gtk_list_store_remove (GTK_LIST_STORE (model2), &iter2);
1055
1056 /* clean :-) */
1057 g_free (str);
1058
1059 dslist->clist1_rows = gtk_tree_model_iter_n_children (model1, NULL);
1060 dslist->clist2_rows = gtk_tree_model_iter_n_children (model2, NULL);
1061
1062 g_signal_emit_by_name (G_OBJECT (treeview2), "cursor-changed");
1063 }
1064 #else /* (GTK_MAJOR_VERSION >= 2) */
1065 {
1066 gint row1, row2;
1067 gpointer rowdata;
1068
1069 row2 = gtk_clist_find_row_from_data (GTK_CLIST (dslist->clist2), text);
1070 if (row2 < 0) return;
1071
1072 /* remove item from right side */
1073 gtk_clist_freeze (GTK_CLIST (dslist->clist1));
1074
1075 row1 = gtk_clist_append (GTK_CLIST (dslist->clist1), &i18n_text);
1076 gtk_clist_set_row_data (GTK_CLIST (dslist->clist1), row1, text);
1077
1078 while (row1 > 0) {
1079 gint idx_prev;
1080
1081 rowdata = gtk_clist_get_row_data (GTK_CLIST (dslist->clist1), row1 - 1);
1082 if (!rowdata) break;
1083
1084 idx_prev = g_list_index (dslist->available_list, rowdata);
1085
1086 if (idx < idx_prev) {
1087 gtk_clist_swap_rows (GTK_CLIST (dslist->clist1), row1, row1 - 1);
1088 row1--;
1089 } else {
1090 break;
1091 }
1092 }
1093
1094 gtk_clist_thaw (GTK_CLIST (dslist->clist1));
1095
1096 gtk_clist_remove (GTK_CLIST (dslist->clist2), row2);
1097
1098 dslist->clist1_rows = GTK_CLIST (dslist->clist1)->rows;
1099 dslist->clist2_rows = GTK_CLIST (dslist->clist2)->rows;
1100 }
1101 #endif /* (GTK_MAJOR_VERSION >= 2) */
1102 }
1103
1104
1105 void
gimv_dlist_column_add_by_label(GimvDList * dslist,const gchar * label)1106 gimv_dlist_column_add_by_label (GimvDList *dslist, const gchar *label)
1107 {
1108 GList *list;
1109 gint j, idx = -1;
1110
1111 g_return_if_fail (GIMV_IS_DLIST (dslist));
1112 g_return_if_fail (label && *label);
1113
1114 list = dslist->available_list;
1115 for (j = 0; list; j++, list = g_list_next (list)) {
1116 if (!strcmp (label, (gchar *)list->data)) {
1117 idx = j;
1118 break;
1119 }
1120 }
1121 if (idx >= 0)
1122 gimv_dlist_column_add (dslist, idx);
1123
1124 gimv_dlist_set_sensitive (dslist);
1125 }
1126
1127
1128 gint
gimv_dlist_get_n_available_items(GimvDList * dslist)1129 gimv_dlist_get_n_available_items (GimvDList *dslist)
1130 {
1131 g_return_val_if_fail (GIMV_IS_DLIST (dslist), 0);
1132 return dslist->clist1_rows;
1133 }
1134
1135
1136 gint
gimv_dlist_get_n_enabled_items(GimvDList * dslist)1137 gimv_dlist_get_n_enabled_items (GimvDList *dslist)
1138 {
1139 g_return_val_if_fail (GIMV_IS_DLIST (dslist), 0);
1140 return dslist->clist2_rows;
1141 }
1142
1143
1144 gchar *
gimv_dlist_get_enabled_row_text(GimvDList * dslist,gint row)1145 gimv_dlist_get_enabled_row_text (GimvDList *dslist, gint row)
1146 {
1147 gchar *text;
1148
1149 g_return_val_if_fail (GIMV_IS_DLIST (dslist), NULL);
1150 g_return_val_if_fail (row >= 0 && row < dslist->clist2_rows, NULL);
1151
1152 #if (GTK_MAJOR_VERSION >= 2)
1153 {
1154 GtkTreeView *treeview = GTK_TREE_VIEW (dslist->clist2);
1155 GtkTreeModel *model = gtk_tree_view_get_model (treeview);
1156 GtkTreeIter iter;
1157 gboolean success;
1158
1159 success = gtk_tree_model_iter_nth_child (model, &iter, NULL, row);
1160 if (!success) return NULL;
1161
1162 gtk_tree_model_get (model, &iter, 1, &text, -1);
1163 if (!text) return NULL;
1164
1165 return text;
1166 }
1167 #else /* (GTK_MAJOR_VERSION >= 2) */
1168 text = gtk_clist_get_row_data (GTK_CLIST (dslist->clist2), row);
1169 if (!text) return NULL;
1170
1171 return g_strdup (text);
1172 #endif /* (GTK_MAJOR_VERSION >= 2) */
1173 }
1174