New module: psppire_variable_sheet.c
[pspp] / src / ui / gui / psppire-data-editor.c
index 26c375640580d527a0e435291f5a1b345c9b45f5..2bdf3d6b785ede1b38549013131cc99152e4c9ce 100644 (file)
@@ -1,5 +1,6 @@
 /* PSPPIRE - a graphical user interface for PSPP.
-   Copyright (C) 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
+   Copyright (C) 2008, 2009, 2010, 2011, 2012, 2016,
+   2017 Free Software Foundation, Inc.
 
    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
 #include "data/value-labels.h"
 #include "libpspp/range-set.h"
 #include "libpspp/str.h"
+#include "ui/gui/executor.h"
 #include "ui/gui/helper.h"
-#include "ui/gui/pspp-sheet-selection.h"
-#include "ui/gui/psppire-data-sheet.h"
+#include "ui/gui/var-display.h"
+#include "ui/gui/val-labs-dialog.h"
+#include "ui/gui/missing-val-dialog.h"
+#include "ui/gui/var-type-dialog.h"
+#include "ui/gui/psppire-dict.h"
 #include "ui/gui/psppire-data-store.h"
+#include "ui/gui/psppire-data-window.h"
 #include "ui/gui/psppire-value-entry.h"
-#include "ui/gui/psppire-var-sheet.h"
-#include "ui/gui/psppire.h"
+#include "ui/gui/psppire-conf.h"
+#include "ui/gui/psppire-variable-sheet.h"
+
+#include "value-variant.h"
+
+
+#include "ui/gui/efficient-sheet/jmd-sheet.h"
 
 #include <gettext.h>
 #define _(msgid) gettext (msgid)
 
-#define FOR_EACH_DATA_SHEET(DATA_SHEET, IDX, DATA_EDITOR)       \
-  for ((IDX) = 0;                                               \
-       (IDX) < 4                                                \
-         && ((DATA_SHEET) = PSPPIRE_DATA_SHEET (                \
-               (DATA_EDITOR)->data_sheets[IDX])) != NULL;       \
-       (IDX)++)
-
 static void psppire_data_editor_class_init          (PsppireDataEditorClass *klass);
 static void psppire_data_editor_init                (PsppireDataEditor      *de);
 
-static void disconnect_data_sheets (PsppireDataEditor *);
 static void refresh_entry (PsppireDataEditor *);
-static void psppire_data_editor_update_ui_manager (PsppireDataEditor *);
 
 GType
 psppire_data_editor_get_type (void)
@@ -84,8 +86,6 @@ psppire_data_editor_dispose (GObject *obj)
 {
   PsppireDataEditor *de = (PsppireDataEditor *) obj;
 
-  disconnect_data_sheets (de);
-
   if (de->data_store)
     {
       g_object_unref (de->data_store);
@@ -104,12 +104,6 @@ psppire_data_editor_dispose (GObject *obj)
       de->font = NULL;
     }
 
-  if (de->ui_manager)
-    {
-      g_object_unref (de->ui_manager);
-      de->ui_manager = NULL;
-    }
-
   /* Chain up to the parent class */
   G_OBJECT_CLASS (parent_class)->dispose (obj);
 }
@@ -120,20 +114,31 @@ enum
     PROP_DATA_STORE,
     PROP_DICTIONARY,
     PROP_VALUE_LABELS,
-    PROP_SPLIT_WINDOW,
-    PROP_UI_MANAGER
+    PROP_SPLIT_WINDOW
   };
 
 static void
 psppire_data_editor_refresh_model (PsppireDataEditor *de)
 {
-  PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (de->var_sheet);
-  PsppireDataSheet *data_sheet;
-  int i;
+}
+
+static void
+change_data_value (PsppireDataStore *store, gint col, gint row, GValue *value)
+{
+  const struct variable *var = psppire_dict_get_variable (store->dict, col);
+
+  if (NULL == var)
+    return;
+
+  union value v;
+
+  GVariant *vrnt = g_value_get_variant (value);
+
+  value_variant_get (&v, vrnt);
 
-  FOR_EACH_DATA_SHEET (data_sheet, i, de)
-    psppire_data_sheet_set_data_store (data_sheet, de->data_store);
-  psppire_var_sheet_set_dictionary (var_sheet, de->dict);
+  psppire_data_store_set_value (store, row, var, &v);
+
+  value_destroy_from_variant (&v, vrnt);
 }
 
 static void
