psppire-selector.c: Remove unused preprocessor macro
[pspp] / src / ui / gui / psppire-selector.c
index 83fe2d82792a04fb34425b3f0f5a221dde2238fb..027f31203b64d5ce7559e643c08b1b699fd4fd2b 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPPIRE - a graphical user interface for PSPP.
-   Copyright (C) 2007, 2009 Free Software Foundation
+   Copyright (C) 2007, 2009, 2010, 2012, 2015 Free Software Foundation
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -19,7 +19,7 @@
   This module provides a widget, PsppireSelector derived from
   GtkButton.
 
-  It contains a GtkArrow, and is used for selecting objects from a
+  It contains a GtkImage (to indicate the arrow), and is used for selecting objects from a
   GtkTreeView and putting them into a destination widget (often
   another GtkTreeView).  Typically this is used in psppire for
   selecting variables, thus:
 #include <config.h>
 
 #include "psppire-dictview.h"
-#include "psppire-var-view.h"
 #include "psppire-dict.h"
 #include "psppire-select-dest.h"
+#include "psppire-means-layer.h"
 
-#include <gtk/gtksignal.h>
-#include <gtk/gtkbutton.h>
-#include <gtk/gtkentry.h>
+#include <gtk/gtk.h>
 
 #include "psppire-selector.h"
 
-#include <gtk/gtktreeview.h>
-#include <gtk/gtktreeselection.h>
-#include <gtk/gtktextview.h>
-#include <gtk/gtkwidget.h>
-
-static void psppire_selector_base_finalize (PsppireSelectorClass *, gpointer);
-static void psppire_selector_base_init     (PsppireSelectorClass *class);
 static void psppire_selector_class_init    (PsppireSelectorClass *class);
 static void psppire_selector_init          (PsppireSelector      *selector);
 
