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