@@ -143,13 +148,13 @@ psppire_data_editor_set_property (GObject         *object,
                                  GParamSpec      *pspec)
 {
   PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (object);
-  PsppireDataSheet *data_sheet;
-  int i;
 
   switch (prop_id)
     {
     case PROP_SPLIT_WINDOW:
-      psppire_data_editor_split_window (de, g_value_get_boolean (value));
+      de->split = g_value_get_boolean (value);
+      g_object_set (de->data_sheet, "split", de->split, NULL);
+      g_object_set (de->var_sheet, "split", de->split, NULL);
       break;
     case PROP_DATA_STORE:
       if ( de->data_store)
@@ -162,8 +167,17 @@ psppire_data_editor_set_property (GObject         *object,
 
       de->data_store = g_value_get_pointer (value);
       g_object_ref (de->data_store);
+
+      g_object_set (de->data_sheet, "data-model", de->data_store, NULL);
       psppire_data_editor_refresh_model (de);
 
+      g_signal_connect_swapped (de->data_sheet, "selection-changed",
+                               G_CALLBACK (refresh_entry),
+                               de);
+
+      g_signal_connect_swapped (de->data_sheet, "value-changed",
+                               G_CALLBACK (change_data_value), de->data_store);
+
       g_signal_connect_swapped (de->data_store, "case-changed",
                                 G_CALLBACK (refresh_entry), de);
 
@@ -174,15 +188,12 @@ psppire_data_editor_set_property (GObject         *object,
       de->dict = g_value_get_pointer (value);
       g_object_ref (de->dict);
 
-      psppire_var_sheet_set_dictionary (PSPPIRE_VAR_SHEET (de->var_sheet),
-                                        de->dict);
+      g_object_set (de->data_sheet, "hmodel", de->dict, NULL);
+      g_object_set (de->var_sheet, "data-model", de->dict, NULL);
       break;
     case PROP_VALUE_LABELS:
-      FOR_EACH_DATA_SHEET (data_sheet, i, de)
-        psppire_data_sheet_set_value_labels (data_sheet,
-                                          g_value_get_boolean (value));
       break;
-    case PROP_UI_MANAGER:
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -209,12 +220,6 @@ psppire_data_editor_get_property (GObject         *object,
       g_value_set_pointer (value, de->dict);
       break;
     case PROP_VALUE_LABELS:
-      g_value_set_boolean (value,
-                           psppire_data_sheet_get_value_labels (
-                             PSPPIRE_DATA_SHEET (de->data_sheets[0])));
-      break;
-    case PROP_UI_MANAGER:
-      g_value_set_object (value, psppire_data_editor_get_ui_manager (de));
       break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -224,11 +229,11 @@ psppire_data_editor_get_property (GObject         *object,
 
 static void
 psppire_data_editor_switch_page (GtkNotebook     *notebook,
-                                 GtkNotebookPage *page,
+                                GtkWidget *w,
                                  guint            page_num)
 {
-  GTK_NOTEBOOK_CLASS (parent_class)->switch_page (notebook, page, page_num);
-  psppire_data_editor_update_ui_manager (PSPPIRE_DATA_EDITOR (notebook));
+  GTK_NOTEBOOK_CLASS (parent_class)->switch_page (notebook, w, page_num);
+
 }
 
 static void
@@ -236,7 +241,7 @@ psppire_data_editor_set_focus_child (GtkContainer *container,
                                      GtkWidget    *widget)
 {
   GTK_CONTAINER_CLASS (parent_class)->set_focus_child (container, widget);
-  psppire_data_editor_update_ui_manager (PSPPIRE_DATA_EDITOR (container));
+
 }
 
 static void
@@ -246,7 +251,7 @@ psppire_data_editor_class_init (PsppireDataEditorClass *klass)
   GParamSpec *dict_spec ;
   GParamSpec *value_labels_spec;
   GParamSpec *split_window_spec;
-  GParamSpec *ui_manager_spec;
+
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
   GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
   GtkNotebookClass *notebook_class = GTK_NOTEBOOK_CLASS (klass);
@@ -303,446 +308,470 @@ psppire_data_editor_class_init (PsppireDataEditorClass *klass)
   g_object_class_install_property (object_class,
                                    PROP_SPLIT_WINDOW,
                                    split_window_spec);
-
-  ui_manager_spec =
-    g_param_spec_object ("ui-manager",
-                         "UI Manager",
-                         "UI manager for the active notebook tab.  The client should merge this UI manager with the active UI manager to obtain menu items and tool bar items specific to the active notebook tab.",
-                         GTK_TYPE_UI_MANAGER,
-                         G_PARAM_READABLE);
-  g_object_class_install_property (object_class,
-                                   PROP_UI_MANAGER,
-                                   ui_manager_spec);
 }
 
-static gboolean
-on_data_sheet_var_double_clicked (PsppireDataSheet *data_sheet,
-                                  gint dict_index,
-                                  PsppireDataEditor *de)
+
+static void
+on_var_sheet_var_double_clicked (void *var_sheet, gint dict_index,
+                                 PsppireDataEditor *de)
 {
   gtk_notebook_set_current_page (GTK_NOTEBOOK (de),
-                                 PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
-
-  psppire_var_sheet_goto_variable (PSPPIRE_VAR_SHEET (de->var_sheet),
-                                   dict_index);
+                                 PSPPIRE_DATA_EDITOR_DATA_VIEW);
 
-  return TRUE;
+  jmd_sheet_scroll_to (JMD_SHEET (de->data_sheet), dict_index, -1);
 }
 
-static gboolean
-on_var_sheet_var_double_clicked (PsppireVarSheet *var_sheet, gint dict_index,
+
+static void
+on_data_sheet_var_double_clicked (JmdSheet *data_sheet, gint dict_index,
                                  PsppireDataEditor *de)
 {
-  PsppireDataSheet *data_sheet;
 
   gtk_notebook_set_current_page (GTK_NOTEBOOK (de),
-                                 PSPPIRE_DATA_EDITOR_DATA_VIEW);
-
-  data_sheet = psppire_data_editor_get_active_data_sheet (de);
-  psppire_data_sheet_show_variable (data_sheet, dict_index);
+                                 PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
 
-  return TRUE;
+  jmd_sheet_scroll_to (JMD_SHEET (de->var_sheet), -1, dict_index);
 }
 
+
+
 /* Refreshes 'de->cell_ref_label' and 'de->datum_entry' from the currently
    active cell or cells. */
 static void
 refresh_entry (PsppireDataEditor *de)
 {
-  PsppireDataSheet *data_sheet = psppire_data_editor_get_active_data_sheet (de);
-  PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
-  PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
+  gint row, col;
+  if (jmd_sheet_get_active_cell (JMD_SHEET (de->data_sheet), &col, &row))
+    {
+      union value val;
+      const struct variable *var = psppire_dict_get_variable (de->dict, col);
+      if (var == NULL)
+       return;
 
-  gchar *ref_cell_text;
-  GList *selected_columns, *iter;
-  struct variable *var;
-  gint n_cases;
-  gint n_vars;
+      psppire_value_entry_set_variable (PSPPIRE_VALUE_ENTRY (de->datum_entry), var);
 
-  selected_columns = pspp_sheet_selection_get_selected_columns (selection);
-  n_vars = 0;
-  var = NULL;
-  for (iter = selected_columns; iter != NULL; iter = iter->next)
-    {
-      PsppSheetViewColumn *column = iter->data;
-      struct variable *v = g_object_get_data (G_OBJECT (column), "variable");
-      if (v != NULL)
-        {
-          var = v;
-          n_vars++;
-        }
-    }
-  g_list_free (selected_columns);
+      int width = var_get_width (var);
+      if (! psppire_data_store_get_value (PSPPIRE_DATA_STORE (de->data_store),
+                                         row, var, &val))
+       return;
 
-  n_cases = pspp_sheet_selection_count_selected_rows (selection);
-  if (n_cases > 0)
-    {
-      /* The final row is selectable but it isn't a case (it's just used to add
-         more cases), so don't count it. */
-      GtkTreePath *path;
-      gint case_count;
-
-      case_count = psppire_data_store_get_case_count (de->data_store);
-      path = gtk_tree_path_new_from_indices (case_count, -1);
-      if (pspp_sheet_selection_path_is_selected (selection, path))
-        n_cases--;
-      gtk_tree_path_free (path);
+      psppire_value_entry_set_value (PSPPIRE_VALUE_ENTRY (de->datum_entry),
+                                    &val, width);
+      value_destroy (&val, width);
     }
+}
 
-  ref_cell_text = NULL;
-  if (n_cases == 1 && n_vars == 1)
-    {
-      PsppireValueEntry *value_entry = PSPPIRE_VALUE_ENTRY (de->datum_entry);
-      struct range_set *selected_rows;
-      gboolean show_value_labels;
-      union value value;
-      int width;
-      gint row;
-
-      selected_rows = pspp_sheet_selection_get_range_set (selection);
-      row = range_set_scan (selected_rows, 0);
-      range_set_destroy (selected_rows);
+static void
+on_datum_entry_activate (PsppireValueEntry *entry, PsppireDataEditor *de)
+{
+}
 
-      ref_cell_text = g_strdup_printf ("%d : %s", row + 1, var_get_name (var));
 
-      show_value_labels = psppire_data_sheet_get_value_labels (data_sheet);
+/* Called when the active cell or the selection in the data sheet changes */
+static void
+on_data_selection_change (PsppireDataEditor *de, JmdRange *sel)
+{
+  gchar *ref_cell_text = NULL;
 
-      psppire_value_entry_set_variable (value_entry, var);
-      psppire_value_entry_set_show_value_label (value_entry,
-                                                show_value_labels);
+  gint n_cases = abs (sel->end_y - sel->start_y) + 1;
+  gint n_vars = abs (sel->end_x - sel->start_x) + 1;
 
-      width = var_get_width (var);
-      value_init (&value, width);
-      datasheet_get_value (de->data_store->datasheet,
-                           row, var_get_case_index (var), &value);
-      psppire_value_entry_set_value (value_entry, &value, width);
-      value_destroy (&value, width);
+  if (n_cases == 1 && n_vars == 1)
+    {
+      /* A single cell is selected */
+      const struct variable *var = psppire_dict_get_variable (de->dict, sel->start_x);
 
-      gtk_widget_set_sensitive (de->datum_entry, TRUE);
+      if (var)
+       ref_cell_text = g_strdup_printf (_("%d : %s"),
+                                        sel->start_y + 1, var_get_name (var));
     }
   else
     {
-      if (n_cases == 0 || n_vars == 0)
-        {
-          ref_cell_text = NULL;
-        }
-      else
-        {
-          struct string s;
-
-          /* The glib string library does not understand the ' printf modifier
-             on all platforms, but the "struct string" library does (because
-             Gnulib fixes that problem), so use the latter.  */
-          ds_init_empty (&s);
-          ds_put_format (&s, ngettext ("%'d case", "%'d cases", n_cases),
-                         n_cases);
-          ds_put_byte (&s, ' ');
-          ds_put_unichar (&s, 0xd7); /* U+00D7 MULTIPLICATION SIGN */
-          ds_put_byte (&s, ' ');
-          ds_put_format (&s, ngettext ("%'d variable", "%'d variables",
-                                       n_vars),
-                         n_vars);
-          ref_cell_text = ds_steal_cstr (&s);
-        }
-
-      psppire_value_entry_set_variable (PSPPIRE_VALUE_ENTRY (de->datum_entry),
-                                        NULL);
-      gtk_entry_set_text (
-        GTK_ENTRY (gtk_bin_get_child (GTK_BIN (de->datum_entry))), "");
-      gtk_widget_set_sensitive (de->datum_entry, FALSE);
+      struct string s;
+
+      /* The glib string library does not understand the ' printf modifier
+        on all platforms, but the "struct string" library does (because
+        Gnulib fixes that problem), so use the latter.  */
+      ds_init_empty (&s);
+      ds_put_format (&s, ngettext ("%'d case", "%'d cases", n_cases),
+                    n_cases);
+      ds_put_byte (&s, ' ');
+      ds_put_unichar (&s, 0xd7); /* U+00D7 MULTIPLICATION SIGN */
+      ds_put_byte (&s, ' ');
+      ds_put_format (&s, ngettext ("%'d variable", "%'d variables",
+                                  n_vars),
+                    n_vars);
+      ref_cell_text = ds_steal_cstr (&s);
     }
 
   gtk_label_set_label (GTK_LABEL (de->cell_ref_label),
-                       ref_cell_text ? ref_cell_text : "");
+                      ref_cell_text ? ref_cell_text : "");
+
   g_free (ref_cell_text);
 }
 
+
+static void set_font_recursively (GtkWidget *w, gpointer data);
+
+gboolean myreversefunc (GtkTreeModel *model, gint col, gint row, const gchar *in,
+                   GValue *out);
+
+
+enum sort_order
+  {
+    SORT_ASCEND,
+    SORT_DESCEND
+  };
+
 static void
-on_datum_entry_activate (PsppireValueEntry *entry, PsppireDataEditor *de)
+do_sort (PsppireDataEditor *de, enum sort_order order)
 {
-  PsppireDataSheet *data_sheet = psppire_data_editor_get_active_data_sheet (de);
-  struct variable *var;
-  union value value;
-  int width;
-  gint row;
+  JmdRange *range = JMD_SHEET(de->data_sheet)->selection;
 
-  row = psppire_data_sheet_get_current_case (data_sheet);
-  var = psppire_data_sheet_get_current_variable (data_sheet);
-  if (row < 0 || !var)
-    return;
+  int n_vars = 0;
+  int i;
+
+  PsppireDataWindow *pdw =
+     psppire_data_window_for_data_store (de->data_store);
 
-  width = var_get_width (var);
-  value_init (&value, width);
-  if (psppire_value_entry_get_value (PSPPIRE_VALUE_ENTRY (de->datum_entry),
-                                     &value, width))
-    psppire_data_store_set_value (de->data_store, row, var, &value);
-  value_destroy (&value, width);
+  GString *syntax = g_string_new ("SORT CASES BY");
+  for (i = range->start_x ; i <= range->end_x; ++i)
+    {
+      const struct variable *var = psppire_dict_get_variable (de->dict, i);
+      if (var != NULL)
+        {
+          g_string_append_printf (syntax, " %s", var_get_name (var));
+          n_vars++;
+        }
+    }
+  if (n_vars > 0)
+    {
+      if (order == SORT_DESCEND)
+        g_string_append (syntax, " (DOWN)");
+      g_string_append_c (syntax, '.');
+      execute_const_syntax_string (pdw, syntax->str);
+    }
+  g_string_free (syntax, TRUE);
 }
 
+
 static void
-on_data_sheet_selection_changed (PsppSheetSelection *selection,
-                                 PsppireDataEditor *de)
+sort_ascending (PsppireDataEditor *de)
 {
-  /* In a split view, ensure that only a single data sheet has a nonempty
-     selection.  */
-  if (de->split
-      && pspp_sheet_selection_count_selected_rows (selection)
-      && pspp_sheet_selection_count_selected_columns (selection))
-    {
-      PsppireDataSheet *ds;
-      int i;
+  do_sort (de, SORT_ASCEND);
 
-      FOR_EACH_DATA_SHEET (ds, i, de)
-        {
-          PsppSheetSelection *s;
+  gtk_widget_queue_draw (GTK_WIDGET (de));
+}
 
-          s = pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (ds));
-          if (s != selection)
-            pspp_sheet_selection_unselect_all (s);
-        }
-    }
+static void
+sort_descending (PsppireDataEditor *de)
+{
+  do_sort (de, SORT_DESCEND);
 
-  refresh_entry (de);
+  gtk_widget_queue_draw (GTK_WIDGET (de));
 }
 
-/* Ensures that rows in the right-hand panes in the split view have the same
-   row height as the left-hand panes.  Otherwise, the rows in the right-hand
-   pane tend to be smaller, because the right-hand pane doesn't have buttons
-   for case numbers. */
 static void
-on_data_sheet_fixed_height_notify (PsppireDataSheet *ds,
-                                   GParamSpec *pspec,
-                                   PsppireDataEditor *de)
+delete_cases (PsppireDataEditor *de)
 {
-  enum
-    {
-      TL = GTK_XPANED_TOP_LEFT,
-      TR = GTK_XPANED_TOP_RIGHT,
-      BL = GTK_XPANED_BOTTOM_LEFT,
-      BR = GTK_XPANED_BOTTOM_RIGHT
-    };
+  JmdRange *range = JMD_SHEET(de->data_sheet)->selection;
 
-  int fixed_height = pspp_sheet_view_get_fixed_height (PSPP_SHEET_VIEW (ds));
+  psppire_data_store_delete_cases (de->data_store, range->start_y,
+                                  range->end_y - range->start_y + 1);
 
-  pspp_sheet_view_set_fixed_height (PSPP_SHEET_VIEW (de->data_sheets[TR]),
-                                    fixed_height);
-  pspp_sheet_view_set_fixed_height (PSPP_SHEET_VIEW (de->data_sheets[BR]),
-                                    fixed_height);
+  gtk_widget_queue_draw (GTK_WIDGET (de));
 }
 
 static void
-disconnect_data_sheets (PsppireDataEditor *de)
+insert_new_case (PsppireDataEditor *de)
 {
-  PsppireDataSheet *ds;
-  int i;
+  gint posn = GPOINTER_TO_INT (g_object_get_data
+                               (G_OBJECT (de->data_sheet_cases_row_popup), "item"));
 
-  FOR_EACH_DATA_SHEET (ds, i, de)
-    {
-      PsppSheetSelection *selection;
+  psppire_data_editor_insert_new_case_at_posn (de, posn);
+}
 
-      if (ds == NULL)
-        {
-          /* This can only happen if 'dispose' runs more than once. */
-          continue;
-        }
+void
+psppire_data_editor_data_delete_variables (PsppireDataEditor *de)
+{
+  JmdRange *range = JMD_SHEET(de->data_sheet)->selection;
 
-      if (i == GTK_XPANED_TOP_LEFT)
-        g_signal_handlers_disconnect_by_func (
-          ds, G_CALLBACK (on_data_sheet_fixed_height_notify), de);
+  psppire_dict_delete_variables (de->dict, range->start_x,
+                                (range->end_x - range->start_x + 1));
 
-      g_signal_handlers_disconnect_by_func (
-        ds, G_CALLBACK (refresh_entry), de);
-      g_signal_handlers_disconnect_by_func (
-        ds, G_CALLBACK (on_data_sheet_var_double_clicked), de);
+  gtk_widget_queue_draw (GTK_WIDGET (de->data_sheet));
+}
 
-      selection = pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (ds));
-      g_signal_handlers_disconnect_by_func (
-        selection, G_CALLBACK (on_data_sheet_selection_changed), de);
+void
+psppire_data_editor_var_delete_variables (PsppireDataEditor *de)
+{
+  JmdRange *range = JMD_SHEET(de->var_sheet)->selection;
 
-      de->data_sheets[i] = NULL;
-    }
+  psppire_dict_delete_variables (de->dict, range->start_y,
+                                (range->end_y - range->start_y + 1));
+
+  gtk_widget_queue_draw (GTK_WIDGET (de->var_sheet));
+}
+
+void
+psppire_data_editor_insert_new_case_at_posn  (PsppireDataEditor *de, gint posn)
+{
+  psppire_data_store_insert_new_case (de->data_store, posn);
+
+  gtk_widget_queue_draw (GTK_WIDGET (de->data_sheet));
+}
+
+void
+psppire_data_editor_insert_new_variable_at_posn (PsppireDataEditor *de, gint posn)
+{
+  const struct variable *v = psppire_dict_insert_variable (de->dict, posn, NULL);
+  psppire_data_store_insert_value (de->data_store, var_get_width(v),
+                                  var_get_case_index (v));
+
+  gtk_widget_queue_draw (GTK_WIDGET (de));
 }
 
 static GtkWidget *
-make_data_sheet (PsppireDataEditor *de, GtkTreeViewGridLines grid_lines)
+create_data_row_header_popup_menu (PsppireDataEditor *de)
 {
-  PsppSheetSelection *selection;
-  GtkWidget *ds;
+  GtkWidget *menu = gtk_menu_new ();
 
-  ds = psppire_data_sheet_new ();
-  pspp_sheet_view_set_grid_lines (PSPP_SHEET_VIEW (ds), grid_lines);
+  GtkWidget *item =
+    gtk_menu_item_new_with_mnemonic  (_("_Insert Case"));
 
-  g_signal_connect_swapped (ds, "notify::value-labels",
-                            G_CALLBACK (refresh_entry), de);
-  g_signal_connect (ds, "var-double-clicked",
-                    G_CALLBACK (on_data_sheet_var_double_clicked), de);
+  g_signal_connect_swapped (item, "activate", G_CALLBACK (insert_new_case), de);
+  gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+
+  item = gtk_separator_menu_item_new ();
+  gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+
+  de->data_clear_cases_menu_item = gtk_menu_item_new_with_mnemonic (_("Cl_ear Cases"));
+  gtk_widget_set_sensitive (de->data_clear_cases_menu_item, FALSE);
+  gtk_menu_shell_append (GTK_MENU_SHELL (menu), de->data_clear_cases_menu_item);
+  g_signal_connect_swapped (de->data_clear_cases_menu_item, "activate",
+                           G_CALLBACK (delete_cases), de);
 
-  selection = pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (ds));
-  g_signal_connect (selection, "changed",
-                    G_CALLBACK (on_data_sheet_selection_changed), de);
+  gtk_widget_show_all (menu);
+  return menu;
+}
+
+static void
+insert_new_variable_data (PsppireDataEditor *de)
+{
+  gint posn = GPOINTER_TO_INT (g_object_get_data
+                               (G_OBJECT (de->data_sheet_cases_column_popup),
+                                "item"));
 
-  return ds;
+  psppire_data_editor_insert_new_variable_at_posn (de, posn);
 }
 
 static GtkWidget *
-make_single_datasheet (PsppireDataEditor *de, GtkTreeViewGridLines grid_lines)
+create_data_column_header_popup_menu (PsppireDataEditor *de)
 {
-  GtkWidget *data_sheet_scroller;
+  GtkWidget *menu = gtk_menu_new ();
+
+  GtkWidget *item =
+    gtk_menu_item_new_with_mnemonic  (_("_Insert Variable"));
+  g_signal_connect_swapped (item, "activate", G_CALLBACK (insert_new_variable_data),
+                           de);
+  gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+
+  item = gtk_separator_menu_item_new ();
+  gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+
+  de->data_clear_variables_menu_item =
+    gtk_menu_item_new_with_mnemonic  (_("Cl_ear Variables"));
+  g_signal_connect_swapped (de->data_clear_variables_menu_item, "activate",
+                           G_CALLBACK (psppire_data_editor_data_delete_variables),
+                           de);
+  gtk_widget_set_sensitive (de->data_clear_variables_menu_item, FALSE);
+  gtk_menu_shell_append (GTK_MENU_SHELL (menu), de->data_clear_variables_menu_item);
+
+  item = gtk_separator_menu_item_new ();
+  gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+
+  de->data_sort_ascending_menu_item =
+    gtk_menu_item_new_with_mnemonic (_("Sort _Ascending"));
+  g_signal_connect_swapped (de->data_sort_ascending_menu_item, "activate",
+                           G_CALLBACK (sort_ascending), de);
+  gtk_widget_set_sensitive (de->data_sort_ascending_menu_item, FALSE);
+  gtk_menu_shell_append (GTK_MENU_SHELL (menu), de->data_sort_ascending_menu_item);
+
+  de->data_sort_descending_menu_item =
+    gtk_menu_item_new_with_mnemonic (_("Sort _Descending"));
+  g_signal_connect_swapped (de->data_sort_descending_menu_item, "activate",
+                           G_CALLBACK (sort_descending), de);
+  gtk_widget_set_sensitive (de->data_sort_descending_menu_item, FALSE);
+  gtk_menu_shell_append (GTK_MENU_SHELL (menu), de->data_sort_descending_menu_item);
+
+  gtk_widget_show_all (menu);
+  return menu;
+}
 
-  de->data_sheets[0] = make_data_sheet (de, grid_lines);
-  de->data_sheets[1] = de->data_sheets[2] = de->data_sheets[3] = NULL;
+static void
+set_menu_items_sensitivity (JmdSheet *sheet, gpointer selection, gpointer p)
+{
+  JmdRange *range = selection;
+  PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (p);
+  gint width = gtk_tree_model_get_n_columns (sheet->data_model);
+  gint length = psppire_data_store_get_case_count (de->data_store);
 
-  /* Put data sheet in scroller. */
-  data_sheet_scroller = gtk_scrolled_window_new (NULL, NULL);
-  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (data_sheet_scroller),
-                                  GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
-  gtk_container_add (GTK_CONTAINER (data_sheet_scroller), de->data_sheets[0]);
 
-  return data_sheet_scroller;
+  gboolean whole_row_selected = (range->start_x == 0 && range->end_x == width - 1);
+  gtk_widget_set_sensitive (de->data_clear_cases_menu_item, whole_row_selected);
+
+
+  gboolean whole_column_selected =
+    (range->start_y == 0 && range->end_y == length - 1);
+  gtk_widget_set_sensitive (de->data_clear_variables_menu_item,
+                           whole_column_selected);
+  gtk_widget_set_sensitive (de->data_sort_ascending_menu_item,
+                           whole_column_selected);
+  gtk_widget_set_sensitive (de->data_sort_descending_menu_item,
+                           whole_column_selected);
 }
 
-static GtkWidget *
-make_split_datasheet (PsppireDataEditor *de, GtkTreeViewGridLines grid_lines)
+static void
+show_cases_row_popup (JmdSheet *sheet, int row, uint button, uint state, gpointer p)
 {
-  /* Panes, in the order in which we want to create them. */
-  enum
-    {
-      TL,                       /* top left */
-      TR,                       /* top right */
-      BL,                       /* bottom left */
-      BR                        /* bottom right */
-    };
+  PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (p);
+  GListModel *vmodel = NULL;
+  g_object_get (sheet, "vmodel", &vmodel, NULL);
+  if (vmodel == NULL)
+    return;
 
-  PsppSheetView *ds[4];
-  GtkXPaned *xpaned;
-  int i;
+  guint n_items = g_list_model_get_n_items (vmodel);
 
-  xpaned = GTK_XPANED (gtk_xpaned_new ());
+  if (row >= n_items)
+    return;
 
-  for (i = 0; i < 4; i++)
-    {
-      GtkAdjustment *hadjust, *vadjust;
-      GtkPolicyType hpolicy, vpolicy;
-      GtkWidget *scroller;
-
-      de->data_sheets[i] = make_data_sheet (de, grid_lines);
-      ds[i] = PSPP_SHEET_VIEW (de->data_sheets[i]);
-
-      if (i == BL)
-        hadjust = pspp_sheet_view_get_hadjustment (ds[TL]);
-      else if (i == BR)
-        hadjust = pspp_sheet_view_get_hadjustment (ds[TR]);
-      else
-        hadjust = NULL;
-
-      if (i == TR)
-        vadjust = pspp_sheet_view_get_vadjustment (ds[TL]);
-      else if (i == BR)
-        vadjust = pspp_sheet_view_get_vadjustment (ds[BL]);
-      else
-        vadjust = NULL;
-
-      scroller = gtk_scrolled_window_new (hadjust, vadjust);
-      gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroller),
-                                           GTK_SHADOW_ETCHED_IN);
-      hpolicy = i == TL || i == TR ? GTK_POLICY_NEVER : GTK_POLICY_ALWAYS;
-      vpolicy = i == TL || i == BL ? GTK_POLICY_NEVER : GTK_POLICY_ALWAYS;
-      gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroller),
-                                      hpolicy, vpolicy);
-      gtk_container_add (GTK_CONTAINER (scroller), GTK_WIDGET (ds[i]));
-
-      switch (i)
-        {
-        case TL:
-          gtk_xpaned_pack_top_left (xpaned, scroller, TRUE, TRUE);
-          break;
+  if (button != 3)
+    return;
 
-        case TR:
-          gtk_xpaned_pack_top_right (xpaned, scroller, TRUE, TRUE);
-          break;
+  g_object_set_data (G_OBJECT (de->data_sheet_cases_row_popup), "item",
+                    GINT_TO_POINTER (row));
 
-        case BL:
-          gtk_xpaned_pack_bottom_left (xpaned, scroller, TRUE, TRUE);
-          break;
+  gtk_menu_popup_at_pointer (GTK_MENU (de->data_sheet_cases_row_popup), NULL);
+}
 
-        case BR:
-          gtk_xpaned_pack_bottom_right (xpaned, scroller, TRUE, TRUE);
-          break;
+static void
+show_cases_column_popup (JmdSheet *sheet, int column, uint button, uint state,
+                        gpointer p)
+{
+  PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (p);
+  GListModel *hmodel = NULL;
+  g_object_get (sheet, "hmodel", &hmodel, NULL);
+  if (hmodel == NULL)
+    return;
 
-        default:
-          g_warn_if_reached ();
-        }
-    }
+  guint n_items = g_list_model_get_n_items (hmodel);
 
-  /* Bottom sheets don't display variable names. */
-  pspp_sheet_view_set_headers_visible (ds[BL], FALSE);
-  pspp_sheet_view_set_headers_visible (ds[BR], FALSE);
+  if (column >= n_items)
+    return;
 
-  /* Right sheets don't display case numbers. */
-  psppire_data_sheet_set_case_numbers (PSPPIRE_DATA_SHEET (ds[TR]), FALSE);
-  psppire_data_sheet_set_case_numbers (PSPPIRE_DATA_SHEET (ds[BR]), FALSE);
+  if (button != 3)
+    return;
 
-  g_signal_connect (ds[TL], "notify::fixed-height",
-                    G_CALLBACK (on_data_sheet_fixed_height_notify), de);
+  g_object_set_data (G_OBJECT (de->data_sheet_cases_column_popup), "item",
+                    GINT_TO_POINTER (column));
 
-  return GTK_WIDGET (xpaned);
+  gtk_menu_popup_at_pointer (GTK_MENU (de->data_sheet_cases_column_popup), NULL);
+}
+
+
+static gchar *
+data_store_value_to_string (JmdSheet *data_sheet, PsppireDataStore *store, gint col, gint row, const GValue *v)
+{
+  return psppire_data_store_value_to_string (store, col, row, v);
 }
 
 static void
 psppire_data_editor_init (PsppireDataEditor *de)
 {
-  GtkWidget *var_sheet_scroller;
   GtkWidget *hbox;
+  gchar *fontname = NULL;
+
+  GtkStyleContext *context = gtk_widget_get_style_context (GTK_WIDGET (de));
+  gtk_style_context_add_class (context, "psppire-data-editor");
 
   de->font = NULL;
-  de->ui_manager = NULL;
-  de->old_vbox_widget = NULL;
 
   g_object_set (de, "tab-pos", GTK_POS_BOTTOM, NULL);
 
   de->cell_ref_label = gtk_label_new ("");
   gtk_label_set_width_chars (GTK_LABEL (de->cell_ref_label), 25);
-  gtk_misc_set_alignment (GTK_MISC (de->cell_ref_label), 0.0, 0.5);
+  gtk_widget_set_valign (de->cell_ref_label, GTK_ALIGN_CENTER);
 
   de->datum_entry = psppire_value_entry_new ();
   g_signal_connect (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (de->datum_entry))),
                     "activate", G_CALLBACK (on_datum_entry_activate), de);
 