@@ -89,6 +80,26 @@ enum  {SELECTED,    /* Emitted when an item is inserted into dest */
 
 static guint signals [n_SIGNALS];
 
+/* Callback for when an item disappears from the source list.
+   By implication, this means that the item has been inserted into the
+   destination.
+ */
+static void
+on_row_deleted (PsppireSelector *selector)
+{
+  g_signal_emit (selector, signals [SELECTED], 0);
+}
+
+/* Callback for when a new item appears in the source list.
+   By implication, this means that an item has been deleted from the
+   destination.
+ */
+static void
+on_row_inserted (PsppireSelector *selector)
+{
+  g_signal_emit (selector, signals [DE_SELECTED], 0);
+}
+
 
 GType
 psppire_selector_get_type (void)
@@ -100,8 +111,8 @@ psppire_selector_get_type (void)
       static const GTypeInfo psppire_selector_info =
       {
        sizeof (PsppireSelectorClass),
-       (GBaseInitFunc) psppire_selector_base_init,
-        (GBaseFinalizeFunc) psppire_selector_base_finalize,
+       (GBaseInitFunc) NULL, 
+        (GBaseFinalizeFunc) NULL,
        (GClassInitFunc)psppire_selector_class_init,
        (GClassFinalizeFunc) NULL,
        NULL,
@@ -120,18 +131,41 @@ psppire_selector_get_type (void)
 
 static GObjectClass * parent_class = NULL;
 
+
+
 static void
-psppire_selector_finalize (GObject *obj)
+dump_hash_entry (gpointer key, gpointer value, gpointer obj)
 {
-   /* Chain up to the parent class */
-   G_OBJECT_CLASS (parent_class)->finalize (obj);
+  GList *item = NULL;
+  g_print ("Source %p; ", key);
+
+  for (item = g_list_first (value);
+       item != NULL;
+       item = g_list_next (item))
+    {
+      g_print ("%p(%p) ", item->data, item);
+    }
+  g_print ("\n");
+}
+
+/* This function is for debugging only */
+void 
+psppire_selector_show_map (PsppireSelector *obj)
+{
+  PsppireSelectorClass *class = g_type_class_peek (PSPPIRE_SELECTOR_TYPE);
+
+  g_print ("%s %p\n", __FUNCTION__, obj);
+  g_hash_table_foreach (class->source_hash, dump_hash_entry, obj);
 }
 
 
+
 static void
 psppire_selector_dispose (GObject *obj)
 {
   PsppireSelector *sel = PSPPIRE_SELECTOR (obj);
+  PsppireSelectorClass *class = g_type_class_peek (PSPPIRE_SELECTOR_TYPE);
+  GList *list;
 
   if (sel->dispose_has_run)
     return;
@@ -139,6 +173,21 @@ psppire_selector_dispose (GObject *obj)
   /* Make sure dispose does not run twice. */
   sel->dispose_has_run = TRUE;
 
+  /* Remove ourself from the source map. If we are the only entry for this source
+     widget, that will result in an empty list for the source.  In that case, we
+     remove the entire entry too. */
+  if ((list = g_hash_table_lookup (class->source_hash, sel->source)))
+    {
+      GList *newlist = g_list_remove_link (list, sel->source_litem);
+      g_list_free (sel->source_litem);
+      if (newlist == NULL)
+       g_hash_table_remove (class->source_hash, sel->source);
+      else
+       g_hash_table_replace (class->source_hash, sel->source, newlist);
+
+      sel->source_litem = NULL;
+    }
+  
   g_object_unref (sel->dest);
   g_object_unref (sel->source);
 
@@ -158,9 +207,8 @@ enum
 };
 
 
-static void on_activate (PsppireSelector *selector, gpointer data);
-
-static void update_subjects (PsppireSelector *selector);
+static void on_click (GtkButton *b);
+static void on_realize (GtkWidget *selector);
 
 
 static void
@@ -179,15 +227,15 @@ psppire_selector_set_property (GObject         *object,
       break;
     case PROP_PRIMARY:
       selector->primary_requested = TRUE;
-      update_subjects (selector);
+      psppire_selector_update_subjects (selector);
       break;
     case PROP_SOURCE_WIDGET:
       selector->source = g_value_dup_object (value);
-      update_subjects (selector);
+      psppire_selector_update_subjects (selector);
       break;
     case PROP_DEST_WIDGET:
       selector->dest = g_value_dup_object (value);
-      update_subjects (selector);
+      psppire_selector_update_subjects (selector);
       break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -225,6 +273,8 @@ static void
 psppire_selector_class_init (PsppireSelectorClass *class)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (class);
+  GtkButtonClass *button_class = GTK_BUTTON_CLASS (class);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
   GParamSpec *orientation_spec =
     g_param_spec_enum ("orientation",
                       "Orientation",
@@ -257,6 +307,10 @@ psppire_selector_class_init (PsppireSelectorClass *class)
                         G_PARAM_READWRITE);
 
 
+  button_class->clicked = on_click;
+  widget_class->realize = on_realize;
+
+
   object_class->set_property = psppire_selector_set_property;
   object_class->get_property = psppire_selector_get_property;
 
@@ -298,31 +352,13 @@ psppire_selector_class_init (PsppireSelectorClass *class)
                  G_TYPE_NONE,
                  0);
 
-  class->default_selection_funcs = g_hash_table_new (g_direct_hash, g_direct_equal);
-}
-
-
-static void
-psppire_selector_base_init (PsppireSelectorClass *class)
-{
-  GObjectClass *object_class = G_OBJECT_CLASS (class);
-
-  object_class->finalize = psppire_selector_finalize;
   object_class->dispose = psppire_selector_dispose;
 
   class->source_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
+  class->default_selection_funcs = g_hash_table_new (g_direct_hash, g_direct_equal);
 }
 
 
-
-static void
-psppire_selector_base_finalize(PsppireSelectorClass *class,
-                               gpointer class_data)
-{
-  g_hash_table_destroy (class->source_hash);
-  g_hash_table_destroy (class->default_selection_funcs);
-}
-
 /* Callback for when the source treeview is activated (double clicked) */
 static void
 on_row_activate (GtkTreeView       *tree_view,
@@ -330,9 +366,7 @@ on_row_activate (GtkTreeView       *tree_view,
                 GtkTreeViewColumn *column,
                 gpointer           data)
 {
-  PsppireSelector *selector  = data;
-
-  gtk_action_activate (selector->action);
+  on_click (GTK_BUTTON (data));
 }
 
 /* Callback for when the source selection changes */
@@ -345,12 +379,12 @@ on_source_select (GtkTreeSelection *treeselection, gpointer data)
 
   if ( selector->allow_selection )
     {
-      gtk_action_set_sensitive (selector->action,
+      gtk_widget_set_sensitive (GTK_WIDGET (selector),
                                selector->allow_selection (selector->source, selector->dest));
     }
   else if ( GTK_IS_ENTRY (selector->dest) )
     {
-      gtk_action_set_sensitive (selector->action,
+      gtk_widget_set_sensitive (GTK_WIDGET (selector),
                                gtk_tree_selection_count_selected_rows
                                (treeselection) <= 1 );
     }
@@ -358,13 +392,17 @@ on_source_select (GtkTreeSelection *treeselection, gpointer data)
 
 
 static void
-on_realize (PsppireSelector *selector)
+on_realize (GtkWidget *w)
 {
+  PsppireSelector *selector = PSPPIRE_SELECTOR (w);
   PsppireSelectorClass *class = g_type_class_peek (PSPPIRE_SELECTOR_TYPE);
   GtkTreeSelection* selection ;
 
   GList *list = g_hash_table_lookup (class->source_hash, selector->source);
 
+  if (GTK_WIDGET_CLASS (parent_class)->realize)
+    GTK_WIDGET_CLASS (parent_class)->realize (w);
+
   if ( NULL == list)
     return;
 
@@ -396,19 +434,12 @@ psppire_selector_init (PsppireSelector *selector)
   selector->allow_selection = NULL;
   selector->filter = NULL;
 
-  selector->arrow = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_NONE);
-  selector->filtered_source = NULL;
-
-  selector->action = gtk_action_new ("select", NULL, NULL, "pspp-stock-select");
-
-  gtk_action_connect_proxy (selector->action, GTK_WIDGET (selector));
+  selector->arrow = gtk_image_new_from_icon_name ("pan-start-symbolic", GTK_ICON_SIZE_BUTTON);
 
   gtk_container_add (GTK_CONTAINER (selector), selector->arrow);
 
   gtk_widget_show (selector->arrow);
 
-  g_signal_connect_swapped (selector->action, "activate", G_CALLBACK (on_activate), selector);
-
   selector->selecting = FALSE;
 
   selector->source = NULL;
@@ -419,9 +450,7 @@ psppire_selector_init (PsppireSelector *selector)
   selector->row_activate_id = 0;
   selector->source_select_id  = 0;
 
-  g_signal_connect (selector, "realize",
-                   G_CALLBACK (on_realize), NULL);
-
+  selector->source_litem = NULL;
 }
 
 
@@ -444,16 +473,16 @@ set_direction (PsppireSelector *selector, enum psppire_selector_dir d)
       switch (selector->orientation)
        {
        case   PSPPIRE_SELECT_SOURCE_BEFORE_DEST:
-         g_object_set (selector->arrow, "arrow-type", GTK_ARROW_RIGHT, NULL);
+         g_object_set (selector->arrow, "icon-name", "pan-end-symbolic", NULL);
          break;
        case   PSPPIRE_SELECT_SOURCE_AFTER_DEST:
-         g_object_set (selector->arrow, "arrow-type", GTK_ARROW_LEFT, NULL);
+         g_object_set (selector->arrow, "icon-name", "pan-start-symbolic", NULL);
          break;
        case   PSPPIRE_SELECT_SOURCE_ABOVE_DEST:
-         g_object_set (selector->arrow, "arrow-type", GTK_ARROW_DOWN, NULL);
+         g_object_set (selector->arrow, "icon-name", "pan-down-symbolic", NULL);
          break;
        case   PSPPIRE_SELECT_SOURCE_BELOW_DEST:
-         g_object_set (selector->arrow, "arrow-type", GTK_ARROW_UP, NULL);
+         g_object_set (selector->arrow, "icon-name", "pan-up-symbolic", NULL);
          break;
        default:
          g_assert_not_reached ();
@@ -465,22 +494,21 @@ set_direction (PsppireSelector *selector, enum psppire_selector_dir d)
       switch (selector->orientation)
        {
        case   PSPPIRE_SELECT_SOURCE_BEFORE_DEST:
-         g_object_set (selector->arrow, "arrow-type", GTK_ARROW_LEFT, NULL);
+         g_object_set (selector->arrow, "icon-name", "pan-start-symbolic", NULL);
          break;
        case   PSPPIRE_SELECT_SOURCE_AFTER_DEST:
-         g_object_set (selector->arrow, "arrow-type", GTK_ARROW_RIGHT, NULL);
+         g_object_set (selector->arrow, "icon-name", "pan-end-symbolic", NULL);
          break;
        case   PSPPIRE_SELECT_SOURCE_ABOVE_DEST:
-         g_object_set (selector->arrow, "arrow-type", GTK_ARROW_UP, NULL);
+         g_object_set (selector->arrow, "icon-name", "pan-up-symbolic", NULL);
          break;
        case   PSPPIRE_SELECT_SOURCE_BELOW_DEST:
-         g_object_set (selector->arrow, "arrow-type", GTK_ARROW_DOWN, NULL);
+         g_object_set (selector->arrow, "icon-name", "pan-down-symbolic", NULL);
          break;
        default:
          g_assert_not_reached ();
          break;
        };
-
     }
 }
 
@@ -495,6 +523,7 @@ on_dest_treeview_select (GtkTreeSelection *treeselection, gpointer data)
   set_direction (selector, PSPPIRE_SELECTOR_DEST_TO_SOURCE);
 }
 
+
 /* Callback for source deselection, when the dest is GtkEntry */
 static void
 de_select_selection_entry (PsppireSelector *selector)
@@ -502,23 +531,47 @@ de_select_selection_entry (PsppireSelector *selector)
   gtk_entry_set_text (GTK_ENTRY (selector->dest), "");
 }
 
