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