Update selector widgets on dialog activation.
[pspp] / src / ui / gui / psppire-selector.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2007, 2009, 2010, 2012, 2015 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 /*
19   This module provides a widget, PsppireSelector derived from
20   GtkButton.
21
22   It contains a GtkImage (to indicate the arrow), and is used for selecting objects from a
23   GtkTreeView and putting them into a destination widget (often
24   another GtkTreeView).  Typically this is used in psppire for
25   selecting variables, thus:
26
27
28   +----------------------------------------------------------+
29   |                                                          |
30   |     Source Widget                       Dest Widget      |
31   |   +----------------+                 +----------------+  |
32   |   | Variable0      |                 | Variable2      |  |
33   |   | Variable1      |                 |                |  |
34   |   | Variable3      |                 |                |  |
35   |   |                |    Selector     |                |  |
36   |   |                |                 |                |  |
37   |   |                |    +------+     |                |  |
38   |   |                |    | |\   |     |                |  |
39   |   |                |    | | \  |     |                |  |
40   |   |                |    | | /  |     |                |  |
41   |   |                |    | |/   |     |                |  |
42   |   |                |    +------+     |                |  |
43   |   |                |                 |                |  |
44   |   |                |                 |                |  |
45   |   |                |                 |                |  |
46   |   |                |                 |                |  |
47   |   +----------------+                 +----------------+  |
48   |                                                          |
49   +----------------------------------------------------------+
50
51   The Source Widget is always a GtkTreeView.  The Dest Widget may be a
52   GtkTreeView or a GtkEntry (other destination widgets may be
53   supported in the future).
54
55   Widgets may be source to more than one PsppireSelector.
56 */
57
58
59 #include <config.h>
60
61 #include "psppire-dictview.h"
62 #include "psppire-dict.h"
63 #include "psppire-select-dest.h"
64 #include "psppire-means-layer.h"
65
66 #include <gtk/gtk.h>
67
68 #include "psppire-selector.h"
69
70 static void psppire_selector_class_init    (PsppireSelectorClass *class);
71 static void psppire_selector_init          (PsppireSelector      *selector);
72
73
74 static void set_direction (PsppireSelector *, enum psppire_selector_dir);
75
76
77 enum  {SELECTED,    /* Emitted when an item is inserted into dest */
78        DE_SELECTED, /* Emitted when an item is removed from dest */
79        n_SIGNALS};
80
81 static guint signals [n_SIGNALS];
82
83 /* Callback for when an item disappears from the source list.
84    By implication, this means that the item has been inserted into the
85    destination.
86  */
87 static void
88 on_row_deleted (PsppireSelector *selector)
89 {
90   g_signal_emit (selector, signals [SELECTED], 0);
91 }
92
93 /* Callback for when a new item appears in the source list.
94    By implication, this means that an item has been deleted from the
95    destination.
96  */
97 static void
98 on_row_inserted (PsppireSelector *selector)
99 {
100   g_signal_emit (selector, signals [DE_SELECTED], 0);
101 }
102
103
104 GType
105 psppire_selector_get_type (void)
106 {
107   static GType psppire_selector_type = 0;
108
109   if (!psppire_selector_type)
110     {
111       static const GTypeInfo psppire_selector_info =
112       {
113         sizeof (PsppireSelectorClass),
114         (GBaseInitFunc) NULL, 
115         (GBaseFinalizeFunc) NULL,
116         (GClassInitFunc)psppire_selector_class_init,
117         (GClassFinalizeFunc) NULL,
118         NULL,
119         sizeof (PsppireSelector),
120         0,
121         (GInstanceInitFunc) psppire_selector_init,
122       };
123
124       psppire_selector_type =
125         g_type_register_static (GTK_TYPE_BUTTON, "PsppireSelector",
126                                 &psppire_selector_info, 0);
127     }
128
129   return psppire_selector_type;
130 }
131
132 static GObjectClass * parent_class = NULL;
133
134
135
136 #define SELECTOR_DEBUGGING 0
137
138 static void
139 dump_hash_entry (gpointer key, gpointer value, gpointer obj)
140 {
141   GList *item = NULL;
142   g_print ("Source %p; ", key);
143
144   for (item = g_list_first (value);
145        item != NULL;
146        item = g_list_next (item))
147     {
148       g_print ("%p(%p) ", item->data, item);
149     }
150   g_print ("\n");
151 }
152
153 /* This function is for debugging only */
154 void 
155 psppire_selector_show_map (PsppireSelector *obj)
156 {
157   PsppireSelectorClass *class = g_type_class_peek (PSPPIRE_SELECTOR_TYPE);
158
159   g_print ("%s %p\n", __FUNCTION__, obj);
160   g_hash_table_foreach (class->source_hash, dump_hash_entry, obj);
161 }
162
163
164
165 static void
166 psppire_selector_dispose (GObject *obj)
167 {
168   PsppireSelector *sel = PSPPIRE_SELECTOR (obj);
169   PsppireSelectorClass *class = g_type_class_peek (PSPPIRE_SELECTOR_TYPE);
170   GList *list;
171
172   if (sel->dispose_has_run)
173     return;
174
175   /* Make sure dispose does not run twice. */
176   sel->dispose_has_run = TRUE;
177
178   /* Remove ourself from the source map. If we are the only entry for this source
179      widget, that will result in an empty list for the source.  In that case, we
180      remove the entire entry too. */
181   if ((list = g_hash_table_lookup (class->source_hash, sel->source)))
182     {
183       GList *newlist = g_list_remove_link (list, sel->source_litem);
184       g_list_free (sel->source_litem);
185       if (newlist == NULL)
186         g_hash_table_remove (class->source_hash, sel->source);
187       else
188         g_hash_table_replace (class->source_hash, sel->source, newlist);
189
190       sel->source_litem = NULL;
191     }
192   
193   g_object_unref (sel->dest);
194   g_object_unref (sel->source);
195
196   /* Chain up to the parent class */
197   G_OBJECT_CLASS (parent_class)->dispose (obj);
198 }
199
200
201 /* Properties */
202 enum
203 {
204   PROP_0,
205   PROP_ORIENTATION,
206   PROP_PRIMARY,
207   PROP_SOURCE_WIDGET,
208   PROP_DEST_WIDGET
209 };
210
211
212 static void on_click (GtkButton *b);
213 static void on_realize (GtkWidget *selector);
214
215
216 static void
217 psppire_selector_set_property (GObject         *object,
218                                guint            prop_id,
219                                const GValue    *value,
220                                GParamSpec      *pspec)
221 {
222   PsppireSelector *selector = PSPPIRE_SELECTOR (object);
223
224   switch (prop_id)
225     {
226     case PROP_ORIENTATION:
227       selector->orientation = g_value_get_enum (value);
228       set_direction (selector, selector->direction);
229       break;
230     case PROP_PRIMARY:
231       selector->primary_requested = TRUE;
232       psppire_selector_update_subjects (selector);
233       break;
234     case PROP_SOURCE_WIDGET:
235       selector->source = g_value_dup_object (value);
236       psppire_selector_update_subjects (selector);
237       break;
238     case PROP_DEST_WIDGET:
239       selector->dest = g_value_dup_object (value);
240       psppire_selector_update_subjects (selector);
241       break;
242     default:
243       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
244       break;
245     };
246 }
247
248
249 static void
250 psppire_selector_get_property (GObject         *object,
251                                guint            prop_id,
252                                GValue          *value,
253                                GParamSpec      *pspec)
254 {
255   PsppireSelector *selector = PSPPIRE_SELECTOR (object);
256
257   switch (prop_id)
258     {
259     case PROP_ORIENTATION:
260       g_value_set_enum (value, selector->orientation);
261       break;
262     case PROP_SOURCE_WIDGET:
263       g_value_take_object (value, selector->source);
264       break;
265     case PROP_DEST_WIDGET:
266       g_value_take_object (value, selector->dest);
267       break;
268     default:
269       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
270       break;
271     };
272 }
273
274 static void
275 psppire_selector_class_init (PsppireSelectorClass *class)
276 {
277   GObjectClass *object_class = G_OBJECT_CLASS (class);
278   GtkButtonClass *button_class = GTK_BUTTON_CLASS (class);
279   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
280   GParamSpec *orientation_spec =
281     g_param_spec_enum ("orientation",
282                        "Orientation",
283                        "Where the selector is relative to its subjects",
284                        PSPPIRE_TYPE_SELECTOR_ORIENTATION,
285                        PSPPIRE_SELECT_SOURCE_BEFORE_DEST /* default value */,
286                        G_PARAM_CONSTRUCT_ONLY |G_PARAM_READWRITE);
287
288
289  /* Meaningfull only if more than one selector shares this selectors source */
290   GParamSpec *primary_spec =
291     g_param_spec_boolean ("primary",
292                           "Primary",
293                           "Whether this selector should be the primary selector for the source",
294                           FALSE,
295                           G_PARAM_READWRITE);
296
297   GParamSpec *source_widget_spec = 
298     g_param_spec_object ("source-widget",
299                          "Source Widget",
300                          "The widget to be used as the source for this selector",
301                          GTK_TYPE_WIDGET,
302                          G_PARAM_READWRITE);
303
304   GParamSpec *dest_widget_spec = 
305     g_param_spec_object ("dest-widget",
306                          "Destination Widget",
307                          "The widget to be used as the destination for this selector",
308                          GTK_TYPE_WIDGET,
309                          G_PARAM_READWRITE);
310
311
312   button_class->clicked = on_click;
313   widget_class->realize = on_realize;
314
315
316   object_class->set_property = psppire_selector_set_property;
317   object_class->get_property = psppire_selector_get_property;
318
319   g_object_class_install_property (object_class,
320                                    PROP_ORIENTATION,
321                                    orientation_spec);
322
323   g_object_class_install_property (object_class,
324                                    PROP_PRIMARY,
325                                    primary_spec);
326
327   g_object_class_install_property (object_class,
328                                    PROP_SOURCE_WIDGET,
329                                    source_widget_spec);
330
331   g_object_class_install_property (object_class,
332                                    PROP_DEST_WIDGET,
333                                    dest_widget_spec);
334
335   parent_class = g_type_class_peek_parent (class);
336
337   signals [SELECTED] =
338     g_signal_new ("selected",
339                   G_TYPE_FROM_CLASS (class),
340                   G_SIGNAL_RUN_FIRST,
341                   0,
342                   NULL, NULL,
343                   g_cclosure_marshal_VOID__VOID,
344                   G_TYPE_NONE,
345                   0);
346
347   signals [DE_SELECTED] =
348     g_signal_new ("de-selected",
349                   G_TYPE_FROM_CLASS (class),
350                   G_SIGNAL_RUN_FIRST,
351                   0,
352                   NULL, NULL,
353                   g_cclosure_marshal_VOID__VOID,
354                   G_TYPE_NONE,
355                   0);
356
357   object_class->dispose = psppire_selector_dispose;
358
359   class->source_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
360   class->default_selection_funcs = g_hash_table_new (g_direct_hash, g_direct_equal);
361 }
362
363
364 /* Callback for when the source treeview is activated (double clicked) */
365 static void
366 on_row_activate (GtkTreeView       *tree_view,
367                  GtkTreePath       *path,
368                  GtkTreeViewColumn *column,
369                  gpointer           data)
370 {
371   on_click (GTK_BUTTON (data));
372 }
373
374 /* Callback for when the source selection changes */
375 static void
376 on_source_select (GtkTreeSelection *treeselection, gpointer data)
377 {
378   PsppireSelector *selector = data;
379
380   set_direction (selector, PSPPIRE_SELECTOR_SOURCE_TO_DEST);
381
382   if ( selector->allow_selection )
383     {
384       gtk_widget_set_sensitive (GTK_WIDGET (selector),
385                                 selector->allow_selection (selector->source, selector->dest));
386     }
387   else if ( GTK_IS_ENTRY (selector->dest) )
388     {
389       gtk_widget_set_sensitive (GTK_WIDGET (selector),
390                                 gtk_tree_selection_count_selected_rows
391                                 (treeselection) <= 1 );
392     }
393 }
394
395
396 static void
397 on_realize (GtkWidget *w)
398 {
399   PsppireSelector *selector = PSPPIRE_SELECTOR (w);
400   PsppireSelectorClass *class = g_type_class_peek (PSPPIRE_SELECTOR_TYPE);
401   GtkTreeSelection* selection ;
402
403   GList *list = g_hash_table_lookup (class->source_hash, selector->source);
404
405   if (GTK_WIDGET_CLASS (parent_class)->realize)
406     GTK_WIDGET_CLASS (parent_class)->realize (w);
407
408   if ( NULL == list)
409     return;
410
411   if ( g_list_first (list)->data == selector)
412     {
413       if ( selector->row_activate_id )
414         g_signal_handler_disconnect (selector->source, selector->row_activate_id);
415
416       selector->row_activate_id =  
417         g_signal_connect (selector->source, "row-activated", G_CALLBACK (on_row_activate), selector);
418     }
419
420   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (selector->source));
421
422   if ( selector->source_select_id )
423     g_signal_handler_disconnect (selection, selector->source_select_id);
424
425   selector->source_select_id = 
426     g_signal_connect (selection, "changed", G_CALLBACK (on_source_select), selector);
427 }
428
429
430 static void
431 psppire_selector_init (PsppireSelector *selector)
432 {
433   selector->primary_requested = FALSE;
434   selector->select_user_data = NULL;
435   selector->select_items = NULL;
436   selector->allow_selection = NULL;
437   selector->filter = NULL;
438
439   selector->arrow = gtk_image_new_from_icon_name ("pan-start-symbolic", GTK_ICON_SIZE_BUTTON);
440
441   gtk_container_add (GTK_CONTAINER (selector), selector->arrow);
442
443   gtk_widget_show (selector->arrow);
444
445   selector->selecting = FALSE;
446
447   selector->source = NULL;
448   selector->dest = NULL;
449   selector->dispose_has_run = FALSE;
450
451
452   selector->row_activate_id = 0;
453   selector->source_select_id  = 0;
454
455   selector->source_litem = NULL;
456 }
457
458
459 GtkWidget*
460 psppire_selector_new (void)
461 {
462   return GTK_WIDGET (g_object_new (psppire_selector_get_type (), NULL));
463 }
464
465
466 static void
467 set_direction (PsppireSelector *selector, enum psppire_selector_dir d)
468 {
469   selector->direction = d;
470
471   /* FIXME: Need to reverse the arrow direction if an RTL locale is in
472      effect */
473   if ( d == PSPPIRE_SELECTOR_SOURCE_TO_DEST )
474     {
475       switch (selector->orientation)
476         {
477         case   PSPPIRE_SELECT_SOURCE_BEFORE_DEST:
478           g_object_set (selector->arrow, "icon-name", "pan-end-symbolic", NULL);
479           break;
480         case   PSPPIRE_SELECT_SOURCE_AFTER_DEST:
481           g_object_set (selector->arrow, "icon-name", "pan-start-symbolic", NULL);
482           break;
483         case   PSPPIRE_SELECT_SOURCE_ABOVE_DEST:
484           g_object_set (selector->arrow, "icon-name", "pan-down-symbolic", NULL);
485           break;
486         case   PSPPIRE_SELECT_SOURCE_BELOW_DEST:
487           g_object_set (selector->arrow, "icon-name", "pan-up-symbolic", NULL);
488           break;
489         default:
490           g_assert_not_reached ();
491           break;
492         };
493     }
494   else
495     {
496       switch (selector->orientation)
497         {
498         case   PSPPIRE_SELECT_SOURCE_BEFORE_DEST:
499           g_object_set (selector->arrow, "icon-name", "pan-start-symbolic", NULL);
500           break;
501         case   PSPPIRE_SELECT_SOURCE_AFTER_DEST:
502           g_object_set (selector->arrow, "icon-name", "pan-end-symbolic", NULL);
503           break;
504         case   PSPPIRE_SELECT_SOURCE_ABOVE_DEST:
505           g_object_set (selector->arrow, "icon-name", "pan-up-symbolic", NULL);
506           break;
507         case   PSPPIRE_SELECT_SOURCE_BELOW_DEST:
508           g_object_set (selector->arrow, "icon-name", "pan-down-symbolic", NULL);
509           break;
510         default:
511           g_assert_not_reached ();
512           break;
513         };
514     }
515 }
516
517 /* Callback for when the destination treeview selection changes */
518 static void
519 on_dest_treeview_select (GtkTreeSelection *treeselection, gpointer data)
520 {
521   PsppireSelector *selector = data;
522
523   gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (selector->source)));
524
525   set_direction (selector, PSPPIRE_SELECTOR_DEST_TO_SOURCE);
526 }
527
528
529 /* Callback for source deselection, when the dest is GtkEntry */
530 static void
531 de_select_selection_entry (PsppireSelector *selector)
532 {
533   gtk_entry_set_text (GTK_ENTRY (selector->dest), "");
534 }
535
536
537 static void  de_select_tree_model (GtkTreeSelection *selection, GtkTreeModel *model);
538
539 /* Callback for source deselection, when the dest is PsppireMeansLayer */
540 static void
541 de_select_selection_means_layer (PsppireSelector *selector)
542 {
543   PsppireMeansLayer *ml = PSPPIRE_MEANS_LAYER (selector->dest);
544   GtkTreeView *tv = GTK_TREE_VIEW (ml->var_view);
545   GtkTreeSelection *selection = gtk_tree_view_get_selection (tv);
546
547   GtkTreeModel *model = psppire_means_layer_get_model (ml);
548
549   g_return_if_fail (selector->select_items);
550
551   de_select_tree_model (selection, model);
552 }
553
554 /* Callback for source deselection, when the dest is GtkTreeView */
555 static void
556 de_select_selection_tree_view (PsppireSelector *selector)
557 {
558   GtkTreeSelection* selection =
559     gtk_tree_view_get_selection ( GTK_TREE_VIEW (selector->dest));
560
561   GtkTreeModel *model =
562     gtk_tree_view_get_model (GTK_TREE_VIEW (selector->dest));
563
564   g_return_if_fail (selector->select_items);
565
566   de_select_tree_model (selection, model);
567 }
568
569 static void 
570 de_select_tree_model (GtkTreeSelection *selection, GtkTreeModel *model)
571 {
572   GList *item;
573
574   GList *selected_rows =
575     gtk_tree_selection_get_selected_rows (selection, NULL);
576
577   /* Convert paths to RowRefs */
578   for (item = g_list_first (selected_rows);
579        item != NULL;
580        item = g_list_next (item))
581     {
582       GtkTreeRowReference* rowref;
583       GtkTreePath *path  = item->data;
584
585       rowref = gtk_tree_row_reference_new (GTK_TREE_MODEL (model), path);
586
587       item->data = rowref ;
588       gtk_tree_path_free (path);
589     }
590
591   /* Remove each selected row from the dest widget */
592   for (item = g_list_first (selected_rows);
593        item != NULL;
594        item = g_list_next (item))
595     {
596       GtkTreeIter iter;
597       GtkTreeRowReference *rr = item->data;
598
599       GtkTreePath *path = gtk_tree_row_reference_get_path (rr);
600
601       gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path);
602
603       gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
604
605       gtk_tree_path_free (path);
606     }
607
608   /* Delete list of RowRefs and its contents */
609   g_list_foreach (selected_rows, (GFunc) gtk_tree_row_reference_free, NULL);
610   g_list_free (selected_rows);
611 }
612
613
614 /* Callback which causes the filter to be refiltered.
615    Called when the DEST GtkEntry is activated (Enter is pressed), or when it
616    looses focus.
617 */
618 static gboolean
619 refilter (PsppireSelector *selector)
620 {
621   GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector->source));
622   if (GTK_IS_TREE_MODEL_FILTER (model))
623     gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
624   return FALSE;
625 }
626
627 /* Removes something from the DEST widget */
628 static void
629 de_select_selection (PsppireSelector *selector)
630 {
631   selector->selecting = TRUE;
632
633   if ( GTK_IS_TREE_VIEW (selector->dest ) )
634     de_select_selection_tree_view (selector);
635
636   else if ( GTK_IS_ENTRY (selector->dest))
637     de_select_selection_entry (selector);
638
639   else if ( PSPPIRE_IS_MEANS_LAYER (selector->dest))
640     de_select_selection_means_layer (selector);
641
642   else
643     g_assert_not_reached ();
644
645   selector->selecting = FALSE;
646
647   refilter (selector);
648
649   g_signal_emit (selector, signals [DE_SELECTED], 0);
650 }
651
652
653 /* Puts something into the DEST widget */
654 static void
655 select_selection (PsppireSelector *selector)
656 {
657   GList *item ;
658   GtkTreeSelection* selection =
659     gtk_tree_view_get_selection ( GTK_TREE_VIEW (selector->source));
660
661   GList *selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
662
663   GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector->source));
664
665   GtkTreeModel *childmodel = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
666
667   g_return_if_fail (selector->select_items);
668
669   if (selector->allow_selection && 
670       ! selector->allow_selection (selector->source, selector->dest))
671     return;
672
673   selector->selecting = TRUE;
674
675   for (item = g_list_first (selected_rows);
676        item != NULL;
677        item = g_list_next (item))
678     {
679       GtkTreeIter child_iter;
680       GtkTreeIter iter;
681       GtkTreePath *path  = item->data;
682
683       g_return_if_fail (model);
684
685       gtk_tree_model_get_iter (model, &iter, path);
686
687       gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model),
688                                                         &child_iter, &iter);
689       selector->select_items (child_iter,
690                               selector->dest,
691                               childmodel,
692                               selector->select_user_data
693                               );
694     }
695
696   g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
697   g_list_free (selected_rows);
698
699   refilter (selector);
700
701   g_signal_emit (selector, signals [SELECTED], 0);
702
703   selector->selecting = FALSE;
704 }
705
706 /* Callback for when the selector button is clicked,
707    or other event which causes the selector's action to occur.
708  */
709 static void
710 on_click (GtkButton *b)
711 {
712   PsppireSelector *selector = PSPPIRE_SELECTOR (b);
713
714   switch (selector->direction)
715     {
716     case PSPPIRE_SELECTOR_SOURCE_TO_DEST:
717       select_selection (selector);
718       break;
719     case PSPPIRE_SELECTOR_DEST_TO_SOURCE:
720       de_select_selection (selector);
721       break;
722     default:
723       g_assert_not_reached ();
724       break;
725     }
726
727   if (GTK_BUTTON_CLASS (parent_class)->clicked)
728     GTK_BUTTON_CLASS (parent_class)->clicked (b);
729 }
730
731 static gboolean
732 is_item_in_dest (GtkTreeModel *model, GtkTreeIter *iter, PsppireSelector *selector)
733 {
734   gboolean result = FALSE;
735   GtkTreeIter source_iter;
736   GtkTreeModel *source_model;
737   GValue value = {0};
738
739   if (GTK_IS_TREE_MODEL_FILTER (model))
740     {
741       source_model = gtk_tree_model_filter_get_model
742         (GTK_TREE_MODEL_FILTER (model));
743
744       gtk_tree_model_filter_convert_iter_to_child_iter
745         (GTK_TREE_MODEL_FILTER (model),  &source_iter, iter);
746     }
747   else
748     {
749       source_model = model;
750       source_iter = *iter;
751     }
752
753   gtk_tree_model_get_value (source_model, &source_iter, DICT_TVM_COL_VAR, &value);
754
755   result = psppire_select_dest_widget_contains_var (PSPPIRE_SELECT_DEST_WIDGET (selector->dest),
756                                                     &value);
757
758   g_value_unset (&value);
759
760   return result;
761 }
762
763
764
765 /* Visibility function for items in the SOURCE widget.
766    Returns TRUE iff *all* the selectors for which SOURCE is associated
767    are visible */
768 static gboolean
769 is_source_item_visible (GtkTreeModel *childmodel,
770                         GtkTreeIter *iter, gpointer data)
771 {
772   PsppireSelector *selector = data;
773   PsppireSelectorClass *class = g_type_class_peek (PSPPIRE_SELECTOR_TYPE);
774
775   GList *list = NULL;
776
777   list = g_hash_table_lookup (class->source_hash, selector->source);
778
779   while (list)
780     {
781       PsppireSelector *selector = list->data;
782
783       if ( selector->filter && selector->filter (childmodel, iter, selector))
784         return FALSE;
785
786       list = list->next;
787     }
788
789
790   return TRUE;
791 }
792
793 /* set the source widget to SOURCE */
794 static void
795 set_tree_view_source (PsppireSelector *selector)
796 {
797   GList *list = NULL;
798
799   PsppireSelectorClass *class = g_type_class_peek (PSPPIRE_SELECTOR_TYPE);
800   
801   if ( ! (list = g_hash_table_lookup (class->source_hash, selector->source)))
802     {
803       /* Base case:  This widget is currently not the source of 
804          any selector.  Create a hash entry and make this selector
805          the first selector in the list */
806
807       list = g_list_append (list, selector);
808       g_hash_table_insert (class->source_hash, selector->source, list);
809
810       /* Save the list item so that it can be removed later */
811       selector->source_litem = list;
812     }
813   else
814     {  /* Append this selector to the list and push the <source,list>
815           pair onto the hash table */
816
817       if ( NULL == g_list_find (list, selector) )
818         {
819           if ( selector->primary_requested )
820             {
821               list = g_list_prepend (list, selector);
822               selector->source_litem = list;
823             }
824           else
825             {
826               list = g_list_append (list, selector);
827               selector->source_litem = g_list_last (list);
828             }
829           g_hash_table_replace (class->source_hash, selector->source, list);
830         }
831     }
832 }
833
834
835
836 /* This function is a callback which occurs when the
837    SOURCE's model has changed */
838 static void
839 update_model (
840               GtkTreeView *source,
841               GParamSpec *psp,
842               PsppireSelector *selector
843               )
844 {
845   GtkTreeModel *model = gtk_tree_view_get_model (source);
846
847   g_assert (source == GTK_TREE_VIEW (selector->source));
848
849   if (model && (model == g_object_get_data (G_OBJECT (source), "model-copy")))
850     return;
851
852   if (model != NULL) 
853     {      
854       GtkTreeModel *new_model = gtk_tree_model_filter_new (model, NULL); 
855
856       g_object_set_data (G_OBJECT (source), "model-copy", new_model);  
857
858       gtk_tree_view_set_model (source, new_model);
859   
860       gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_model),
861                                               is_source_item_visible,
862                                               selector,
863                                               NULL);
864
865       g_signal_connect_swapped (new_model,
866                                 "row-deleted",
867                                 G_CALLBACK (on_row_deleted), selector);
868
869       g_signal_connect_swapped (new_model,
870                                 "row-inserted",
871                                 G_CALLBACK (on_row_inserted), selector);
872
873       g_object_unref (new_model);
874     }
875 }
876
877
878
879 /*
880    Callback for when the destination treeview's data changes
881  */
882 static void
883 on_dest_data_change (GtkTreeModel *tree_model,
884                      GtkTreePath  *path,
885                      GtkTreeIter  *iter,
886                      gpointer      user_data)
887 {
888   PsppireSelector *selector = user_data;
889
890   if ( selector->selecting) return;
891
892   refilter (selector);
893 }
894
895
896 static void
897 on_dest_data_delete (GtkTreeModel *tree_model,
898                      GtkTreePath  *path,
899                      gpointer      user_data)
900 {
901   PsppireSelector *selector = user_data;
902
903   if ( selector->selecting ) return;
904
905   refilter (selector);
906 }
907
908
909 static void
910 remove_selector_handlers (PsppireSelector *selector, GObject *sel)
911 {
912   g_signal_handlers_disconnect_by_data (sel, selector);
913 }
914
915 static void
916 on_dest_model_changed (PsppireSelector *selector)
917 {
918   GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector->dest));
919
920   if (model == NULL) 
921     return;
922
923   g_signal_connect (model, "row-changed", G_CALLBACK (on_dest_data_change),
924                     selector);
925   
926   g_signal_connect (model, "row-deleted", G_CALLBACK (on_dest_data_delete),
927                     selector);
928
929   g_signal_connect (selector, "destroy", G_CALLBACK (remove_selector_handlers), model);
930   
931   if ( selector->selecting ) return;
932   
933   refilter (selector);
934 }
935
936 /* Set the destination widget to DEST */
937 static void
938 set_tree_view_dest (PsppireSelector *selector,
939                     GtkTreeView *dest)
940 {
941   GtkTreeSelection* selection = gtk_tree_view_get_selection (dest);
942
943
944   gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
945
946   g_signal_connect (selection, "changed", G_CALLBACK (on_dest_treeview_select),
947                     selector);
948
949   on_dest_model_changed (selector);
950   g_signal_connect_swapped (dest, "notify::model",
951                             G_CALLBACK (on_dest_model_changed), selector);
952 }
953
954 static void
955 set_layer_dest (PsppireSelector *selector,
956                     PsppireMeansLayer *dest)
957 {
958   GtkTreeSelection* selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dest->var_view));
959
960   gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
961
962
963   g_signal_connect (selection, "changed", G_CALLBACK (on_dest_treeview_select),
964                     selector);
965 }
966
967
968 /* Callback for when the DEST GtkEntry is selected (clicked) */
969 static gboolean
970 on_entry_dest_select (GtkWidget *widget, GdkEventFocus *event, gpointer data)
971 {
972   PsppireSelector * selector = data;
973
974   gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (selector->source)));
975   set_direction (selector, PSPPIRE_SELECTOR_DEST_TO_SOURCE);
976
977   return FALSE;
978 }
979
980
981
982 /* Set DEST to be the destination GtkEntry widget */
983 static void
984 set_entry_dest (PsppireSelector *selector,
985                 GtkEntry *dest)
986 {
987   g_signal_connect_swapped (dest, "activate", G_CALLBACK (refilter),
988                     selector);
989
990   g_signal_connect_swapped (dest, "changed", G_CALLBACK (refilter),
991                     selector);
992
993   g_signal_connect (dest, "focus-in-event", G_CALLBACK (on_entry_dest_select),
994                     selector);
995
996   g_signal_connect_swapped (dest, "focus-out-event", G_CALLBACK (refilter),
997                     selector);
998
999
1000 }
1001
1002 static void
1003 set_default_filter (PsppireSelector *selector)
1004 {
1005   if ( selector->filter == NULL)
1006     {
1007       if  (GTK_IS_TREE_VIEW (selector->dest))
1008         selector->filter = is_item_in_dest;
1009     }
1010 }
1011
1012
1013 void
1014 psppire_selector_update_subjects (PsppireSelector *selector)
1015 {
1016   if ( NULL == selector->dest )
1017     return;
1018
1019   set_default_filter (selector);
1020
1021   if ( NULL == selector->source )
1022     return;
1023
1024   if ( GTK_IS_TREE_VIEW (selector->source))
1025     {
1026       set_tree_view_source (selector);
1027
1028       g_signal_connect (selector->source, "notify::model", 
1029                               G_CALLBACK (update_model), selector); 
1030
1031       update_model (GTK_TREE_VIEW (selector->source), 0, selector);
1032     }
1033   else
1034     g_error ("Unsupported source widget: %s", G_OBJECT_TYPE_NAME (selector->source));
1035
1036   if ( NULL == selector->dest)
1037     ;
1038   else if  ( GTK_IS_TREE_VIEW (selector->dest))
1039     {
1040       set_tree_view_dest (selector, GTK_TREE_VIEW (selector->dest));
1041     }
1042   else if ( GTK_IS_ENTRY (selector->dest))
1043     {
1044       set_entry_dest (selector, GTK_ENTRY (selector->dest));
1045     }
1046   else if (PSPPIRE_IS_MEANS_LAYER (selector->dest))
1047     {
1048       set_layer_dest (selector, PSPPIRE_MEANS_LAYER (selector->dest));
1049     }
1050   else if (GTK_IS_TEXT_VIEW (selector->dest))
1051     {
1052       /* Nothing to be done */
1053     }
1054   else
1055     g_error ("Unsupported destination widget: %s", G_OBJECT_TYPE_NAME (selector->dest));
1056
1057
1058   /* FIXME: Remove this dependency */
1059   if ( PSPPIRE_IS_DICT_VIEW (selector->source) )
1060     {
1061       GObjectClass *class = G_OBJECT_GET_CLASS (selector);
1062       GType type = G_OBJECT_TYPE (selector->dest);
1063
1064       SelectItemsFunc *func  = 
1065         g_hash_table_lookup (PSPPIRE_SELECTOR_CLASS (class)->default_selection_funcs, (gpointer) type);
1066
1067       if ( func )
1068         psppire_selector_set_select_func (PSPPIRE_SELECTOR (selector),
1069                                           func, NULL);
1070     }
1071 }
1072
1073
1074 void
1075 psppire_selector_set_default_selection_func (GType type, SelectItemsFunc *func)
1076 {
1077   GObjectClass *class = g_type_class_ref (PSPPIRE_SELECTOR_TYPE);
1078
1079   g_hash_table_insert (PSPPIRE_SELECTOR_CLASS (class)->default_selection_funcs, (gpointer) type, func);
1080
1081   g_type_class_unref (class);
1082 }
1083
1084
1085
1086
1087 /* Set FILTER_FUNC for this selector */
1088 void
1089 psppire_selector_set_filter_func (PsppireSelector *selector,
1090                                   FilterItemsFunc *filter_func)
1091 {
1092   selector->filter = filter_func ;
1093 }
1094
1095
1096 /* Set SELECT_FUNC for this selector */
1097 void
1098 psppire_selector_set_select_func (PsppireSelector *selector,
1099                                SelectItemsFunc *select_func,
1100                                gpointer user_data)
1101 {
1102   selector->select_user_data = user_data;
1103   selector->select_items = select_func;
1104 }
1105
1106
1107
1108 void
1109 psppire_selector_set_allow (PsppireSelector *selector, AllowSelectionFunc *allow)
1110 {
1111   selector->allow_selection = allow;
1112 }
1113
1114
1115 GType
1116 psppire_selector_orientation_get_type (void)
1117 {
1118   static GType etype = 0;
1119   if (etype == 0)
1120     {
1121       static const GEnumValue values[] =
1122         {
1123           { PSPPIRE_SELECT_SOURCE_BEFORE_DEST, "PSPPIRE_SELECT_SOURCE_BEFORE_DEST", "source before destination" },
1124           { PSPPIRE_SELECT_SOURCE_AFTER_DEST, "PSPPIRE_SELECT_SOURCE_AFTER_DEST", "source after destination" },
1125           { PSPPIRE_SELECT_SOURCE_ABOVE_DEST, "PSPPIRE_SELECT_SOURCE_ABOVE_DEST", "source above destination" },
1126           { PSPPIRE_SELECT_SOURCE_BELOW_DEST, "PSPPIRE_SELECT_SOURCE_BELOW_DEST", "source below destination" },
1127           { 0, NULL, NULL }
1128         };
1129       etype = g_enum_register_static (g_intern_static_string ("PsppireSelectorOrientation"), values);
1130     }
1131   return etype;
1132 }