+
+static void  de_select_tree_model (GtkTreeSelection *selection, GtkTreeModel *model);
+
+/* Callback for source deselection, when the dest is PsppireMeansLayer */
+static void
+de_select_selection_means_layer (PsppireSelector *selector)
+{
+  PsppireMeansLayer *ml = PSPPIRE_MEANS_LAYER (selector->dest);
+  GtkTreeView *tv = GTK_TREE_VIEW (ml->var_view);
+  GtkTreeSelection *selection = gtk_tree_view_get_selection (tv);
+
+  GtkTreeModel *model = psppire_means_layer_get_model (ml);
+
+  g_return_if_fail (selector->select_items);
+
+  de_select_tree_model (selection, model);
+}
+
 /* Callback for source deselection, when the dest is GtkTreeView */
 static void
 de_select_selection_tree_view (PsppireSelector *selector)
 {
-  GList *item;
-
   GtkTreeSelection* selection =
     gtk_tree_view_get_selection ( GTK_TREE_VIEW (selector->dest));
 
   GtkTreeModel *model =
     gtk_tree_view_get_model (GTK_TREE_VIEW (selector->dest));
 
+  g_return_if_fail (selector->select_items);
+
+  de_select_tree_model (selection, model);
+}
+
+static void 
+de_select_tree_model (GtkTreeSelection *selection, GtkTreeModel *model)
+{
+  GList *item;
+
   GList *selected_rows =
     gtk_tree_selection_get_selected_rows (selection, NULL);
 
-  g_return_if_fail (selector->select_items);
-
   /* Convert paths to RowRefs */
   for (item = g_list_first (selected_rows);
        item != NULL;
@@ -556,6 +609,19 @@ de_select_selection_tree_view (PsppireSelector *selector)
 }
 
 
+/* Callback which causes the filter to be refiltered.
+   Called when the DEST GtkEntry is activated (Enter is pressed), or when it
+   looses focus.
+*/
+static gboolean
+refilter (PsppireSelector *selector)
+{
+  GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector->source));
+  if (GTK_IS_TREE_MODEL_FILTER (model))
+    gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
+  return FALSE;
+}
+
 /* Removes something from the DEST widget */
 static void
 de_select_selection (PsppireSelector *selector)
