Fix bug in casemapping long strings.
[pspp-builds.git] / src / ui / gui / psppire-selector.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2007  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
116 static void
117 psppire_selector_finalize (GObject *object)
118 {
119 }
120
121 /* Properties */
122 enum
123 {
124   PROP_0,
125   PROP_ORIENTATION
126 };
127
128
129 static void on_activate (PsppireSelector *selector, gpointer data);
130
131
132 static void
133 psppire_selector_set_property (GObject         *object,
134                                guint            prop_id,
135                                const GValue    *value,
136                                GParamSpec      *pspec)
137 {
138   PsppireSelector *selector = PSPPIRE_SELECTOR (object);
139
140   switch (prop_id)
141     {
142     case PROP_ORIENTATION:
143       selector->orientation = g_value_get_enum (value);
144       set_direction (selector, selector->direction);
145       break;
146     default:
147       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
148       break;
149     };
150 }
151
152
153 static void
154 psppire_selector_get_property (GObject         *object,
155                                guint            prop_id,
156                                GValue          *value,
157                                GParamSpec      *pspec)
158 {
159   PsppireSelector *selector = PSPPIRE_SELECTOR (object);
160
161   switch (prop_id)
162     {
163     case PROP_ORIENTATION:
164       g_value_set_enum (value, selector->orientation);
165       break;
166     default:
167       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
168       break;
169     };
170 }
171
172
173
174 static void
175 psppire_selector_class_init (PsppireSelectorClass *class)
176 {
177   GObjectClass *object_class = G_OBJECT_CLASS (class);
178   GParamSpec *orientation_spec =
179     g_param_spec_enum ("orientation",
180                        "Orientation",
181                        "Where the selector is relative to its subjects",
182                        PSPPIRE_TYPE_SELECTOR_ORIENTATION,
183                        PSPPIRE_SELECT_SOURCE_BEFORE_DEST /* default value */,
184                        G_PARAM_CONSTRUCT_ONLY |G_PARAM_READWRITE);
185
186
187   object_class->set_property = psppire_selector_set_property;
188   object_class->get_property = psppire_selector_get_property;
189
190   g_object_class_install_property (object_class,
191                                    PROP_ORIENTATION,
192                                    orientation_spec);
193
194   signals [SELECTED] =
195     g_signal_new ("selected",
196                   G_TYPE_FROM_CLASS (class),
197                   G_SIGNAL_RUN_FIRST,
198                   0,
199                   NULL, NULL,
200                   g_cclosure_marshal_VOID__VOID,
201                   G_TYPE_NONE,
202                   0);
203
204   signals [DE_SELECTED] =
205     g_signal_new ("de-selected",
206                   G_TYPE_FROM_CLASS (class),
207                   G_SIGNAL_RUN_FIRST,
208                   0,
209                   NULL, NULL,
210                   g_cclosure_marshal_VOID__VOID,
211                   G_TYPE_NONE,
212                   0);
213 }
214
215
216 static void
217 psppire_selector_base_init (PsppireSelectorClass *class)
218 {
219   GObjectClass *object_class = G_OBJECT_CLASS (class);
220
221   object_class->finalize = psppire_selector_finalize;
222
223   class->source_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
224 }
225
226
227
228 static void
229 psppire_selector_base_finalize(PsppireSelectorClass *class,
230                                 gpointer class_data)
231 {
232   g_hash_table_destroy (class->source_hash);
233 }
234
235
236
237 static void
238 psppire_selector_init (PsppireSelector *selector)
239 {
240   selector->arrow = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_NONE);
241   selector->filtered_source = NULL;
242
243   selector->action = gtk_action_new ("select", NULL, NULL, "pspp-stock-select");
244
245   gtk_action_connect_proxy (selector->action, GTK_WIDGET (selector));
246
247   gtk_container_add (GTK_CONTAINER (selector), selector->arrow);
248
249   gtk_widget_show (selector->arrow);
250
251   g_signal_connect_swapped (selector->action, "activate", G_CALLBACK (on_activate), selector);
252
253   selector->selecting = FALSE;
254 }
255
256
257 GtkWidget*
258 psppire_selector_new (void)
259 {
260   return GTK_WIDGET (g_object_new (psppire_selector_get_type (), NULL));
261 }
262
263
264 static void
265 set_direction (PsppireSelector *selector, enum psppire_selector_dir d)
266 {
267   selector->direction = d;
268
269   /* FIXME: Need to reverse the arrow direction if an RTL locale is in
270      effect */
271   if ( d == PSPPIRE_SELECTOR_SOURCE_TO_DEST )
272     {
273       switch (selector->orientation)
274         {
275         case   PSPPIRE_SELECT_SOURCE_BEFORE_DEST:
276           g_object_set (selector->arrow, "arrow-type", GTK_ARROW_RIGHT, NULL);
277           break;
278         case   PSPPIRE_SELECT_SOURCE_AFTER_DEST:
279           g_object_set (selector->arrow, "arrow-type", GTK_ARROW_LEFT, NULL);
280           break;
281         case   PSPPIRE_SELECT_SOURCE_ABOVE_DEST:
282           g_object_set (selector->arrow, "arrow-type", GTK_ARROW_DOWN, NULL);
283           break;
284         case   PSPPIRE_SELECT_SOURCE_BELOW_DEST:
285           g_object_set (selector->arrow, "arrow-type", GTK_ARROW_UP, NULL);
286           break;
287         default:
288           g_assert_not_reached ();
289           break;
290         };
291     }
292   else
293     {
294       switch (selector->orientation)
295         {
296         case   PSPPIRE_SELECT_SOURCE_BEFORE_DEST:
297           g_object_set (selector->arrow, "arrow-type", GTK_ARROW_LEFT, NULL);
298           break;
299         case   PSPPIRE_SELECT_SOURCE_AFTER_DEST:
300           g_object_set (selector->arrow, "arrow-type", GTK_ARROW_RIGHT, NULL);
301           break;
302         case   PSPPIRE_SELECT_SOURCE_ABOVE_DEST:
303           g_object_set (selector->arrow, "arrow-type", GTK_ARROW_UP, NULL);
304           break;
305         case   PSPPIRE_SELECT_SOURCE_BELOW_DEST:
306           g_object_set (selector->arrow, "arrow-type", GTK_ARROW_DOWN, NULL);
307           break;
308         default:
309           g_assert_not_reached ();
310           break;
311         };
312
313     }
314 }
315
316 /* Callback for when the source selection changes */
317 static void
318 on_source_select (GtkTreeSelection *treeselection, gpointer data)
319 {
320   PsppireSelector *selector = data;
321
322   set_direction (selector, PSPPIRE_SELECTOR_SOURCE_TO_DEST);
323
324   if ( selector->allow_selection )
325     {
326       gtk_action_set_sensitive (selector->action,
327                                 selector->allow_selection (selector->source, selector->dest));
328     }
329   else if ( GTK_IS_ENTRY (selector->dest) )
330     {
331       gtk_action_set_sensitive (selector->action,
332                                 gtk_tree_selection_count_selected_rows
333                                 (treeselection) <= 1 );
334     }
335 }
336
337 /* Callback for when the destination treeview selection changes */
338 static void
339 on_dest_treeview_select (GtkTreeSelection *treeselection, gpointer data)
340 {
341   PsppireSelector *selector = data;
342
343   gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (selector->source)));
344
345   set_direction (selector, PSPPIRE_SELECTOR_DEST_TO_SOURCE);
346 }
347
348 /* Callback for source deselection, when the dest is GtkEntry */
349 static void
350 de_select_selection_entry (PsppireSelector *selector)
351 {
352   gtk_entry_set_text (GTK_ENTRY (selector->dest), "");
353 }
354
355 /* Callback for source deselection, when the dest is GtkTreeView */
356 static void
357 de_select_selection_tree_view (PsppireSelector *selector)
358 {
359   GList *item;
360
361   GtkTreeSelection* selection =
362     gtk_tree_view_get_selection ( GTK_TREE_VIEW (selector->dest));
363
364   GtkTreeModel *model =
365     gtk_tree_view_get_model (GTK_TREE_VIEW (selector->dest));
366
367   GList *selected_rows =
368     gtk_tree_selection_get_selected_rows (selection, NULL);
369
370   g_return_if_fail (selector->select_items);
371
372   /* Convert paths to RowRefs */
373   for (item = g_list_first (selected_rows);
374        item != NULL;
375        item = g_list_next (item))
376     {
377       GtkTreeRowReference* rowref;
378       GtkTreePath *path  = item->data;
379
380       rowref = gtk_tree_row_reference_new (GTK_TREE_MODEL (model), path);
381
382       item->data = rowref ;
383       gtk_tree_path_free (path);
384     }
385
386   /* Remove each selected row from the dest widget */
387   for (item = g_list_first (selected_rows);
388        item != NULL;
389        item = g_list_next (item))
390     {
391       GtkTreeIter iter;
392       GtkTreeRowReference *rr = item->data;
393
394       GtkTreePath *path = gtk_tree_row_reference_get_path (rr);
395
396       gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path);
397
398       gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
399
400       gtk_tree_path_free (path);
401     }
402
403   /* Delete list of RowRefs and its contents */
404   g_list_foreach (selected_rows, (GFunc) gtk_tree_row_reference_free, NULL);
405   g_list_free (selected_rows);
406 }
407
408
409 /* Removes something from the DEST widget */
410 static void
411 de_select_selection (PsppireSelector *selector)
412 {
413   selector->selecting = TRUE;
414
415   if ( GTK_IS_TREE_VIEW (selector->dest ) )
416     de_select_selection_tree_view (selector);
417
418   else if ( GTK_IS_ENTRY (selector->dest))
419     de_select_selection_entry (selector);
420
421   else
422     g_assert_not_reached ();
423
424   selector->selecting = FALSE;
425
426   gtk_tree_model_filter_refilter (selector->filtered_source);
427
428   g_signal_emit (selector, signals [DE_SELECTED], 0);
429 }
430
431
432 /* Puts something into the DEST widget */
433 static void
434 select_selection (PsppireSelector *selector)
435 {
436   GList *item ;
437   GtkTreeSelection* selection =
438     gtk_tree_view_get_selection ( GTK_TREE_VIEW (selector->source));
439
440   GList *selected_rows =
441     gtk_tree_selection_get_selected_rows (selection, NULL);
442
443   GtkTreeModel *childmodel  = gtk_tree_model_filter_get_model
444     (selector->filtered_source);
445
446   g_return_if_fail (selector->select_items);
447
448   selector->selecting = TRUE;
449
450   for (item = g_list_first (selected_rows);
451        item != NULL;
452        item = g_list_next (item))
453     {
454       GtkTreeIter child_iter;
455       GtkTreeIter iter;
456       GtkTreePath *path  = item->data;
457
458       gtk_tree_model_get_iter (GTK_TREE_MODEL (selector->filtered_source),
459                                &iter, path);
460
461       gtk_tree_model_filter_convert_iter_to_child_iter
462         (selector->filtered_source,
463          &child_iter,
464          &iter);
465
466       selector->select_items (child_iter,
467                               selector->dest,
468                               childmodel,
469                               selector->select_user_data
470                               );
471     }
472
473   g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
474   g_list_free (selected_rows);
475
476   gtk_tree_model_filter_refilter (selector->filtered_source);
477
478   g_signal_emit (selector, signals [SELECTED], 0);
479
480   selector->selecting = FALSE;
481 }
482
483 /* Callback for when the source treeview is activated (double clicked) */
484 static void
485 on_row_activate (GtkTreeView       *tree_view,
486                  GtkTreePath       *path,
487                  GtkTreeViewColumn *column,
488                  gpointer           data)
489 {
490   PsppireSelector *selector  = data;
491
492   gtk_action_activate (selector->action);
493 }
494
495 /* Callback for when the selector button is clicked,
496    or other event which causes the selector's action to occur.
497  */
498 static void
499 on_activate (PsppireSelector *selector, gpointer data)
500 {
501   switch (selector->direction)
502     {
503     case PSPPIRE_SELECTOR_SOURCE_TO_DEST:
504       select_selection (selector);
505       break;
506     case PSPPIRE_SELECTOR_DEST_TO_SOURCE:
507       de_select_selection (selector);
508       break;
509     default:
510       g_assert_not_reached ();
511       break;
512     }
513 }
514
515 /* Default visibility filter for GtkTreeView DEST widget */
516 static gboolean
517 is_item_in_dest (GtkTreeModel *model, GtkTreeIter *iter,
518                  PsppireSelector *selector)
519 {
520   GtkTreeModel *dest_model;
521   GtkTreeIter dest_iter;
522   GtkTreeIter source_iter;
523   gint index;
524   GtkTreePath *path ;
525   GtkTreeModel *source_model;
526
527   if ( GTK_IS_TREE_MODEL_FILTER (model) )
528     {
529       source_model = gtk_tree_model_filter_get_model
530         (GTK_TREE_MODEL_FILTER (model));
531
532       gtk_tree_model_filter_convert_iter_to_child_iter
533         ( GTK_TREE_MODEL_FILTER (model),  &source_iter,  iter  );
534     }
535   else
536     {
537       source_model = model;
538       source_iter = *iter;
539     }
540
541   dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector->dest));
542
543   path = gtk_tree_model_get_path (source_model, &source_iter);
544
545   index = *gtk_tree_path_get_indices (path);
546
547   gtk_tree_path_free (path);
548
549   if ( ! gtk_tree_model_get_iter_first (dest_model, &dest_iter) )
550     return FALSE;
551
552   do
553     {
554       int x;
555       GValue value = {0};
556       GValue int_value = {0};
557       gtk_tree_model_get_value (dest_model, &dest_iter, 0, &value);
558
559       g_value_init (&int_value, G_TYPE_INT);
560
561       g_value_transform (&value, &int_value);
562
563       x = g_value_get_int (&int_value);
564
565       g_value_unset (&int_value);
566       g_value_unset (&value);
567
568       if ( x == index )
569         return TRUE;
570     }
571   while (gtk_tree_model_iter_next (dest_model, &dest_iter));
572
573   return FALSE;
574 }
575
576 /* Visibility function for items in the SOURCE widget.
577    Returns TRUE iff *all* the selectors for which SOURCE is associated
578    are visible */
579 static gboolean
580 is_source_item_visible (GtkTreeModel *childmodel,
581                         GtkTreeIter *iter, gpointer data)
582 {
583   PsppireSelector *selector = data;
584   PsppireSelectorClass *class = g_type_class_peek (PSPPIRE_SELECTOR_TYPE);
585
586   GList *list = NULL;
587
588   list = g_hash_table_lookup (class->source_hash, selector->source);
589
590   while (list)
591     {
592       PsppireSelector *selector = list->data;
593
594       if ( selector->filter && selector->filter (childmodel, iter, selector))
595         return FALSE;
596
597       list = list->next;
598     }
599
600
601   return TRUE;
602 }
603
604 /* set the source widget to SOURCE */
605 static void
606 set_tree_view_source (PsppireSelector *selector,
607                       GtkTreeView *source)
608 {
609   GtkTreeSelection* selection ;
610   GList *list = NULL;
611
612   PsppireSelectorClass *class = g_type_class_peek (PSPPIRE_SELECTOR_TYPE);
613
614   if ( ! (list = g_hash_table_lookup (class->source_hash, source)))
615     {
616       selector->filtered_source =
617         GTK_TREE_MODEL_FILTER (gtk_tree_model_filter_new
618                                (gtk_tree_view_get_model (source),  NULL));
619
620       gtk_tree_view_set_model (source, NULL);
621
622       gtk_tree_view_set_model (source,
623                                GTK_TREE_MODEL (selector->filtered_source));
624
625       list = g_list_append (list, selector);
626       g_hash_table_insert (class->source_hash, source, list);
627
628
629       gtk_tree_model_filter_set_visible_func (selector->filtered_source,
630                                               is_source_item_visible,
631                                               selector,
632                                               NULL);
633     }
634   else
635     {  /* Append this selector to the list and push the <source,list>
636           pair onto the hash table */
637
638       selector->filtered_source = GTK_TREE_MODEL_FILTER (
639         gtk_tree_view_get_model (source));
640
641       list = g_list_append (list, selector);
642       g_hash_table_replace (class->source_hash, source, list);
643     }
644
645   selection = gtk_tree_view_get_selection (source);
646
647   g_signal_connect (source, "row-activated", G_CALLBACK (on_row_activate),
648                     selector);
649
650   g_signal_connect (selection, "changed", G_CALLBACK (on_source_select),
651                     selector);
652 }
653
654
655 /*
656    Callback for when the destination treeview's data changes
657  */
658 static void
659 on_dest_data_change (GtkTreeModel *tree_model,
660                      GtkTreePath  *path,
661                      GtkTreeIter  *iter,
662                      gpointer      user_data)
663 {
664   PsppireSelector *selector = user_data;
665
666   if ( selector->selecting) return;
667
668   gtk_tree_model_filter_refilter (selector->filtered_source);
669 }
670
671
672 static void
673 on_dest_data_delete (GtkTreeModel *tree_model,
674                      GtkTreePath  *path,
675                      gpointer      user_data)
676 {
677   PsppireSelector *selector = user_data;
678
679   if ( selector->selecting ) return;
680
681   gtk_tree_model_filter_refilter (selector->filtered_source);
682 }
683
684
685
686
687 /* Set the destination widget to DEST */
688 static void
689 set_tree_view_dest (PsppireSelector *selector,
690                     GtkTreeView *dest)
691 {
692   GtkTreeSelection* selection = gtk_tree_view_get_selection (dest);
693   GtkTreeModel *model = gtk_tree_view_get_model (dest);
694
695   gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
696
697   g_signal_connect (selection, "changed", G_CALLBACK (on_dest_treeview_select),
698                     selector);
699
700   g_signal_connect (model, "row-changed", G_CALLBACK (on_dest_data_change),
701                       selector);
702
703   g_signal_connect (model, "row-deleted", G_CALLBACK (on_dest_data_delete),
704                       selector);
705
706 }
707
708 /* Callback which causes the filter to be refiltered.
709    Called when the DEST GtkEntry is activated (Enter is pressed), or when it
710    looses focus.
711 */
712 static gboolean
713 refilter (PsppireSelector *selector)
714 {
715   gtk_tree_model_filter_refilter (selector->filtered_source);
716   return FALSE;
717 }
718
719 /* Callback for when the DEST GtkEntry is selected (clicked) */
720 static gboolean
721 on_entry_dest_select (GtkWidget *widget, GdkEventFocus *event, gpointer data)
722 {
723   PsppireSelector * selector = data;
724
725   gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (selector->source)));
726   set_direction (selector, PSPPIRE_SELECTOR_DEST_TO_SOURCE);
727
728   return FALSE;
729 }
730
731
732
733 /* Callback for when an item disappears from the source list.
734    By implication, this means that the item has been inserted into the
735    destination.
736  */
737 static void
738 on_row_deleted (PsppireSelector *selector)
739 {
740   g_signal_emit (selector, signals [SELECTED], 0);
741 }
742
743 /* Callback for when a new item appears in the source list.
744    By implication, this means that an item has been deleted from the
745    destination.
746  */
747 static void
748 on_row_inserted (PsppireSelector *selector)
749 {
750   g_signal_emit (selector, signals [DE_SELECTED], 0);
751 }
752
753
754
755 /* Set DEST to be the destination GtkEntry widget */
756 static void
757 set_entry_dest (PsppireSelector *selector,
758                 GtkEntry *dest)
759 {
760   g_signal_connect_swapped (dest, "activate", G_CALLBACK (refilter),
761                     selector);
762
763   g_signal_connect_swapped (dest, "changed", G_CALLBACK (refilter),
764                     selector);
765
766   g_signal_connect (dest, "focus-in-event", G_CALLBACK (on_entry_dest_select),
767                     selector);
768
769   g_signal_connect_swapped (dest, "focus-out-event", G_CALLBACK (refilter),
770                     selector);
771
772
773   g_signal_connect_swapped (selector->filtered_source, "row-deleted",
774                     G_CALLBACK (on_row_deleted), selector);
775
776   g_signal_connect_swapped (selector->filtered_source, "row-inserted",
777                     G_CALLBACK (on_row_inserted), selector);
778 }
779
780
781 /* Set SOURCE and DEST for this selector, and
782    set SELECT_FUNC and FILTER_FUNC */
783 void
784 psppire_selector_set_subjects (PsppireSelector *selector,
785                                GtkWidget *source,
786                                GtkWidget *dest,
787                                SelectItemsFunc *select_func,
788                                FilterItemsFunc *filter_func,
789                                gpointer user_data)
790 {
791   g_assert(selector);
792
793   selector->filter = filter_func ;
794
795   selector->source = source;
796   selector->dest = dest;
797   selector->select_user_data = user_data;
798
799   if ( filter_func == NULL)
800     {
801       if  (GTK_IS_TREE_VIEW (dest))
802         selector->filter = is_item_in_dest;
803     }
804
805   if ( GTK_IS_TREE_VIEW (source))
806     set_tree_view_source (selector, GTK_TREE_VIEW (source) );
807   else
808     g_error ("Unsupported source widget: %s", G_OBJECT_TYPE_NAME (source));
809
810   g_assert ( GTK_IS_TREE_MODEL_FILTER (selector->filtered_source));
811
812   if ( NULL == dest)
813     ;
814   else if  ( GTK_IS_TREE_VIEW (dest))
815     set_tree_view_dest (selector, GTK_TREE_VIEW (dest));
816
817   else if ( GTK_IS_ENTRY (dest))
818     set_entry_dest (selector, GTK_ENTRY (dest));
819
820   else if (GTK_IS_TEXT_VIEW (dest))
821     {
822       /* Nothing to be done */
823     }
824
825   else
826     g_error ("Unsupported destination widget: %s", G_OBJECT_TYPE_NAME (dest));
827
828   selector->select_items = select_func;
829 }
830
831
832
833 void
834 psppire_selector_set_allow        (PsppireSelector *selector , AllowSelectionFunc *allow)
835 {
836   selector->allow_selection = allow;
837 }
838
839
840
841 GType
842 psppire_selector_orientation_get_type (void)
843 {
844   static GType etype = 0;
845   if (etype == 0) {
846     static const GEnumValue values[] = {
847       { PSPPIRE_SELECT_SOURCE_BEFORE_DEST, "PSPPIRE_SELECT_SOURCE_BEFORE_DEST", "source before destination" },
848       { PSPPIRE_SELECT_SOURCE_AFTER_DEST, "PSPPIRE_SELECT_SOURCE_AFTER_DEST", "source after destination" },
849       { PSPPIRE_SELECT_SOURCE_ABOVE_DEST, "PSPPIRE_SELECT_SOURCE_ABOVE_DEST", "source above destination" },
850       { PSPPIRE_SELECT_SOURCE_BELOW_DEST, "PSPPIRE_SELECT_SOURCE_BELOW_DEST", "source below destination" },
851       { 0, NULL, NULL }
852     };
853     etype = g_enum_register_static (g_intern_static_string ("PsppireSelectorOrientation"), values);
854   }
855   return etype;
856 }