Separated psppire_selector_set_subjects into two individual functions
[pspp-builds.git] / src / ui / gui / psppire-selector.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2007, 2009 Free Software Foundation
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 /*
18   This module provides a widget, PsppireSelector derived from
19   GtkButton.
20
21   It contains a GtkArrow, and is used for selecting objects from a
22   GtkTreeView and putting them into a destination widget (often
23   another GtkTreeView).  Typically this is used in psppire for
24   selecting variables, thus:
25
26
27   +----------------------------------------------------------+
28   |                                                          |
29   |     Source Widget                       Dest Widget      |
30   |   +----------------+                 +----------------+  |
31   |   | Variable0      |                 | Variable2      |  |
32   |   | Variable1      |                 |                |  |
33   |   | Variable3      |                 |                |  |
34   |   |                |    Selector     |                |  |
35   |   |                |                 |                |  |
36   |   |                |    +------+     |                |  |
37   |   |                |    | |\   |     |                |  |
38   |   |                |    | | \  |     |                |  |
39   |   |                |    | | /  |     |                |  |
40   |   |                |    | |/   |     |                |  |
41   |   |                |    +------+     |                |  |
42   |   |                |                 |                |  |
43   |   |                |                 |                |  |
44   |   |                |                 |                |  |
45   |   |                |                 |                |  |
46   |   +----------------+                 +----------------+  |
47   |                                                          |
48   +----------------------------------------------------------+
49
50   The Source Widget is always a GtkTreeView.  The Dest Widget may be a
51   GtkTreeView or a GtkEntry (other destination widgets may be
52   supported in the future).
53
54   Widgets may be source to more than one PsppireSelector.
55 */
56
57
58 #include <config.h>
59
60 #include <gtk/gtksignal.h>
61 #include <gtk/gtkbutton.h>
62 #include <gtk/gtkentry.h>
63
64 #include "psppire-selector.h"
65
66 #include <gtk/gtktreeview.h>
67 #include <gtk/gtktreeselection.h>
68 #include <gtk/gtktextview.h>
69 #include <gtk/gtkwidget.h>
70
71 static void psppire_selector_base_finalize (PsppireSelectorClass *, gpointer);
72 static void psppire_selector_base_init     (PsppireSelectorClass *class);
73 static void psppire_selector_class_init    (PsppireSelectorClass *class);
74 static void psppire_selector_init          (PsppireSelector      *selector);
75
76
77 static void set_direction (PsppireSelector *, enum psppire_selector_dir);
78
79
80 enum  {SELECTED,    /* Emitted when an item is inserted into dest */
81        DE_SELECTED, /* Emitted when an item is removed from dest */
82        n_SIGNALS};
83
84 static guint signals [n_SIGNALS];
85
86
87 GType
88 psppire_selector_get_type (void)
89 {
90   static GType psppire_selector_type = 0;
91
92   if (!psppire_selector_type)
93     {
94       static const GTypeInfo psppire_selector_info =
95       {
96         sizeof (PsppireSelectorClass),
97         (GBaseInitFunc) psppire_selector_base_init,
98         (GBaseFinalizeFunc) psppire_selector_base_finalize,
99         (GClassInitFunc)psppire_selector_class_init,
100         (GClassFinalizeFunc) NULL,
101         NULL,
102         sizeof (PsppireSelector),
103         0,
104         (GInstanceInitFunc) psppire_selector_init,
105       };
106
107       psppire_selector_type =
108         g_type_register_static (GTK_TYPE_BUTTON, "PsppireSelector",
109                                 &psppire_selector_info, 0);
110     }
111
112   return psppire_selector_type;
113 }
114
115 static GObjectClass * parent_class = NULL;
116
117 static void
118 psppire_selector_finalize (GObject *obj)
119 {
120    /* Chain up to the parent class */
121    G_OBJECT_CLASS (parent_class)->finalize (obj);
122 }
123
124
125 static void
126 psppire_selector_dispose (GObject *obj)
127 {
128   PsppireSelector *sel = PSPPIRE_SELECTOR (obj);
129
130   if (sel->dispose_has_run)
131     return;
132
133   /* Make sure dispose does not run twice. */
134   sel->dispose_has_run = TRUE;
135
136   g_object_unref (sel->dest);
137   g_object_unref (sel->source);
138
139   /* Chain up to the parent class */
140   G_OBJECT_CLASS (parent_class)->dispose (obj);
141 }
142
143
144 /* Properties */
145 enum
146 {
147   PROP_0,
148   PROP_ORIENTATION,
149   PROP_SOURCE_WIDGET,
150   PROP_DEST_WIDGET
151 };
152
153
154 static void on_activate (PsppireSelector *selector, gpointer data);
155
156 static void update_subjects (PsppireSelector *selector);
157
158
159 static void
160 psppire_selector_set_property (GObject         *object,
161                                guint            prop_id,
162                                const GValue    *value,
163                                GParamSpec      *pspec)
164 {
165   PsppireSelector *selector = PSPPIRE_SELECTOR (object);
166
167   switch (prop_id)
168     {
169     case PROP_ORIENTATION:
170       selector->orientation = g_value_get_enum (value);
171       set_direction (selector, selector->direction);
172       break;
173     case PROP_SOURCE_WIDGET:
174       selector->source = g_value_dup_object (value);
175       update_subjects (selector);
176       break;
177     case PROP_DEST_WIDGET:
178       selector->dest = g_value_dup_object (value);
179       update_subjects (selector);
180       break;
181     default:
182       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
183       break;
184     };
185 }
186
187
188 static void
189 psppire_selector_get_property (GObject         *object,
190                                guint            prop_id,
191                                GValue          *value,
192                                GParamSpec      *pspec)
193 {
194   PsppireSelector *selector = PSPPIRE_SELECTOR (object);
195
196   switch (prop_id)
197     {
198     case PROP_ORIENTATION:
199       g_value_set_enum (value, selector->orientation);
200       break;
201     case PROP_SOURCE_WIDGET:
202       g_value_take_object (value, selector->source);
203       break;
204     case PROP_DEST_WIDGET:
205       g_value_take_object (value, selector->dest);
206       break;
207     default:
208       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
209       break;
210     };
211 }
212
213
214
215 static void
216 psppire_selector_class_init (PsppireSelectorClass *class)
217 {
218   GObjectClass *object_class = G_OBJECT_CLASS (class);
219   GParamSpec *orientation_spec =
220     g_param_spec_enum ("orientation",
221                        "Orientation",
222                        "Where the selector is relative to its subjects",
223                        PSPPIRE_TYPE_SELECTOR_ORIENTATION,
224                        PSPPIRE_SELECT_SOURCE_BEFORE_DEST /* default value */,
225                        G_PARAM_CONSTRUCT_ONLY |G_PARAM_READWRITE);
226
227   GParamSpec *source_widget_spec = 
228     g_param_spec_object ("source-widget",
229                          "Source Widget",
230                          "The widget to be used as the source for this selector",
231                          GTK_TYPE_WIDGET,
232                          G_PARAM_READWRITE);
233
234   GParamSpec *dest_widget_spec = 
235     g_param_spec_object ("dest-widget",
236                          "Destination Widget",
237                          "The widget to be used as the destination for this selector",
238                          GTK_TYPE_WIDGET,
239                          G_PARAM_READWRITE);
240
241
242   object_class->set_property = psppire_selector_set_property;
243   object_class->get_property = psppire_selector_get_property;
244
245   g_object_class_install_property (object_class,
246                                    PROP_ORIENTATION,
247                                    orientation_spec);
248
249   g_object_class_install_property (object_class,
250                                    PROP_SOURCE_WIDGET,
251                                    source_widget_spec);
252
253   g_object_class_install_property (object_class,
254                                    PROP_DEST_WIDGET,
255                                    dest_widget_spec);
256
257   parent_class = g_type_class_peek_parent (class);
258
259   signals [SELECTED] =
260     g_signal_new ("selected",
261                   G_TYPE_FROM_CLASS (class),
262                   G_SIGNAL_RUN_FIRST,
263                   0,
264                   NULL, NULL,
265                   g_cclosure_marshal_VOID__VOID,
266                   G_TYPE_NONE,
267                   0);
268
269   signals [DE_SELECTED] =
270     g_signal_new ("de-selected",
271                   G_TYPE_FROM_CLASS (class),
272                   G_SIGNAL_RUN_FIRST,
273                   0,
274                   NULL, NULL,
275                   g_cclosure_marshal_VOID__VOID,
276                   G_TYPE_NONE,
277                   0);
278 }
279
280
281 static void
282 psppire_selector_base_init (PsppireSelectorClass *class)
283 {
284   GObjectClass *object_class = G_OBJECT_CLASS (class);
285
286   object_class->finalize = psppire_selector_finalize;
287   object_class->dispose = psppire_selector_dispose;
288
289   class->source_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
290 }
291
292
293
294 static void
295 psppire_selector_base_finalize(PsppireSelectorClass *class,
296                                 gpointer class_data)
297 {
298   g_hash_table_destroy (class->source_hash);
299 }
300
301
302
303 static void
304 psppire_selector_init (PsppireSelector *selector)
305 {
306   selector->select_user_data = NULL;
307   selector->select_items = NULL;
308   selector->allow_selection = NULL;
309   selector->filter = NULL;
310
311   selector->arrow = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_NONE);
312   selector->filtered_source = NULL;
313
314   selector->action = gtk_action_new ("select", NULL, NULL, "pspp-stock-select");
315
316   gtk_action_connect_proxy (selector->action, GTK_WIDGET (selector));
317
318   gtk_container_add (GTK_CONTAINER (selector), selector->arrow);
319
320   gtk_widget_show (selector->arrow);
321
322   g_signal_connect_swapped (selector->action, "activate", G_CALLBACK (on_activate), selector);
323
324   selector->selecting = FALSE;
325
326   selector->source = NULL;
327   selector->dest = NULL;
328   selector->dispose_has_run = FALSE;
329 }
330
331
332 GtkWidget*
333 psppire_selector_new (void)
334 {
335   return GTK_WIDGET (g_object_new (psppire_selector_get_type (), NULL));
336 }
337
338
339 static void
340 set_direction (PsppireSelector *selector, enum psppire_selector_dir d)
341 {
342   selector->direction = d;
343
344   /* FIXME: Need to reverse the arrow direction if an RTL locale is in
345      effect */
346   if ( d == PSPPIRE_SELECTOR_SOURCE_TO_DEST )
347     {
348       switch (selector->orientation)
349         {
350         case   PSPPIRE_SELECT_SOURCE_BEFORE_DEST:
351           g_object_set (selector->arrow, "arrow-type", GTK_ARROW_RIGHT, NULL);
352           break;
353         case   PSPPIRE_SELECT_SOURCE_AFTER_DEST:
354           g_object_set (selector->arrow, "arrow-type", GTK_ARROW_LEFT, NULL);
355           break;
356         case   PSPPIRE_SELECT_SOURCE_ABOVE_DEST:
357           g_object_set (selector->arrow, "arrow-type", GTK_ARROW_DOWN, NULL);
358           break;
359         case   PSPPIRE_SELECT_SOURCE_BELOW_DEST:
360           g_object_set (selector->arrow, "arrow-type", GTK_ARROW_UP, NULL);
361           break;
362         default:
363           g_assert_not_reached ();
364           break;
365         };
366     }
367   else
368     {
369       switch (selector->orientation)
370         {
371         case   PSPPIRE_SELECT_SOURCE_BEFORE_DEST:
372           g_object_set (selector->arrow, "arrow-type", GTK_ARROW_LEFT, NULL);
373           break;
374         case   PSPPIRE_SELECT_SOURCE_AFTER_DEST:
375           g_object_set (selector->arrow, "arrow-type", GTK_ARROW_RIGHT, NULL);
376           break;
377         case   PSPPIRE_SELECT_SOURCE_ABOVE_DEST:
378           g_object_set (selector->arrow, "arrow-type", GTK_ARROW_UP, NULL);
379           break;
380         case   PSPPIRE_SELECT_SOURCE_BELOW_DEST:
381           g_object_set (selector->arrow, "arrow-type", GTK_ARROW_DOWN, NULL);
382           break;
383         default:
384           g_assert_not_reached ();
385           break;
386         };
387
388     }
389 }
390
391 /* Callback for when the source selection changes */
392 static void
393 on_source_select (GtkTreeSelection *treeselection, gpointer data)
394 {
395   PsppireSelector *selector = data;
396
397   set_direction (selector, PSPPIRE_SELECTOR_SOURCE_TO_DEST);
398
399   if ( selector->allow_selection )
400     {
401       gtk_action_set_sensitive (selector->action,
402                                 selector->allow_selection (selector->source, selector->dest));
403     }
404   else if ( GTK_IS_ENTRY (selector->dest) )
405     {
406       gtk_action_set_sensitive (selector->action,
407                                 gtk_tree_selection_count_selected_rows
408                                 (treeselection) <= 1 );
409     }
410 }
411
412 /* Callback for when the destination treeview selection changes */
413 static void
414 on_dest_treeview_select (GtkTreeSelection *treeselection, gpointer data)
415 {
416   PsppireSelector *selector = data;
417
418   gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (selector->source)));
419
420   set_direction (selector, PSPPIRE_SELECTOR_DEST_TO_SOURCE);
421 }
422
423 /* Callback for source deselection, when the dest is GtkEntry */
424 static void
425 de_select_selection_entry (PsppireSelector *selector)
426 {
427   gtk_entry_set_text (GTK_ENTRY (selector->dest), "");
428 }
429
430 /* Callback for source deselection, when the dest is GtkTreeView */
431 static void
432 de_select_selection_tree_view (PsppireSelector *selector)
433 {
434   GList *item;
435
436   GtkTreeSelection* selection =
437     gtk_tree_view_get_selection ( GTK_TREE_VIEW (selector->dest));
438
439   GtkTreeModel *model =
440     gtk_tree_view_get_model (GTK_TREE_VIEW (selector->dest));
441
442   GList *selected_rows =
443     gtk_tree_selection_get_selected_rows (selection, NULL);
444
445   g_return_if_fail (selector->select_items);
446
447   /* Convert paths to RowRefs */
448   for (item = g_list_first (selected_rows);
449        item != NULL;
450        item = g_list_next (item))
451     {
452       GtkTreeRowReference* rowref;
453       GtkTreePath *path  = item->data;
454
455       rowref = gtk_tree_row_reference_new (GTK_TREE_MODEL (model), path);
456
457       item->data = rowref ;
458       gtk_tree_path_free (path);
459     }
460
461   /* Remove each selected row from the dest widget */
462   for (item = g_list_first (selected_rows);
463        item != NULL;
464        item = g_list_next (item))
465     {
466       GtkTreeIter iter;
467       GtkTreeRowReference *rr = item->data;
468
469       GtkTreePath *path = gtk_tree_row_reference_get_path (rr);
470
471       gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path);
472
473       gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
474
475       gtk_tree_path_free (path);
476     }
477
478   /* Delete list of RowRefs and its contents */
479   g_list_foreach (selected_rows, (GFunc) gtk_tree_row_reference_free, NULL);
480   g_list_free (selected_rows);
481 }
482
483
484 /* Removes something from the DEST widget */
485 static void
486 de_select_selection (PsppireSelector *selector)
487 {
488   selector->selecting = TRUE;
489
490   if ( GTK_IS_TREE_VIEW (selector->dest ) )
491     de_select_selection_tree_view (selector);
492
493   else if ( GTK_IS_ENTRY (selector->dest))
494     de_select_selection_entry (selector);
495
496   else
497     g_assert_not_reached ();
498
499   selector->selecting = FALSE;
500
501   gtk_tree_model_filter_refilter (selector->filtered_source);
502
503   g_signal_emit (selector, signals [DE_SELECTED], 0);
504 }
505
506
507 /* Puts something into the DEST widget */
508 static void
509 select_selection (PsppireSelector *selector)
510 {
511   GList *item ;
512   GtkTreeSelection* selection =
513     gtk_tree_view_get_selection ( GTK_TREE_VIEW (selector->source));
514
515   GList *selected_rows =
516     gtk_tree_selection_get_selected_rows (selection, NULL);
517
518   GtkTreeModel *childmodel  = gtk_tree_model_filter_get_model
519     (selector->filtered_source);
520
521   g_return_if_fail (selector->select_items);
522
523   selector->selecting = TRUE;
524
525   for (item = g_list_first (selected_rows);
526        item != NULL;
527        item = g_list_next (item))
528     {
529       GtkTreeIter child_iter;
530       GtkTreeIter iter;
531       GtkTreePath *path  = item->data;
532
533       gtk_tree_model_get_iter (GTK_TREE_MODEL (selector->filtered_source),
534                                &iter, path);
535
536       gtk_tree_model_filter_convert_iter_to_child_iter
537         (selector->filtered_source,
538          &child_iter,
539          &iter);
540
541       selector->select_items (child_iter,
542                               selector->dest,
543                               childmodel,
544                               selector->select_user_data
545                               );
546     }
547
548   g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
549   g_list_free (selected_rows);
550
551   gtk_tree_model_filter_refilter (selector->filtered_source);
552
553   g_signal_emit (selector, signals [SELECTED], 0);
554
555   selector->selecting = FALSE;
556 }
557
558 /* Callback for when the source treeview is activated (double clicked) */
559 static void
560 on_row_activate (GtkTreeView       *tree_view,
561                  GtkTreePath       *path,
562                  GtkTreeViewColumn *column,
563                  gpointer           data)
564 {
565   PsppireSelector *selector  = data;
566
567   gtk_action_activate (selector->action);
568 }
569
570 /* Callback for when the selector button is clicked,
571    or other event which causes the selector's action to occur.
572  */
573 static void
574 on_activate (PsppireSelector *selector, gpointer data)
575 {
576   switch (selector->direction)
577     {
578     case PSPPIRE_SELECTOR_SOURCE_TO_DEST:
579       select_selection (selector);
580       break;
581     case PSPPIRE_SELECTOR_DEST_TO_SOURCE:
582       de_select_selection (selector);
583       break;
584     default:
585       g_assert_not_reached ();
586       break;
587     }
588 }
589
590 /* Default visibility filter for GtkTreeView DEST widget */
591 static gboolean
592 is_item_in_dest (GtkTreeModel *model, GtkTreeIter *iter,
593                  PsppireSelector *selector)
594 {
595   GtkTreeModel *dest_model;
596   GtkTreeIter dest_iter;
597   GtkTreeIter source_iter;
598   gint index;
599   GtkTreePath *path ;
600   GtkTreeModel *source_model;
601
602   if ( GTK_IS_TREE_MODEL_FILTER (model) )
603     {
604       source_model = gtk_tree_model_filter_get_model
605         (GTK_TREE_MODEL_FILTER (model));
606
607       gtk_tree_model_filter_convert_iter_to_child_iter
608         ( GTK_TREE_MODEL_FILTER (model),  &source_iter,  iter  );
609     }
610   else
611     {
612       source_model = model;
613       source_iter = *iter;
614     }
615
616   dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector->dest));
617
618   path = gtk_tree_model_get_path (source_model, &source_iter);
619
620   index = *gtk_tree_path_get_indices (path);
621
622   gtk_tree_path_free (path);
623
624   if ( ! gtk_tree_model_get_iter_first (dest_model, &dest_iter) )
625     return FALSE;
626
627   do
628     {
629       int x;
630       GValue value = {0};
631       GValue int_value = {0};
632       gtk_tree_model_get_value (dest_model, &dest_iter, 0, &value);
633
634       g_value_init (&int_value, G_TYPE_INT);
635
636       g_value_transform (&value, &int_value);
637
638       x = g_value_get_int (&int_value);
639
640       g_value_unset (&int_value);
641       g_value_unset (&value);
642
643       if ( x == index )
644         return TRUE;
645     }
646   while (gtk_tree_model_iter_next (dest_model, &dest_iter));
647
648   return FALSE;
649 }
650
651 /* Visibility function for items in the SOURCE widget.
652    Returns TRUE iff *all* the selectors for which SOURCE is associated
653    are visible */
654 static gboolean
655 is_source_item_visible (GtkTreeModel *childmodel,
656                         GtkTreeIter *iter, gpointer data)
657 {
658   PsppireSelector *selector = data;
659   PsppireSelectorClass *class = g_type_class_peek (PSPPIRE_SELECTOR_TYPE);
660
661   GList *list = NULL;
662
663   list = g_hash_table_lookup (class->source_hash, selector->source);
664
665   while (list)
666     {
667       PsppireSelector *selector = list->data;
668
669       if ( selector->filter && selector->filter (childmodel, iter, selector))
670         return FALSE;
671
672       list = list->next;
673     }
674
675
676   return TRUE;
677 }
678
679 /* set the source widget to SOURCE */
680 static void
681 set_tree_view_source (PsppireSelector *selector,
682                       GtkTreeView *source)
683 {
684   GtkTreeSelection* selection ;
685   GList *list = NULL;
686
687   PsppireSelectorClass *class = g_type_class_peek (PSPPIRE_SELECTOR_TYPE);
688
689   if ( ! (list = g_hash_table_lookup (class->source_hash, source)))
690     {
691       selector->filtered_source =
692         GTK_TREE_MODEL_FILTER (gtk_tree_model_filter_new
693                                (gtk_tree_view_get_model (source),  NULL));
694
695       gtk_tree_view_set_model (source, NULL);
696
697       gtk_tree_view_set_model (source,
698                                GTK_TREE_MODEL (selector->filtered_source));
699
700       list = g_list_append (list, selector);
701       g_hash_table_insert (class->source_hash, source, list);
702
703
704       gtk_tree_model_filter_set_visible_func (selector->filtered_source,
705                                               is_source_item_visible,
706                                               selector,
707                                               NULL);
708     }
709   else
710     {  /* Append this selector to the list and push the <source,list>
711           pair onto the hash table */
712
713       selector->filtered_source = GTK_TREE_MODEL_FILTER (
714         gtk_tree_view_get_model (source));
715
716       list = g_list_append (list, selector);
717       g_hash_table_replace (class->source_hash, source, list);
718     }
719
720   selection = gtk_tree_view_get_selection (source);
721
722   g_signal_connect (source, "row-activated", G_CALLBACK (on_row_activate),
723                     selector);
724
725   g_signal_connect (selection, "changed", G_CALLBACK (on_source_select),
726                     selector);
727 }
728
729
730 /*
731    Callback for when the destination treeview's data changes
732  */
733 static void
734 on_dest_data_change (GtkTreeModel *tree_model,
735                      GtkTreePath  *path,
736                      GtkTreeIter  *iter,
737                      gpointer      user_data)
738 {
739   PsppireSelector *selector = user_data;
740
741   if ( selector->selecting) return;
742
743   gtk_tree_model_filter_refilter (selector->filtered_source);
744 }
745
746
747 static void
748 on_dest_data_delete (GtkTreeModel *tree_model,
749                      GtkTreePath  *path,
750                      gpointer      user_data)
751 {
752   PsppireSelector *selector = user_data;
753
754   if ( selector->selecting ) return;
755
756   gtk_tree_model_filter_refilter (selector->filtered_source);
757 }
758
759
760 static void
761 xxx (PsppireSelector *selector)
762 {
763   GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector->dest));
764
765   g_signal_connect (model, "row-changed", G_CALLBACK (on_dest_data_change),
766                     selector);
767
768   g_signal_connect (model, "row-deleted", G_CALLBACK (on_dest_data_delete),
769                     selector);
770 }
771
772 /* Set the destination widget to DEST */
773 static void
774 set_tree_view_dest (PsppireSelector *selector,
775                     GtkTreeView *dest)
776 {
777   GtkTreeSelection* selection = gtk_tree_view_get_selection (dest);
778
779
780   gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
781
782   g_signal_connect (selection, "changed", G_CALLBACK (on_dest_treeview_select),
783                     selector);
784
785
786   g_signal_connect_swapped (dest, "notify::model",
787                             G_CALLBACK (xxx), selector);
788
789 }
790
791 /* Callback which causes the filter to be refiltered.
792    Called when the DEST GtkEntry is activated (Enter is pressed), or when it
793    looses focus.
794 */
795 static gboolean
796 refilter (PsppireSelector *selector)
797 {
798   gtk_tree_model_filter_refilter (selector->filtered_source);
799   return FALSE;
800 }
801
802 /* Callback for when the DEST GtkEntry is selected (clicked) */
803 static gboolean
804 on_entry_dest_select (GtkWidget *widget, GdkEventFocus *event, gpointer data)
805 {
806   PsppireSelector * selector = data;
807
808   gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (selector->source)));
809   set_direction (selector, PSPPIRE_SELECTOR_DEST_TO_SOURCE);
810
811   return FALSE;
812 }
813
814
815
816 /* Callback for when an item disappears from the source list.
817    By implication, this means that the item has been inserted into the
818    destination.
819  */
820 static void
821 on_row_deleted (PsppireSelector *selector)
822 {
823   g_signal_emit (selector, signals [SELECTED], 0);
824 }
825
826 /* Callback for when a new item appears in the source list.
827    By implication, this means that an item has been deleted from the
828    destination.
829  */
830 static void
831 on_row_inserted (PsppireSelector *selector)
832 {
833   g_signal_emit (selector, signals [DE_SELECTED], 0);
834 }
835
836
837
838 /* Set DEST to be the destination GtkEntry widget */
839 static void
840 set_entry_dest (PsppireSelector *selector,
841                 GtkEntry *dest)
842 {
843   g_signal_connect_swapped (dest, "activate", G_CALLBACK (refilter),
844                     selector);
845
846   g_signal_connect_swapped (dest, "changed", G_CALLBACK (refilter),
847                     selector);
848
849   g_signal_connect (dest, "focus-in-event", G_CALLBACK (on_entry_dest_select),
850                     selector);
851
852   g_signal_connect_swapped (dest, "focus-out-event", G_CALLBACK (refilter),
853                     selector);
854
855
856   g_signal_connect_swapped (selector->filtered_source, "row-deleted",
857                     G_CALLBACK (on_row_deleted), selector);
858
859   g_signal_connect_swapped (selector->filtered_source, "row-inserted",
860                     G_CALLBACK (on_row_inserted), selector);
861 }
862
863 static void
864 update_subjects (PsppireSelector *selector)
865 {
866   GtkTreeModel *model = NULL;
867
868   if ( NULL == selector->dest )
869     return;
870
871   if ( selector->filter == NULL)
872     {
873       if  (GTK_IS_TREE_VIEW (selector->dest))
874         selector->filter = is_item_in_dest;
875     }
876
877   if ( NULL == selector->source )
878     return;
879
880   g_signal_connect_swapped (selector->source, "notify::model",
881                             G_CALLBACK (update_subjects), selector);
882
883   model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector->source));
884
885   if ( NULL == model)
886     return;
887
888
889   if ( GTK_IS_TREE_VIEW (selector->source))
890     set_tree_view_source (selector, GTK_TREE_VIEW (selector->source) );
891   else
892     g_error ("Unsupported source widget: %s", G_OBJECT_TYPE_NAME (selector->source));
893
894   if ( NULL == selector->dest)
895     ;
896   else if  ( GTK_IS_TREE_VIEW (selector->dest))
897     {
898       set_tree_view_dest (selector, GTK_TREE_VIEW (selector->dest));
899     }
900
901   else if ( GTK_IS_ENTRY (selector->dest))
902     set_entry_dest (selector, GTK_ENTRY (selector->dest));
903
904   else if (GTK_IS_TEXT_VIEW (selector->dest))
905     {
906       /* Nothing to be done */
907     }
908   else
909     g_error ("Unsupported destination widget: %s", G_OBJECT_TYPE_NAME (selector->dest));
910
911 }
912
913
914 /* Set FILTER_FUNC for this selector */
915 void
916 psppire_selector_set_filter_func (PsppireSelector *selector,
917                                   FilterItemsFunc *filter_func)
918 {
919   selector->filter = filter_func ;
920
921   if ( selector->filter == NULL)
922     {
923       if  (GTK_IS_TREE_VIEW (selector->dest))
924         selector->filter = is_item_in_dest;
925     }
926 }
927
928
929 /* Set SELECT_FUNC for this selector */
930 void
931 psppire_selector_set_select_func (PsppireSelector *selector,
932                                SelectItemsFunc *select_func,
933                                gpointer user_data)
934 {
935   selector->select_user_data = user_data;
936   selector->select_items = select_func;
937 }
938
939
940
941 void
942 psppire_selector_set_allow (PsppireSelector *selector, AllowSelectionFunc *allow)
943 {
944   selector->allow_selection = allow;
945 }
946
947
948
949 GType
950 psppire_selector_orientation_get_type (void)
951 {
952   static GType etype = 0;
953   if (etype == 0) {
954     static const GEnumValue values[] = {
955       { PSPPIRE_SELECT_SOURCE_BEFORE_DEST, "PSPPIRE_SELECT_SOURCE_BEFORE_DEST", "source before destination" },
956       { PSPPIRE_SELECT_SOURCE_AFTER_DEST, "PSPPIRE_SELECT_SOURCE_AFTER_DEST", "source after destination" },
957       { PSPPIRE_SELECT_SOURCE_ABOVE_DEST, "PSPPIRE_SELECT_SOURCE_ABOVE_DEST", "source above destination" },
958       { PSPPIRE_SELECT_SOURCE_BELOW_DEST, "PSPPIRE_SELECT_SOURCE_BELOW_DEST", "source below destination" },
959       { 0, NULL, NULL }
960     };
961     etype = g_enum_register_static (g_intern_static_string ("PsppireSelectorOrientation"), values);
962   }
963   return etype;
964 }