@@ -568,12 +634,15 @@ de_select_selection (PsppireSelector *selector)
   else if ( GTK_IS_ENTRY (selector->dest))
     de_select_selection_entry (selector);
 
+  else if ( PSPPIRE_IS_MEANS_LAYER (selector->dest))
+    de_select_selection_means_layer (selector);
+
   else
     g_assert_not_reached ();
 
   selector->selecting = FALSE;
 
-  gtk_tree_model_filter_refilter (selector->filtered_source);
+  refilter (selector);
 
   g_signal_emit (selector, signals [DE_SELECTED], 0);
 }
@@ -587,14 +656,18 @@ select_selection (PsppireSelector *selector)
   GtkTreeSelection* selection =
     gtk_tree_view_get_selection ( GTK_TREE_VIEW (selector->source));
 
-  GList *selected_rows =
-    gtk_tree_selection_get_selected_rows (selection, NULL);
+  GList *selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
 
-  GtkTreeModel *childmodel  = gtk_tree_model_filter_get_model
-    (selector->filtered_source);
+  GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector->source));
+
+  GtkTreeModel *childmodel = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
 
   g_return_if_fail (selector->select_items);
 
+  if (selector->allow_selection && 
+      ! selector->allow_selection (selector->source, selector->dest))
+    return;
+
   selector->selecting = TRUE;
 
   for (item = g_list_first (selected_rows);
@@ -605,14 +678,12 @@ select_selection (PsppireSelector *selector)
       GtkTreeIter iter;
       GtkTreePath *path  = item->data;
 
-      gtk_tree_model_get_iter (GTK_TREE_MODEL (selector->filtered_source),
-                              &iter, path);
+      g_return_if_fail (model);
 
-      gtk_tree_model_filter_convert_iter_to_child_iter
-       (selector->filtered_source,
-        &child_iter,
-        &iter);
+      gtk_tree_model_get_iter (model, &iter, path);
 
+      gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model),
+                                                       &child_iter, &iter);
       selector->select_items (child_iter,
                              selector->dest,
                              childmodel,
@@ -623,7 +694,7 @@ select_selection (PsppireSelector *selector)
   g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
   g_list_free (selected_rows);
 
-  gtk_tree_model_filter_refilter (selector->filtered_source);
+  refilter (selector);
 
   g_signal_emit (selector, signals [SELECTED], 0);
 
@@ -634,8 +705,10 @@ select_selection (PsppireSelector *selector)
    or other event which causes the selector's action to occur.
  */
 static void
-on_activate (PsppireSelector *selector, gpointer data)
+on_click (GtkButton *b)
 {
+  PsppireSelector *selector = PSPPIRE_SELECTOR (b);
+
   switch (selector->direction)
     {
     case PSPPIRE_SELECTOR_SOURCE_TO_DEST:
@@ -648,6 +721,9 @@ on_activate (PsppireSelector *selector, gpointer data)
       g_assert_not_reached ();
       break;
     }
+
+  if (GTK_BUTTON_CLASS (parent_class)->clicked)
+    GTK_BUTTON_CLASS (parent_class)->clicked (b);
 }
 
 static gboolean
@@ -714,50 +790,90 @@ is_source_item_visible (GtkTreeModel *childmodel,
 
 /* set the source widget to SOURCE */
 static void
-set_tree_view_source (PsppireSelector *selector,
-                     GtkTreeView *source)
+set_tree_view_source (PsppireSelector *selector)
 {
   GList *list = NULL;
 
   PsppireSelectorClass *class = g_type_class_peek (PSPPIRE_SELECTOR_TYPE);
   
-  GtkTreeModel *model = gtk_tree_view_get_model (source);
-
-  if ( ! (list = g_hash_table_lookup (class->source_hash, source)))
+  if ( ! (list = g_hash_table_lookup (class->source_hash, selector->source)))
     {
-      selector->filtered_source =
-       GTK_TREE_MODEL_FILTER (gtk_tree_model_filter_new (model, NULL));
-
-      gtk_tree_view_set_model (source,
-                              GTK_TREE_MODEL (selector->filtered_source));
+      /* Base case:  This widget is currently not the source of 
+        any selector.  Create a hash entry and make this selector
+        the first selector in the list */
 
       list = g_list_append (list, selector);
-      g_hash_table_insert (class->source_hash, source, list);
-
+      g_hash_table_insert (class->source_hash, selector->source, list);
 
-      gtk_tree_model_filter_set_visible_func (selector->filtered_source,
-                                             is_source_item_visible,
-                                             selector,
-                                             NULL);
+      /* Save the list item so that it can be removed later */
+      selector->source_litem = list;
     }
   else
     {  /* Append this selector to the list and push the <source,list>
          pair onto the hash table */
 
-      selector->filtered_source = GTK_TREE_MODEL_FILTER (model);
-
       if ( NULL == g_list_find (list, selector) )
        {
          if ( selector->primary_requested )
-           list = g_list_prepend (list, selector);
+           {
+             list = g_list_prepend (list, selector);
+             selector->source_litem = list;
+           }
          else
-           list = g_list_append (list, selector);
-         g_hash_table_replace (class->source_hash, source, list);
+           {
+             list = g_list_append (list, selector);
+             selector->source_litem = g_list_last (list);
+           }
+         g_hash_table_replace (class->source_hash, selector->source, list);
        }
     }
 }
 
 
+
+/* This function is a callback which occurs when the
+   SOURCE's model has changed */
+static void
+update_model (
+              GtkTreeView *source,
+              GParamSpec *psp,
+              PsppireSelector *selector
+              )
+{
+  GtkTreeModel *model = gtk_tree_view_get_model (source);
+
+  g_assert (source == GTK_TREE_VIEW (selector->source));
+
+  if (model && (model == g_object_get_data (G_OBJECT (source), "model-copy")))
+    return;
+
+  if (model != NULL) 
+    {      
+      GtkTreeModel *new_model = gtk_tree_model_filter_new (model, NULL); 
+
+      g_object_set_data (G_OBJECT (source), "model-copy", new_model);  
+
+      gtk_tree_view_set_model (source, new_model);
+  
+      gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_model),
+                                              is_source_item_visible,
+                                              selector,
+                                              NULL);
+
+      g_signal_connect_swapped (new_model,
+                               "row-deleted",
+                               G_CALLBACK (on_row_deleted), selector);
+
+      g_signal_connect_swapped (new_model,
+                               "row-inserted",
+                               G_CALLBACK (on_row_inserted), selector);
+
+      g_object_unref (new_model);
+    }
+}
+
+
+
 /*
    Callback for when the destination treeview's data changes
  */