-  hbox = gtk_hbox_new (FALSE, 0);
+  hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
   gtk_box_pack_start (GTK_BOX (hbox), de->cell_ref_label, FALSE, FALSE, 0);
   gtk_box_pack_start (GTK_BOX (hbox), de->datum_entry, TRUE, TRUE, 0);
 
   de->split = FALSE;
-  de->datasheet_vbox_widget
-    = make_single_datasheet (de, GTK_TREE_VIEW_GRID_LINES_BOTH);
+  de->data_sheet = jmd_sheet_new ();
+
+  de->data_sheet_cases_column_popup = create_data_column_header_popup_menu (de);
+  de->data_sheet_cases_row_popup = create_data_row_header_popup_menu (de);
+
+  g_signal_connect (de->data_sheet, "row-header-pressed",
+                   G_CALLBACK (show_cases_row_popup), de);
+
+  g_signal_connect (de->data_sheet, "column-header-pressed",
+                   G_CALLBACK (show_cases_column_popup), de);
 
-  de->vbox = gtk_vbox_new (FALSE, 0);
+  g_signal_connect (de->data_sheet, "selection-changed",
+                   G_CALLBACK (set_menu_items_sensitivity), de);
+
+  g_object_set (de->data_sheet,
+               "forward-conversion", data_store_value_to_string,
+               "reverse-conversion", myreversefunc,
+               NULL);
+
+  GtkWidget *data_button = jmd_sheet_get_button (JMD_SHEET (de->data_sheet));
+  gtk_button_set_label (GTK_BUTTON (data_button), _("Case"));
+  de->vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
   gtk_box_pack_start (GTK_BOX (de->vbox), hbox, FALSE, FALSE, 0);