@@ -771,7 +887,7 @@ on_dest_data_change (GtkTreeModel *tree_model,
 
   if ( selector->selecting) return;
 
-  gtk_tree_model_filter_refilter (selector->filtered_source);
+  refilter (selector);
 }
 
 
@@ -784,20 +900,35 @@ on_dest_data_delete (GtkTreeModel *tree_model,
 
   if ( selector->selecting ) return;
 
-  gtk_tree_model_filter_refilter (selector->filtered_source);
+  refilter (selector);
 }
 
 
+static void
+remove_selector_handlers (PsppireSelector *selector, GObject *sel)
+{
+  g_signal_handlers_disconnect_by_data (sel, selector);
+}
+
 static void
 on_dest_model_changed (PsppireSelector *selector)
 {
   GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector->dest));
 
+  if (model == NULL) 
+    return;
+
   g_signal_connect (model, "row-changed", G_CALLBACK (on_dest_data_change),
                    selector);
-
+  
   g_signal_connect (model, "row-deleted", G_CALLBACK (on_dest_data_delete),
                    selector);
+
+  g_signal_connect (selector, "destroy", G_CALLBACK (remove_selector_handlers), model);
+  
+  if ( selector->selecting ) return;
+  
+  refilter (selector);
 }
 
 /* Set the destination widget to DEST */
@@ -818,16 +949,17 @@ set_tree_view_dest (PsppireSelector *selector,
                            G_CALLBACK (on_dest_model_changed), selector);
 }
 
-
-/* Callback which causes the filter to be refiltered.
-   Called when the DEST GtkEntry is activated (Enter is pressed), or when it
-   looses focus.
-*/
-static gboolean
-refilter (PsppireSelector *selector)
+static void
+set_layer_dest (PsppireSelector *selector,
+                   PsppireMeansLayer *dest)
 {
-  gtk_tree_model_filter_refilter (selector->filtered_source);
-  return FALSE;
+  GtkTreeSelection* selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dest->var_view));
+
+  gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
+
+
+  g_signal_connect (selection, "changed", G_CALLBACK (on_dest_treeview_select),
+                   selector);
 }
 
 
@@ -844,27 +976,6 @@ on_entry_dest_select (GtkWidget *widget, GdkEventFocus *event, gpointer data)
 }
 
 
-/* Callback for when an item disappears from the source list.
-   By implication, this means that the item has been inserted into the
-   destination.
- */
-static void
-on_row_deleted (PsppireSelector *selector)
-{
-  g_signal_emit (selector, signals [SELECTED], 0);
-}
-
-/* Callback for when a new item appears in the source list.
-   By implication, this means that an item has been deleted from the
-   destination.
- */
-static void
-on_row_inserted (PsppireSelector *selector)
-{
-  g_signal_emit (selector, signals [DE_SELECTED], 0);
-}
-
-
 
 /* Set DEST to be the destination GtkEntry widget */
 static void
@@ -884,11 +995,6 @@ set_entry_dest (PsppireSelector *selector,
                    selector);
 
 
-  g_signal_connect_swapped (selector->filtered_source, "row-deleted",
-                   G_CALLBACK (on_row_deleted), selector);
-
-  g_signal_connect_swapped (selector->filtered_source, "row-inserted",
-                   G_CALLBACK (on_row_inserted), selector);
 }
 
 static void