-  gtk_box_pack_start (GTK_BOX (de->vbox), de->datasheet_vbox_widget,
-                      TRUE, TRUE, 0);
+  gtk_box_pack_start (GTK_BOX (de->vbox), de->data_sheet, TRUE, TRUE, 0);
+
+
+  g_signal_connect_swapped (de->data_sheet, "selection-changed",
+                   G_CALLBACK (on_data_selection_change), de);
 
   gtk_notebook_append_page (GTK_NOTEBOOK (de), de->vbox,
                            gtk_label_new_with_mnemonic (_("Data View")));
 
   gtk_widget_show_all (de->vbox);
 
-  de->var_sheet = GTK_WIDGET (psppire_var_sheet_new ());
-  pspp_sheet_view_set_grid_lines (PSPP_SHEET_VIEW (de->var_sheet),
-                                  GTK_TREE_VIEW_GRID_LINES_BOTH);
-  var_sheet_scroller = gtk_scrolled_window_new (NULL, NULL);
-  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (var_sheet_scroller),
-                                  GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
-  gtk_container_add (GTK_CONTAINER (var_sheet_scroller), de->var_sheet);
-  gtk_widget_show_all (var_sheet_scroller);
-  gtk_notebook_append_page (GTK_NOTEBOOK (de), var_sheet_scroller,
+  de->var_sheet = psppire_variable_sheet_new ();
+
+  GtkWidget *var_button = jmd_sheet_get_button (JMD_SHEET (de->var_sheet));
+  gtk_button_set_label (GTK_BUTTON (var_button), _("Variable"));
+
+  gtk_notebook_append_page (GTK_NOTEBOOK (de), de->var_sheet,
                            gtk_label_new_with_mnemonic (_("Variable View")));
 
-  g_signal_connect (de->var_sheet, "var-double-clicked",
+  gtk_widget_show_all (de->var_sheet);
+
+  g_signal_connect (de->var_sheet, "row-header-double-clicked",
                     G_CALLBACK (on_var_sheet_var_double_clicked), de);
 
+  g_signal_connect (de->data_sheet, "column-header-double-clicked",
+                    G_CALLBACK (on_data_sheet_var_double_clicked), de);
+
   g_object_set (de, "can-focus", FALSE, NULL);
 
-  psppire_data_editor_update_ui_manager (de);
+  if (psppire_conf_get_string (psppire_conf_new (),
+                          "Data Editor", "font",
+                               &fontname) )
+    {
+      de->font = pango_font_description_from_string (fontname);
+      g_free (fontname);
+      set_font_recursively (GTK_WIDGET (de), de->font);
+    }
+
 }
 
 GtkWidget*
@@ -760,17 +789,8 @@ psppire_data_editor_new (PsppireDict *dict,
 void
 psppire_data_editor_show_grid (PsppireDataEditor *de, gboolean grid_visible)
 {
-  GtkTreeViewGridLines grid;
-  PsppireDataSheet *data_sheet;
-  int i;
-
-  grid = (grid_visible
-          ? GTK_TREE_VIEW_GRID_LINES_BOTH
-          : GTK_TREE_VIEW_GRID_LINES_NONE);
-
-  FOR_EACH_DATA_SHEET (data_sheet, i, de)
-    pspp_sheet_view_set_grid_lines (PSPP_SHEET_VIEW (data_sheet), grid);
-  pspp_sheet_view_set_grid_lines (PSPP_SHEET_VIEW (de->var_sheet), grid);
+  g_object_set (JMD_SHEET (de->var_sheet), "gridlines", grid_visible, NULL);
+  g_object_set (JMD_SHEET (de->data_sheet), "gridlines", grid_visible, NULL);
 }
 
 
@@ -778,12 +798,29 @@ static void
 set_font_recursively (GtkWidget *w, gpointer data)
 {
   PangoFontDescription *font_desc = data;
-  GtkRcStyle *style = gtk_widget_get_modifier_style (w);
 
-  pango_font_description_free (style->font_desc);
-  style->font_desc = pango_font_description_copy (font_desc);
+  GtkStyleContext *style = gtk_widget_get_style_context (w);
+  GtkCssProvider *cssp = gtk_css_provider_new ();
+
+  gchar *str = pango_font_description_to_string (font_desc);
+  gchar *css =
+    g_strdup_printf ("* {font: %s}", str);
+  g_free (str);
+
+  GError *err = NULL;
+  gtk_css_provider_load_from_data (cssp, css, -1, &err);
+  if (err)
+    {
+      g_warning ("Failed to load font css \"%s\": %s", css, err->message);
+      g_error_free (err);
+    }
+  g_free (css);
+
+  gtk_style_context_add_provider (style,
+                                 GTK_STYLE_PROVIDER (cssp),
+                                 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+  g_object_unref (cssp);
 
-  gtk_widget_modify_style (w, style);
 
   if ( GTK_IS_CONTAINER (w))
     gtk_container_foreach (GTK_CONTAINER (w), set_font_recursively, font_desc);
@@ -793,11 +830,18 @@ set_font_recursively (GtkWidget *w, gpointer data)
 void
 psppire_data_editor_set_font (PsppireDataEditor *de, PangoFontDescription *font_desc)
 {
+  gchar *font_name;
   set_font_recursively (GTK_WIDGET (de), font_desc);
 
   if (de->font)
     pango_font_description_free (de->font);
   de->font = pango_font_description_copy (font_desc);
+  font_name = pango_font_description_to_string (de->font);
+
+  psppire_conf_set_string (psppire_conf_new (),
+                          "Data Editor", "font",
+                          font_name);
+  g_free (font_name);
 }
 
 /* If SPLIT is TRUE, splits DE's data sheet into four panes.
@@ -805,42 +849,7 @@ psppire_data_editor_set_font (PsppireDataEditor *de, PangoFontDescription *font_
 void
 psppire_data_editor_split_window (PsppireDataEditor *de, gboolean split)
 {
-  GtkTreeViewGridLines grid_lines;
-
-  if (split == de->split)
-    return;
-
-
-  grid_lines = pspp_sheet_view_get_grid_lines (
-    PSPP_SHEET_VIEW (de->data_sheets[0]));
-
-  disconnect_data_sheets (de);
-  if (de->old_vbox_widget)
-    g_object_unref (de->old_vbox_widget);
-  de->old_vbox_widget = de->datasheet_vbox_widget;
-  g_object_ref (de->old_vbox_widget);
-  /* FIXME:  old_vbox_widget needs to be unreffed in dispose.
-       (currently it seems to provoke an error if I do that.  
-       I don't know why. */
-  gtk_container_remove (GTK_CONTAINER (de->vbox), de->datasheet_vbox_widget);
-
-  if (split)
-    de->datasheet_vbox_widget = make_split_datasheet (de, grid_lines);
-  else
-    de->datasheet_vbox_widget = make_single_datasheet (de, grid_lines);
-
-  psppire_data_editor_refresh_model (de);
-
-  gtk_box_pack_start (GTK_BOX (de->vbox), de->datasheet_vbox_widget,
-                      TRUE, TRUE, 0);
-  gtk_widget_show_all (de->vbox);
-
-  if (de->font)
-    set_font_recursively (GTK_WIDGET (de), de->font);
-
-  de->split = split;
-  g_object_notify (G_OBJECT (de), "split");
-  psppire_data_editor_update_ui_manager (de);
+  g_object_set (de, "split", split, NULL);
 }
 
 /* Makes the variable with dictionary index DICT_INDEX in DE's dictionary
@@ -848,109 +857,43 @@ psppire_data_editor_split_window (PsppireDataEditor *de, gboolean split)
 void
 psppire_data_editor_goto_variable (PsppireDataEditor *de, gint dict_index)
 {
-  PsppireDataSheet *data_sheet;
+  gint page = gtk_notebook_get_current_page (GTK_NOTEBOOK (de));
 
-  switch (gtk_notebook_get_current_page (GTK_NOTEBOOK (de)))
+  switch (page)
     {
-    case PSPPIRE_DATA_EDITOR_DATA_VIEW:
-      data_sheet = psppire_data_editor_get_active_data_sheet (de);
-      psppire_data_sheet_show_variable (data_sheet, dict_index);
-      break;
-
-    case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
-      psppire_var_sheet_goto_variable (PSPPIRE_VAR_SHEET (de->var_sheet),
-                                       dict_index);
-      break;
+      case PSPPIRE_DATA_EDITOR_DATA_VIEW:
+       jmd_sheet_scroll_to (JMD_SHEET (de->data_sheet), dict_index, -1);
+       jmd_sheet_set_active_cell (JMD_SHEET (de->data_sheet), dict_index, -1, NULL);
+       break;
+      case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
+       jmd_sheet_scroll_to (JMD_SHEET (de->var_sheet), -1, dict_index);
+       jmd_sheet_set_active_cell (JMD_SHEET (de->var_sheet), -1, dict_index, NULL);
+       break;
     }
 }
 
-/* Returns the "active" data sheet in DE.  If DE is in single-paned mode, this
-   is the only data sheet.  If DE is in split mode (showing four data sheets),
-   this is the focused data sheet or, if none is focused, the data sheet with
-   selected cells or, if none has selected cells, the upper-left data sheet. */
-PsppireDataSheet *
-psppire_data_editor_get_active_data_sheet (PsppireDataEditor *de)
-{
-  if (de->split)
-    {
-      PsppireDataSheet *data_sheet;
-      GtkWidget *scroller;
-      int i;
-
-      /* If one of the datasheet's scrollers is focused, choose that one. */
-      scroller = gtk_container_get_focus_child (
-        GTK_CONTAINER (de->datasheet_vbox_widget));
-      if (scroller != NULL)
-        return PSPPIRE_DATA_SHEET (gtk_bin_get_child (GTK_BIN (scroller)));
-
-      /* Otherwise if there's a nonempty selection in some data sheet, choose
-         that one. */
-      FOR_EACH_DATA_SHEET (data_sheet, i, de)
-        {
-          PsppSheetSelection *selection;
-
-          selection = pspp_sheet_view_get_selection (
-            PSPP_SHEET_VIEW (data_sheet));
-          if (pspp_sheet_selection_count_selected_rows (selection)
-              && pspp_sheet_selection_count_selected_columns (selection))
-            return data_sheet;
-        }
-    }
-
-  return PSPPIRE_DATA_SHEET (de->data_sheets[0]);
-}
-
-/* Returns the UI manager that should be merged into DE's toplevel widget's UI
-   manager to display menu items and toolbar items specific to DE's current
-   page and data sheet.
-
-   DE's toplevel widget can watch for changes by connecting to DE's
-   notify::ui-manager signal. */
-GtkUIManager *
-psppire_data_editor_get_ui_manager (PsppireDataEditor *de)
+/* Set the datum at COL, ROW, to that contained in VALUE.
+ */
+static void
+store_set_datum (GtkTreeModel *model, gint col, gint row,
+                        const GValue *value)
 {
-  psppire_data_editor_update_ui_manager (de);
-  return de->ui_manager;
+  PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
+  GVariant *v = g_value_get_variant (value);
+  union value uv;
+  value_variant_get (&uv, v);
+  const struct variable *var = psppire_dict_get_variable (store->dict, col);
+  psppire_data_store_set_value (store, row, var, &uv);
+  value_destroy_from_variant (&uv, v);
 }
 
-static void
-psppire_data_editor_update_ui_manager (PsppireDataEditor *de)
+void
+psppire_data_editor_paste (PsppireDataEditor *de)
 {
-  PsppireDataSheet *data_sheet;
-  GtkUIManager *ui_manager;
-
-  ui_manager = NULL;
+  JmdSheet *sheet = JMD_SHEET (de->data_sheet);
+  GtkClipboard *clip =
+    gtk_clipboard_get_for_display (gtk_widget_get_display (GTK_WIDGET (sheet)),
+                                  GDK_SELECTION_CLIPBOARD);
 
-  switch (gtk_notebook_get_current_page (GTK_NOTEBOOK (de)))
-    {
-    case PSPPIRE_DATA_EDITOR_DATA_VIEW:
-      data_sheet = psppire_data_editor_get_active_data_sheet (de);
-      if (data_sheet != NULL)
-        ui_manager = psppire_data_sheet_get_ui_manager (data_sheet);
-      else
-        {
-          /* This happens transiently in psppire_data_editor_split_window(). */
-        }
-      break;
-
-    case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
-      ui_manager = psppire_var_sheet_get_ui_manager (
-        PSPPIRE_VAR_SHEET (de->var_sheet));
-      break;
-
-    default:
-      /* This happens transiently in psppire_data_editor_init(). */
-      break;
-    }
-
-  if (ui_manager != de->ui_manager)
-    {
-      if (de->ui_manager)
-        g_object_unref (de->ui_manager);
-      if (ui_manager)
-        g_object_ref (ui_manager);
-      de->ui_manager = ui_manager;
-
-      g_object_notify (G_OBJECT (de), "ui-manager");
-    }
+  jmd_sheet_paste (sheet, clip, store_set_datum);
 }