@@ -901,11 +1007,10 @@ set_default_filter (PsppireSelector *selector)
     }
 }
 
-static void
-update_subjects (PsppireSelector *selector)
-{
-  GtkTreeModel *model = NULL;
 
+void
+psppire_selector_update_subjects (PsppireSelector *selector)
+{
   if ( NULL == selector->dest )
     return;
 
@@ -914,17 +1019,15 @@ update_subjects (PsppireSelector *selector)
   if ( NULL == selector->source )
     return;
 
-  g_signal_connect_swapped (selector->source, "notify::model",
-                           G_CALLBACK (update_subjects), selector);
-
-  model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector->source));
-
-  if ( NULL == model)
-    return;
+  if ( GTK_IS_TREE_VIEW (selector->source))
+    {
+      set_tree_view_source (selector);
 
+      g_signal_connect (selector->source, "notify::model", 
+                              G_CALLBACK (update_model), selector); 
 
-  if ( GTK_IS_TREE_VIEW (selector->source))
-    set_tree_view_source (selector, GTK_TREE_VIEW (selector->source) );
+      update_model (GTK_TREE_VIEW (selector->source), 0, selector);
+    }
   else
     g_error ("Unsupported source widget: %s", G_OBJECT_TYPE_NAME (selector->source));
 
@@ -934,10 +1037,14 @@ update_subjects (PsppireSelector *selector)
     {
       set_tree_view_dest (selector, GTK_TREE_VIEW (selector->dest));
     }
-
   else if ( GTK_IS_ENTRY (selector->dest))
-    set_entry_dest (selector, GTK_ENTRY (selector->dest));
-
+    {
+      set_entry_dest (selector, GTK_ENTRY (selector->dest));
+    }
+  else if (PSPPIRE_IS_MEANS_LAYER (selector->dest))
+    {
+      set_layer_dest (selector, PSPPIRE_MEANS_LAYER (selector->dest));
+    }
   else if (GTK_IS_TEXT_VIEW (selector->dest))
     {
       /* Nothing to be done */
@@ -981,8 +1088,6 @@ psppire_selector_set_filter_func (PsppireSelector *selector,
                                  FilterItemsFunc *filter_func)
 {
   selector->filter = filter_func ;
-  
-  set_default_filter (selector);
 }
 
 
@@ -1009,15 +1114,17 @@ GType
 psppire_selector_orientation_get_type (void)
 {
   static GType etype = 0;
-  if (etype == 0) {
-    static const GEnumValue values[] = {
-      { PSPPIRE_SELECT_SOURCE_BEFORE_DEST, "PSPPIRE_SELECT_SOURCE_BEFORE_DEST", "source before destination" },
-      { PSPPIRE_SELECT_SOURCE_AFTER_DEST, "PSPPIRE_SELECT_SOURCE_AFTER_DEST", "source after destination" },
-      { PSPPIRE_SELECT_SOURCE_ABOVE_DEST, "PSPPIRE_SELECT_SOURCE_ABOVE_DEST", "source above destination" },
-      { PSPPIRE_SELECT_SOURCE_BELOW_DEST, "PSPPIRE_SELECT_SOURCE_BELOW_DEST", "source below destination" },
-      { 0, NULL, NULL }
-    };
-    etype = g_enum_register_static (g_intern_static_string ("PsppireSelectorOrientation"), values);
-  }
+  if (etype == 0)
+    {
+      static const GEnumValue values[] =
+       {
+         { PSPPIRE_SELECT_SOURCE_BEFORE_DEST, "PSPPIRE_SELECT_SOURCE_BEFORE_DEST", "source before destination" },
+         { PSPPIRE_SELECT_SOURCE_AFTER_DEST, "PSPPIRE_SELECT_SOURCE_AFTER_DEST", "source after destination" },
+         { PSPPIRE_SELECT_SOURCE_ABOVE_DEST, "PSPPIRE_SELECT_SOURCE_ABOVE_DEST", "source above destination" },
+         { PSPPIRE_SELECT_SOURCE_BELOW_DEST, "PSPPIRE_SELECT_SOURCE_BELOW_DEST", "source below destination" },
+         { 0, NULL, NULL }
+       };
+      etype = g_enum_register_static (g_intern_static_string ("PsppireSelectorOrientation"), values);
+    }
   return etype;
 }