Merge remote-tracking branch 'origin/master' into sheet
authorJohn Darrington <john@darrington.wattle.id.au>
Sun, 10 Jul 2016 05:47:34 +0000 (07:47 +0200)
committerJohn Darrington <john@darrington.wattle.id.au>
Sun, 10 Jul 2016 05:47:34 +0000 (07:47 +0200)
23 files changed:
configure.ac
lib/gtk-contrib/gtkxpaned.c
src/data/variable.c
src/ui/gui/automake.mk
src/ui/gui/efficient-sheet.mk [new file with mode: 0644]
src/ui/gui/find-dialog.c
src/ui/gui/goto-case-dialog.c
src/ui/gui/goto-case-dialog.h
src/ui/gui/marshaller-list
src/ui/gui/psppire-data-editor.c
src/ui/gui/psppire-data-editor.h
src/ui/gui/psppire-data-sheet.c
src/ui/gui/psppire-data-sheet.h
src/ui/gui/psppire-data-store.c
src/ui/gui/psppire-data-window.c
src/ui/gui/psppire-dict.c
src/ui/gui/psppire-dict.h
src/ui/gui/psppire-import-assistant.c
src/ui/gui/psppire-var-sheet-header.c [new file with mode: 0644]
src/ui/gui/psppire-var-sheet-header.h [new file with mode: 0644]
src/ui/gui/psppire-var-sheet.c [deleted file]
src/ui/gui/psppire-var-sheet.h
src/ui/gui/widgets.c

index 417a6c2e350ea3a713a8ca3f262a629dd25a1dbb..4891c906f03db91dbd54d44556ad22b753d094ef 100644 (file)
@@ -90,8 +90,8 @@ if test "$with_cairo" != no && test "$with_gui" != "no"; then
   PKG_CHECK_MODULES([GTKSOURCEVIEW], [gtksourceview-3.0 >= 3.4.2], [],
     [PSPP_REQUIRED_PREREQ([gtksourceview 3.0 version 3.4.2 or later (or use --without-gui)])])
 
-  PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.32], [],
-    [PSPP_REQUIRED_PREREQ([glib 2.0 version 2.32 or later (or use --without-gui)])])
+  PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.44], [],
+    [PSPP_REQUIRED_PREREQ([glib 2.0 version 2.44 or later (or use --without-gui)])])
 
   AC_ARG_VAR([GLIB_GENMARSHAL])
   AC_CHECK_PROGS([GLIB_GENMARSHAL], [glib-genmarshal])
index 9be83311f0f19b7562a8d8332c0c6930f43e7a01..19dea4554b91fb52aa7a738bf55c5c5bc10c88d5 100644 (file)
@@ -54,7 +54,9 @@ enum ChildProperties
   {
     CHILD_PROP_0,
     CHILD_PROP_RESIZE,
-    CHILD_PROP_SHRINK
+    CHILD_PROP_SHRINK,
+    CHILD_PROP_LEFT_ATTACH,
+    CHILD_PROP_TOP_ATTACH
   };
 
 enum WidgetSignals
@@ -526,6 +528,29 @@ gtk_xpaned_class_init (GtkXPanedClass * class)
                                                      G_MAXINT,
                                                      G_PARAM_READABLE));
 
+
+  gtk_container_class_install_child_property (container_class,
+                                              CHILD_PROP_LEFT_ATTACH,
+                                              g_param_spec_int ("left-attach",
+                                                               "Left Attach",
+                                                               "The column number to which the  left side of the widget should be attached",
+                                                               0, 1,
+                                                               0,
+                                                               G_PARAM_READWRITE));
+
+
+
+  gtk_container_class_install_child_property (container_class,
+                                              CHILD_PROP_TOP_ATTACH,
+                                              g_param_spec_int ("top-attach",
+                                                               "Top Attach",
+                                                               "The row number to which the  top side of the widget should be attached",
+                                                               0, 1,
+                                                               0,
+                                                               G_PARAM_READWRITE));
+
+
+  
   /**
    * GtkPaned:resize:
    *
@@ -812,6 +837,12 @@ gtk_xpaned_size_allocate (GtkWidget * widget, GtkAllocation * allocation)
   GtkRequisition bottom_right_child_requisition;
   gint handle_size;
 
+  g_print ("Allocate %p %p %p %p\n",
+          xpaned->top_left_child,
+          xpaned->top_right_child,
+          xpaned->bottom_left_child,
+          xpaned->bottom_right_child);
+  
   /* determine size of handle(s) */
   gtk_widget_style_get (widget, "handle-size", &handle_size, NULL);
 
@@ -1046,73 +1077,109 @@ gtk_xpaned_set_child_property (GtkContainer * container,
                                const GValue * value, GParamSpec * pspec)
 {
   GtkXPaned *xpaned = GTK_XPANED (container);
-  gboolean old_value = FALSE;
-  gboolean new_value = FALSE;
 
   g_assert (child == xpaned->top_left_child ||
             child == xpaned->top_right_child ||
             child == xpaned->bottom_left_child ||
             child == xpaned->bottom_right_child);
 
-  new_value = g_value_get_boolean (value);
-
+  gint attach = g_value_get_int (value);
   switch (property_id)
     {
+    case CHILD_PROP_LEFT_ATTACH:
+      g_object_ref (child);
+      gtk_widget_unparent (child);
+      if (attach == 0)
+      {
+       if (child == xpaned->top_right_child)
+         xpaned->top_left_child = child;
+       else if (child == xpaned->bottom_right_child)
+         xpaned->bottom_left_child = child;
+      }
+      else
+      {
+       if (child == xpaned->top_left_child)
+         xpaned->top_right_child = child;
+       else if (child == xpaned->bottom_left_child)
+         xpaned->bottom_right_child = child;
+      }
+      gtk_widget_set_parent (child, GTK_WIDGET (xpaned));
+      g_object_unref (child);
+      break;
+    case CHILD_PROP_TOP_ATTACH:
+      g_object_ref (child);
+      gtk_widget_unparent (child);
+      if (attach == 0)
+       {
+         if (child == xpaned->bottom_right_child)
+           xpaned->top_right_child = child;
+         else if (child == xpaned->bottom_left_child)
+           xpaned->top_left_child = child;
+       }
+      else
+       {
+         if (child == xpaned->top_left_child)
+           xpaned->bottom_left_child = child;
+         else if (child == xpaned->top_right_child)
+           xpaned->bottom_right_child = child;
+       }
+      gtk_widget_set_parent (child, GTK_WIDGET (xpaned));
+      g_object_unref (child);
+      break;
     case CHILD_PROP_RESIZE:
-      if (child == xpaned->top_left_child)
-        {
-          old_value = xpaned->top_left_child_resize;
-          xpaned->top_left_child_resize = new_value;
-        }
-      else if (child == xpaned->top_right_child)
-        {
-          old_value = xpaned->top_right_child_resize;
-          xpaned->top_right_child_resize = new_value;
-        }
-      else if (child == xpaned->bottom_left_child)
-        {
-          old_value = xpaned->bottom_left_child_resize;
-          xpaned->bottom_left_child_resize = new_value;
-        }
-      else if (child == xpaned->bottom_right_child)
-        {
-          old_value = xpaned->bottom_right_child_resize;
-          xpaned->bottom_right_child_resize = new_value;
-        }
+      {
+       gboolean  new_value = TRUE;
+       
+       if (child == xpaned->top_left_child)
+         {
+           xpaned->top_left_child_resize = new_value;
+         }
+       else if (child == xpaned->top_right_child)
+         {
+           xpaned->top_right_child_resize = new_value;
+         }
+       else if (child == xpaned->bottom_left_child)
+         {
+           xpaned->bottom_left_child_resize = new_value;
+         }
+       else if (child == xpaned->bottom_right_child)
+         {
+           xpaned->bottom_right_child_resize = new_value;
+         }
+      }
       break;
-
+      
     case CHILD_PROP_SHRINK:
-      if (child == xpaned->top_left_child)
-        {
-          old_value = xpaned->top_left_child_shrink;
-          xpaned->top_left_child_shrink = new_value;
-        }
-      else if (child == xpaned->top_right_child)
-        {
-          old_value = xpaned->top_right_child_shrink;
-          xpaned->top_right_child_shrink = new_value;
-        }
-      else if (child == xpaned->bottom_left_child)
-        {
-          old_value = xpaned->bottom_left_child_shrink;
-          xpaned->bottom_left_child_shrink = new_value;
-        }
-      else if (child == xpaned->bottom_right_child)
-        {
-          old_value = xpaned->bottom_right_child_shrink;
-          xpaned->bottom_right_child_shrink = new_value;
-        }
+      {
+       gboolean  new_value = FALSE;
+       
+       if (child == xpaned->top_left_child)
+         {
+           xpaned->top_left_child_shrink = new_value;
+         }
+       else if (child == xpaned->top_right_child)
+         {
+           xpaned->top_right_child_shrink = new_value;
+         }
+       else if (child == xpaned->bottom_left_child)
+         {
+           xpaned->bottom_left_child_shrink = new_value;
+         }
+       else if (child == xpaned->bottom_right_child)
+         {
+           xpaned->bottom_right_child_shrink = new_value;
+         }
+      }
       break;
 
     default:
       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container,
                                                     property_id, pspec);
-      old_value = -1;           /* quiet gcc */
       break;
     }
 
-  if (old_value != new_value)
-    gtk_widget_queue_resize (GTK_WIDGET (container));
+  gtk_widget_queue_resize (GTK_WIDGET (container));
+  gtk_widget_queue_draw (GTK_WIDGET (container));
 }
 
 static void
@@ -1130,6 +1197,26 @@ gtk_xpaned_get_child_property (GtkContainer * container,
 
   switch (property_id)
     {
+    case CHILD_PROP_TOP_ATTACH:
+      if (child == xpaned->top_left_child)
+       g_value_set_int (value, 0);
+      if (child == xpaned->top_right_child)
+       g_value_set_int (value, 0);
+      if (child == xpaned->bottom_left_child)
+       g_value_set_int (value, 1);
+      if (child == xpaned->bottom_right_child)
+       g_value_set_int (value, 1);
+      break;
+    case CHILD_PROP_LEFT_ATTACH:
+      if (child == xpaned->top_left_child)
+       g_value_set_int (value, 0);
+      if (child == xpaned->bottom_left_child)
+       g_value_set_int (value, 0);
+      if (child == xpaned->top_right_child)
+       g_value_set_int (value, 1);
+      if (child == xpaned->bottom_right_child)
+       g_value_set_int (value, 1);
+      break;
     case CHILD_PROP_RESIZE:
       if (child == xpaned->top_left_child)
         g_value_set_boolean (value, xpaned->top_left_child_resize);
index 6a00d014a18df32b7a85cb90bd3d6b3871444a7c..42a07ee8bb5d3481bec1a4656f13bba1dd61a393 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2006, 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2006, 2009, 2010, 2011, 2012, 2013, 2014, 2016 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 "gettext.h"
 #define _(msgid) gettext (msgid)
+#define N_(msgid) (msgid)
+
+/* This should follow the definition in Gtk */
+typedef struct
+{
+  int value;
+  const char *name;
+  const char *label;
+} GEnumValue;
+
+const GEnumValue align[] =
+  {
+    {ALIGN_LEFT,   "left", N_("Left")},
+    {ALIGN_RIGHT,  "right", N_("Right")},
+    {ALIGN_CENTRE, "center", N_("Center")},
+    {0,0,0}
+  };
+
+const GEnumValue measure[] =
+  {
+    {MEASURE_NOMINAL, "nominal", N_("Nominal")},
+    {MEASURE_ORDINAL, "ordinal", N_("Ordinal")},
+    {MEASURE_SCALE,   "scale", N_("Scale")},
+    {0,0,0}
+  };
+
+const GEnumValue role[] =
+  {
+    {ROLE_INPUT,  "input",    N_("Input")},
+    {ROLE_TARGET, "output",   N_("Output")},
+    {ROLE_BOTH,   "both",     N_("Both")},
+    {ROLE_NONE,   "none",     N_("None")},
+    {ROLE_PARTITION, "partition", N_("Partition")},
+    {ROLE_SPLIT,  "split",    N_("Split")},
+    {0,0,0}
+  };
 
 /* A variable. */
 struct variable
@@ -771,20 +807,8 @@ measure_is_valid (enum measure m)
 const char *
 measure_to_string (enum measure m)
 {
-  switch (m)
-    {
-    case MEASURE_NOMINAL:
-      return _("Nominal");
-
-    case MEASURE_ORDINAL:
-      return _("Ordinal");
-
-    case MEASURE_SCALE:
-      return _("Scale");
-
-    default:
-      return "Invalid";
-    }
+  assert (m == measure[m].value);
+  return gettext (measure[m].label);
 }
 
 /* Returns a string version of measurement level M, for use in PSPP command
@@ -866,31 +890,10 @@ var_role_is_valid (enum var_role role)
 
 /* Returns a string version of ROLE, for display to a user. */
 const char *
-var_role_to_string (enum var_role role)
+var_role_to_string (enum var_role r)
 {
-  switch (role)
-    {
-    case ROLE_INPUT:
-      return _("Input");
-
-    case ROLE_TARGET:
-      return _("Output");
-
-    case ROLE_BOTH:
-      return _("Both");
-
-    case ROLE_NONE:
-      return _("None");
-
-    case ROLE_PARTITION:
-      return _("Partition");
-
-    case ROLE_SPLIT:
-      return _("Split");
-
-    default:
-      return "Invalid";
-    }
+  assert (r == role[r].value);
+  return gettext (role[r].label);
 }
 
 /* Returns a string version of ROLE, for use in PSPP comamnd syntax. */
@@ -996,20 +999,8 @@ alignment_is_valid (enum alignment a)
 const char *
 alignment_to_string (enum alignment a)
 {
-  switch (a)
-    {
-    case ALIGN_LEFT:
-      return _("Left");
-
-    case ALIGN_RIGHT:
-      return _("Right");
-
-    case ALIGN_CENTRE:
-      return _("Center");
-
-    default:
-      return "Invalid";
-    }
+  assert (a == align[a].value);
+  return gettext (align[a].label);
 }
 
 /* Returns a string version of alignment A, for use in PSPP command syntax. */
index 0f0ea346627dfeb7a89925d0d5bbe3894d3d986b..1155cf70db779a1b41dd91f9f030b1e9bdc3bd91 100644 (file)
@@ -69,6 +69,7 @@ EXTRA_DIST += \
        src/ui/gui/marshaller-list \
        src/ui/gui/pspplogo.svg
 
+src_ui_gui_psppire_CPPFLAGS=
 
 if HAVE_GUI
 bin_PROGRAMS += src/ui/gui/psppire 
@@ -306,8 +307,9 @@ src_ui_gui_psppire_SOURCES = \
        src/ui/gui/psppire-value-entry.h \
        src/ui/gui/psppire-var-ptr.c \
        src/ui/gui/psppire-var-ptr.h \
-       src/ui/gui/psppire-var-sheet.c \
        src/ui/gui/psppire-var-sheet.h \
+       src/ui/gui/psppire-var-sheet-header.h \
+       src/ui/gui/psppire-var-sheet-header.c \
        src/ui/gui/psppire-vbuttonbox.h \
        src/ui/gui/psppire-window.c \
        src/ui/gui/psppire-window.h \
@@ -387,6 +389,7 @@ dist_appdata_DATA = src/ui/gui/pspp.appdata.xml
 BUILT_SOURCES += src/ui/gui/psppire-marshal.c src/ui/gui/psppire-marshal.h
 CLEANFILES += src/ui/gui/psppire-marshal.c src/ui/gui/psppire-marshal.h \
        $(nodist_src_ui_gui_psppire_DATA)
+include $(top_srcdir)/src/ui/gui/efficient-sheet.mk
 endif HAVE_GUI
 
 #ensure the installcheck passes even if there is no X server available
@@ -394,7 +397,7 @@ installcheck-local:
        DISPLAY=/invalid/port $(MAKE) $(AM_MAKEFLAGS) installcheck-binPROGRAMS
 
 # <gtk/gtk.h> wrapper
-src_ui_gui_psppire_CPPFLAGS = $(AM_CPPFLAGS) -Isrc/ui/gui/include
+src_ui_gui_psppire_CPPFLAGS += $(AM_CPPFLAGS) -Isrc/ui/gui/include
 BUILT_SOURCES += src/ui/gui/include/gtk/gtk.h
 src/ui/gui/include/gtk/gtk.h: src/ui/gui/include/gtk/gtk.in.h
        @$(MKDIR_P) src/ui/gui/include/gtk
diff --git a/src/ui/gui/efficient-sheet.mk b/src/ui/gui/efficient-sheet.mk
new file mode 100644 (file)
index 0000000..c2512ad
--- /dev/null
@@ -0,0 +1,29 @@
+src_ui_gui_psppire_SOURCES += \
+       src/ui/gui/efficient-sheet/jmd-axis-model.c \
+       src/ui/gui/efficient-sheet/jmd-constraint.c \
+       src/ui/gui/efficient-sheet/jmd-sheet.c \
+       src/ui/gui/efficient-sheet/jmd-sheet-axis.c \
+       src/ui/gui/efficient-sheet/jmd-sheet-body.c \
+       src/ui/gui/efficient-sheet/jmd-sheet-single.c \
+       src/ui/gui/efficient-sheet/jmd-datum.c \
+       src/ui/gui/efficient-sheet/jmd-cell.c
+
+
+nodist_src_ui_gui_psppire_SOURCES += \
+       src/ui/gui/efficient-sheet/jmd-marshaller.c \
+       src/ui/gui/efficient-sheet/jmd-marshaller.h
+
+src_ui_gui_psppire_CPPFLAGS+=-Isrc/ui/gui/efficient-sheet
+
+
+
+BUILT_SOURCES += \
+       src/ui/gui/efficient-sheet/jmd-marshaller.c \
+       src/ui/gui/efficient-sheet/jmd-marshaller.h
+
+src/ui/gui/efficient-sheet/jmd-marshaller.c: src/ui/gui/efficient-sheet/marshall-list
+       glib-genmarshal --body --prefix=jmd_cclosure_marshal $< > $@
+
+src/ui/gui/efficient-sheet/jmd-marshaller.h: src/ui/gui/efficient-sheet/marshall-list
+       glib-genmarshal --header --prefix=jmd_cclosure_marshal $< > $@
+
index fddbe8c49bb74759f4e18a33747dd5c3e9f9ad1e..c8f8451bd85be1cf8492ddc3391320afeb77d9c9 100644 (file)
@@ -36,7 +36,6 @@ which match particular strings */
 #include "ui/gui/dict-display.h"
 #include "ui/gui/find-dialog.h"
 #include "ui/gui/helper.h"
-#include "ui/gui/psppire-data-sheet.h"
 #include "ui/gui/psppire-data-store.h"
 #include "ui/gui/psppire-data-window.h"
 #include "ui/gui/psppire-dialog.h"
@@ -100,13 +99,12 @@ refresh (GObject *obj, const struct find_dialog *fd)
 static void
 do_find (GObject *obj, const struct find_dialog *fd)
 {
-  PsppireDataSheet *data_sheet;
   casenumber x = -1;
   gint column = -1;
   glong row;
 
-  data_sheet = psppire_data_editor_get_active_data_sheet (fd->de->data_editor);
-  row = psppire_data_sheet_get_selected_case (data_sheet);
+  
+  row = 10;
 
   find_value (fd, row, &x, &column);
 
@@ -115,8 +113,6 @@ do_find (GObject *obj, const struct find_dialog *fd)
       gtk_notebook_set_current_page (GTK_NOTEBOOK (fd->de->data_editor),
                                     PSPPIRE_DATA_EDITOR_DATA_VIEW);
 
-      psppire_data_sheet_goto_case (data_sheet, x);
-      psppire_data_sheet_goto_variable (data_sheet, column);
     }
 }
 
index 282f41636883c21315d1f21d6ed08cfa1903a922..54d8eaf3349205b850901d2ff8ffce303dbfe74c 100644 (file)
 
 
 static void
-refresh (PsppireDataSheet *ds, GtkBuilder *xml)
+refresh (GtkWidget *ds, GtkBuilder *xml)
 {
-  PsppireDataStore *data_store = psppire_data_sheet_get_data_store (ds);
-  casenumber case_count ;
-
-  GtkWidget *case_num_entry = get_widget_assert (xml, "goto-case-case-num-entry");
-
-  case_count =  psppire_data_store_get_case_count (data_store);
-
-  gtk_spin_button_set_range (GTK_SPIN_BUTTON (case_num_entry), 1, case_count);
 }
 
 void
-goto_case_dialog (PsppireDataSheet *ds)
+goto_case_dialog (void *ds)
 {
-  GtkWindow *top_level;
-  gint response;
-  GtkBuilder *xml = builder_new ("goto-case.ui");
-  GtkWidget *dialog = get_widget_assert   (xml, "goto-case-dialog");
-
-  top_level = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (ds)));
-  gtk_window_set_transient_for (GTK_WINDOW (dialog), top_level);
-
-  refresh (ds, xml);
-
-  response = psppire_dialog_run (PSPPIRE_DIALOG (dialog));
-
-  if ( response == PSPPIRE_RESPONSE_GOTO )
-    {
-      PsppireDataStore *data_store = psppire_data_sheet_get_data_store (ds);
-      glong case_num;
-      GtkWidget *case_num_entry =
-       get_widget_assert (xml, "goto-case-case-num-entry");
-
-      case_num = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (case_num_entry))
-       - FIRST_CASE_NUMBER ;
-
-      if (case_num >= 0
-          && case_num < psppire_data_store_get_case_count (data_store))
-        psppire_data_sheet_goto_case (ds, case_num);
-    }
 }
index e7c70eee258fec818241c6c6ea232d72b4f24f70..455e2e637c06ae9977ba34e92df6e155f379e3b9 100644 (file)
@@ -17,8 +17,7 @@
 #ifndef __GOTO_CASE_DIALOG_H
 #define __GOTO_CASE_DIALOG_H
 
-#include "psppire-data-sheet.h"
 
-void goto_case_dialog (PsppireDataSheet *ds);
+void goto_case_dialog (void *ds);
 
 #endif
index 0f38e6fc2ea18c1ca6ccfb3c812faf8da4d0a204..5b6c0e5b586f1eb12a07b63946fa86023bc915d9 100644 (file)
@@ -12,3 +12,4 @@ VOID:INT,INT
 VOID:OBJECT,OBJECT
 VOID:POINTER,INT,INT
 VOID:INT,UINT,POINTER
+VOID:UINT,UINT,UINT
index 18fd5e8e8d81d294f0557ecb8de1b6cb2b852ed0..b643acf0462a6ee59d29cd16c67f8c3e05ecfc1c 100644 (file)
 #include "libpspp/str.h"
 #include "ui/gui/helper.h"
 #include "ui/gui/pspp-sheet-selection.h"
-#include "ui/gui/psppire-data-sheet.h"
 #include "ui/gui/psppire-data-store.h"
 #include "ui/gui/psppire-value-entry.h"
 #include "ui/gui/psppire-var-sheet.h"
 #include "ui/gui/psppire-conf.h"
+#include "ui/gui/psppire-var-sheet-header.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 GtkCellRenderer *
+create_spin_renderer (GType type)
+{
+  GtkCellRenderer *r = gtk_cell_renderer_spin_new ();
+      
+  GtkAdjustment *adj = gtk_adjustment_new (0,
+                                          0, G_MAXDOUBLE,
+                                          1, 1,
+                                          0);
+  g_object_set (r,
+               "adjustment", adj,
+               NULL);
+  
+  return r;
+}
+
+static GtkCellRenderer *
+create_combo_renderer (GType type)
+{
+  GtkListStore *list_store = gtk_list_store_new (2, G_TYPE_INT, G_TYPE_STRING);
+  
+  GEnumClass *ec = g_type_class_ref (type);
+  
+  const GEnumValue *ev ;
+  for (ev = ec->values; ev->value_name; ++ev)
+    {
+      GtkTreeIter iter;
+
+      gtk_list_store_append (list_store, &iter);
+
+      gtk_list_store_set (list_store, &iter,
+                         0, ev->value,
+                         1, gettext (ev->value_nick),
+                         -1);
+    }
+
+  GtkCellRenderer *r = gtk_cell_renderer_combo_new ();
+
+  g_object_set (r,
+               "model", list_store,
+               "text-column", 1,
+               "has-entry", TRUE,
+               NULL);
+
+  return r;
+}
+
+GtkCellRenderer *xx ;
+GtkCellRenderer *column_width_renderer ;
+GtkCellRenderer *measure_renderer ;
+GtkCellRenderer *alignment_renderer ;
+
+
+
+static GtkCellRenderer *
+select_renderer_func (gint col, gint row, GType type)
+{
+  if (!xx)
+    xx = create_spin_renderer (type);
+
+  if (col == DICT_TVM_COL_ROLE && !column_width_renderer)
+    column_width_renderer = create_combo_renderer (type);
+
+  if (col == DICT_TVM_COL_MEASURE && !measure_renderer)
+    measure_renderer = create_combo_renderer (type);
+
+  if (col == DICT_TVM_COL_ALIGNMENT && !alignment_renderer)
+    alignment_renderer = create_combo_renderer (type);
+
+  switch  (col)
+    {
+    case DICT_TVM_COL_WIDTH:
+    case DICT_TVM_COL_DECIMAL:
+    case DICT_TVM_COL_COLUMNS:
+      return xx;
+      
+    case DICT_TVM_COL_ALIGNMENT:
+      return alignment_renderer;
+
+    case DICT_TVM_COL_MEASURE:
+      return measure_renderer;
+      
+    case DICT_TVM_COL_ROLE:
+      return column_width_renderer;
+    }
+  
+  return NULL;
+}
+
 
 static void psppire_data_editor_class_init          (PsppireDataEditorClass *klass);
 static void psppire_data_editor_init                (PsppireDataEditor      *de);
@@ -127,13 +213,52 @@ enum
 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_var_property (PsppireDict *dict, gint col, gint row, GValue *value)
+{
+  /* Return the IDXth variable */
+  struct variable *var =  psppire_dict_get_variable (dict, row);
+
+  switch (col)
+    {
+    case DICT_TVM_COL_NAME:
+      dict_rename_var (dict->dict, var, g_value_get_string (value));
+      break;
+    case DICT_TVM_COL_LABEL:
+      var_set_label (var, g_value_get_string (value));
+      break;
+    case DICT_TVM_COL_COLUMNS:
+      var_set_display_width (var, g_value_get_int (value));
+      break;
+    case DICT_TVM_COL_MEASURE:
+      var_set_measure (var, g_value_get_enum (value));
+      break;
+    case DICT_TVM_COL_ALIGNMENT:
+      var_set_alignment (var, g_value_get_enum (value));
+      break;
+    case DICT_TVM_COL_ROLE:
+      var_set_role (var, g_value_get_enum (value));
+      break;
+    default:
+      g_message ("Changing col %d of var sheet not yet supported", col);
+      break;
+    }
+}
+
+static void
+change_data_value (PsppireDataStore *store, gint col, gint row, GValue *value)
+{
+  const struct variable *var = psppire_dict_get_variable (store->dict, col);
 
-  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);
+  union value v;
+  value_init (&v, var_get_width (var));
+  v.f = g_value_get_double (value);
+  
+  psppire_data_store_set_value (store, row, var, &v);
+
+  value_destroy (&v, var_get_width (var));
 }
 
 static void
@@ -143,8 +268,6 @@ psppire_data_editor_set_property (GObject         *object,
                                  GParamSpec      *pspec)
 {
   PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (object);
-  PsppireDataSheet *data_sheet;
-  int i;
 
   switch (prop_id)
     {
@@ -162,8 +285,14 @@ psppire_data_editor_set_property (GObject         *object,
 
       de->data_store = g_value_get_pointer (value);
       g_object_ref (de->data_store);
+      g_print ("NEW STORE\n");
+
+      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, "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,13 +303,13 @@ 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);
+      g_signal_connect_swapped (de->var_sheet, "value-changed",
+                               G_CALLBACK (change_var_property), de->dict);
+
       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:
@@ -209,9 +338,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));
@@ -315,390 +441,95 @@ psppire_data_editor_class_init (PsppireDataEditorClass *klass)
                                    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 (PsppireVarSheet *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_goto_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);
-
-  gchar *ref_cell_text;
-  GList *selected_columns, *iter;
-  struct variable *var;
-  gint n_cases;
-  gint n_vars;
-
-  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);
-
-  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);
-    }
-
-  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);
-
-      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);
-
-      psppire_value_entry_set_variable (value_entry, var);
-      psppire_value_entry_set_show_value_label (value_entry,
-                                                show_value_labels);
-
-      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);
-
-      gtk_widget_set_sensitive (de->datum_entry, TRUE);
-    }
-  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);
-    }
-
-  gtk_label_set_label (GTK_LABEL (de->cell_ref_label),
-                       ref_cell_text ? ref_cell_text : "");
-  g_free (ref_cell_text);
+  g_print ("%s\n", __FUNCTION__);
 }
 
 static void
 on_datum_entry_activate (PsppireValueEntry *entry, PsppireDataEditor *de)
 {
-  PsppireDataSheet *data_sheet = psppire_data_editor_get_active_data_sheet (de);
-  struct variable *var;
-  union value value;
-  int width;
-  gint row;
-
-  row = psppire_data_sheet_get_current_case (data_sheet);
-  var = psppire_data_sheet_get_current_variable (data_sheet);
-  if (row < 0 || !var)
-    return;
-
-  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);
-}
-
-static void
-on_data_sheet_selection_changed (PsppSheetSelection *selection,
-                                 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;
-
-      FOR_EACH_DATA_SHEET (ds, i, de)
-        {
-          PsppSheetSelection *s;
-
-          s = pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (ds));
-          if (s != selection)
-            pspp_sheet_selection_unselect_all (s);
-        }
-    }
-
-  refresh_entry (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)
-{
-  enum
-    {
-      TL = GTK_XPANED_TOP_LEFT,
-      TR = GTK_XPANED_TOP_RIGHT,
-      BL = GTK_XPANED_BOTTOM_LEFT,
-      BR = GTK_XPANED_BOTTOM_RIGHT
-    };
-
-  int fixed_height = pspp_sheet_view_get_fixed_height (PSPP_SHEET_VIEW (ds));
-
-  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);
-}
 
 static void
 disconnect_data_sheets (PsppireDataEditor *de)
 {
-  PsppireDataSheet *ds;
-  int i;
-
-  FOR_EACH_DATA_SHEET (ds, i, de)
-    {
-      PsppSheetSelection *selection;
-
-      if (ds == NULL)
-        {
-          /* This can only happen if 'dispose' runs more than once. */
-          continue;
-        }
-
-      if (i == GTK_XPANED_TOP_LEFT)
-        g_signal_handlers_disconnect_by_func (
-          ds, G_CALLBACK (on_data_sheet_fixed_height_notify), de);
-
-      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);
-
-      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);
-
-      de->data_sheets[i] = NULL;
-    }
-}
-
-static GtkWidget *
-make_data_sheet (PsppireDataEditor *de, GtkTreeViewGridLines grid_lines,
-                 gboolean show_value_labels)
-{
-  PsppSheetSelection *selection;
-  GtkWidget *ds;
-
-  ds = psppire_data_sheet_new ();
-  pspp_sheet_view_set_grid_lines (PSPP_SHEET_VIEW (ds), grid_lines);
-  psppire_data_sheet_set_value_labels (PSPPIRE_DATA_SHEET (ds),
-                                       show_value_labels);
-
-  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);
-
-  selection = pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (ds));
-  g_signal_connect (selection, "changed",
-                    G_CALLBACK (on_data_sheet_selection_changed), de);
-
-  return ds;
 }
 
-static GtkWidget *
-make_single_datasheet (PsppireDataEditor *de, GtkTreeViewGridLines grid_lines,
-                       gboolean show_value_labels)
+/* Called when the active cell or the selection in the data sheet changes */
+static void
+on_data_selection_change (PsppireDataEditor *de, JmdRange *sel)
 {
-  GtkWidget *data_sheet_scroller;
-
-  de->data_sheets[0] = make_data_sheet (de, grid_lines, show_value_labels);
-  de->data_sheets[1] = de->data_sheets[2] = de->data_sheets[3] = NULL;
+  gchar *ref_cell_text = NULL;
 
-  /* 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]);
+  gint n_cases = abs (sel->end_y - sel->start_y) + 1;
+  gint n_vars = abs (sel->end_x - sel->start_x) + 1;
 
-  return data_sheet_scroller;
-}
-
-static GtkWidget *
-make_split_datasheet (PsppireDataEditor *de, GtkTreeViewGridLines grid_lines,
-                      gboolean show_value_labels)
-{
-  /* Panes, in the order in which we want to create them. */
-  enum
+  if (n_cases == 1 && n_vars == 1)
     {
-      TL,                       /* top left */
-      TR,                       /* top right */
-      BL,                       /* bottom left */
-      BR                        /* bottom right */
-    };
-
-  PsppSheetView *ds[4];
-  GtkXPaned *xpaned;
-  int i;
-
-  xpaned = GTK_XPANED (gtk_xpaned_new ());
-
-  for (i = 0; i < 4; i++)
+      /* A single cell is selected */
+      const struct variable *var = psppire_dict_get_variable (de->dict, sel->start_x);
+      
+      ref_cell_text = g_strdup_printf (_("%d : %s"),
+                                      sel->start_y + 1, var_get_name (var));
+    }
+  else
     {
-      GtkAdjustment *hadjust, *vadjust;
-      GtkPolicyType hpolicy, vpolicy;
-      GtkWidget *scroller;
-
-      de->data_sheets[i] = make_data_sheet (de, grid_lines, show_value_labels);
-      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;
-
-        case TR:
-          gtk_xpaned_pack_top_right (xpaned, scroller, TRUE, TRUE);
-          break;
-
-        case BL:
-          gtk_xpaned_pack_bottom_left (xpaned, scroller, TRUE, TRUE);
-          break;
-
-        case BR:
-          gtk_xpaned_pack_bottom_right (xpaned, scroller, TRUE, TRUE);
-          break;
-
-        default:
-          g_warn_if_reached ();
-        }
+      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);
     }
-
-  /* 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);
-
-  /* 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);
-
-  g_signal_connect (ds[TL], "notify::fixed-height",
-                    G_CALLBACK (on_data_sheet_fixed_height_notify), de);
-
-  return GTK_WIDGET (xpaned);
+  
+  gtk_label_set_label (GTK_LABEL (de->cell_ref_label),
+                      ref_cell_text ? ref_cell_text : "");
+  
+  g_free (ref_cell_text);
 }
 
+
 static void set_font_recursively (GtkWidget *w, gpointer data);
 
 static void
 psppire_data_editor_init (PsppireDataEditor *de)
 {
-  GtkWidget *var_sheet_scroller;
   GtkWidget *hbox;
   gchar *fontname = NULL;
 
@@ -721,33 +552,46 @@ psppire_data_editor_init (PsppireDataEditor *de)
   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, FALSE);
-
+  de->data_sheet = g_object_new (JMD_TYPE_SHEET, 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 = g_object_new (JMD_TYPE_SHEET, NULL);
+
+  PsppireVarSheetHeader *vsh = g_object_new (PSPPIRE_TYPE_VAR_SHEET_HEADER, NULL);
+  
+  g_object_set (de->var_sheet,
+               "hmodel", vsh,
+               "select-renderer-func", select_renderer_func,
+               NULL);
+
+  
+  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);
 
   if (psppire_conf_get_string (psppire_conf_new (),
@@ -778,15 +622,11 @@ 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);
 }
 
@@ -825,37 +665,13 @@ psppire_data_editor_set_font (PsppireDataEditor *de, PangoFontDescription *font_
 void
 psppire_data_editor_split_window (PsppireDataEditor *de, gboolean split)
 {
-  GtkTreeViewGridLines grid_lines;
-  gboolean labels;
-
   if (split == de->split)
     return;
 
-
-  grid_lines = pspp_sheet_view_get_grid_lines (
-    PSPP_SHEET_VIEW (de->data_sheets[0]));
-  labels = psppire_data_sheet_get_value_labels (PSPPIRE_DATA_SHEET (
-                                                  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, labels);
-  else
-    de->datasheet_vbox_widget = make_single_datasheet (de, grid_lines, labels);
 
   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)
@@ -871,57 +687,8 @@ psppire_data_editor_split_window (PsppireDataEditor *de, gboolean split)
 void
 psppire_data_editor_goto_variable (PsppireDataEditor *de, gint dict_index)
 {
-  PsppireDataSheet *data_sheet;
-
-  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);
-      psppire_data_sheet_goto_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;
-    }
 }
 
-/* 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
@@ -939,7 +706,6 @@ psppire_data_editor_get_ui_manager (PsppireDataEditor *de)
 static void
 psppire_data_editor_update_ui_manager (PsppireDataEditor *de)
 {
-  PsppireDataSheet *data_sheet;
   GtkUIManager *ui_manager;
 
   ui_manager = NULL;
@@ -947,18 +713,9 @@ psppire_data_editor_update_ui_manager (PsppireDataEditor *de)
   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:
index 81495d51207ecd66c1dc836cb45fae7bc42629c8..e75ccd781989a49c92280d59ab2140f94ba78f31 100644 (file)
@@ -61,13 +61,13 @@ struct _PsppireDataEditor
 
   /* Variable sheet tab. */
   GtkWidget *var_sheet;
+  GtkWidget *data_sheet;
 
   /* Data sheet tab. */
   GtkWidget *vbox;             /* Top-level widget in tab. */
   GtkWidget *cell_ref_label;   /* GtkLabel that shows selected case and var. */
   GtkWidget *datum_entry;      /* PsppireValueEntry for editing current cell. */
-  GtkWidget *datasheet_vbox_widget; /* ->vbox child that holds data sheets. */
-  GtkWidget *data_sheets[4];   /* Normally one data sheet; four, if split. */
+
   gboolean split;              /* True if data sheets are split. */
 
   /* UI manager for whichever var or data sheet is currently in use. */
index dbc92159777723e87b9a97c0cf67c97fea241b8e..cce71873b9bceccf4377396d1f331f75436a59a6 100644 (file)
    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
 #include <config.h>
-
-#include "ui/gui/psppire-data-sheet.h"
-
-#include "data/case-map.h"
-#include "data/casereader.h"
-#include "data/casewriter.h"
-#include "data/data-out.h"
-#include "data/datasheet.h"
-#include "data/format.h"
-#include "data/value-labels.h"
-#include "libpspp/intern.h"
-#include "libpspp/range-set.h"
-#include "ui/gui/executor.h"
-#include "ui/gui/find-dialog.h"
-#include "ui/gui/goto-case-dialog.h"
-#include "ui/gui/builder-wrapper.h"
-#include "ui/gui/helper.h"
-#include "ui/gui/pspp-sheet-selection.h"
-#include "ui/gui/psppire-cell-renderer-button.h"
-#include "ui/gui/psppire-data-store.h"
-#include "ui/gui/psppire-data-window.h"
-#include "ui/gui/psppire-dialog-action-var-info.h"
-#include "ui/gui/psppire-empty-list-store.h"
-#include "ui/gui/psppire-marshal.h"
-
-#include "gl/intprops.h"
-#include "gl/xalloc.h"
-
-#include <gettext.h>
-#define _(msgid) gettext (msgid)
-#define N_(msgid) msgid
-
-static void psppire_data_sheet_dispose (GObject *);
-static void psppire_data_sheet_unset_data_store (PsppireDataSheet *);
-
-static void psppire_data_sheet_update_clip_actions (PsppireDataSheet *);
-static void psppire_data_sheet_update_primary_selection (PsppireDataSheet *,
-                                                         gboolean should_own);
-static void psppire_data_sheet_set_clip (PsppireDataSheet *, gboolean cut);
-
-static void on_selection_changed (PsppSheetSelection *, gpointer);
-static void on_owner_change (GtkClipboard *, GdkEventOwnerChange *, gpointer);
-static void psppire_data_sheet_clip_received_cb (GtkClipboard *,
-                                                 GtkSelectionData *, gpointer);
-
-G_DEFINE_TYPE (PsppireDataSheet, psppire_data_sheet, PSPP_TYPE_SHEET_VIEW);
-
-static gboolean
-get_tooltip_location (GtkWidget *widget, GtkTooltip *tooltip,
-                      gint wx, gint wy,
-                      size_t *row, PsppSheetViewColumn **columnp)
-{
-  PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
-  gint bx, by;
-  GtkTreePath *path;
-  GtkTreeIter iter;
-  PsppSheetViewColumn *tree_column;
-  GtkTreeModel *tree_model;
-  bool ok;
-
-  /* Check that WIDGET is really visible on the screen before we
-     do anything else.  This is a bug fix for a sticky situation:
-     when text_data_import_assistant() returns, it frees the data
-     necessary to compose the tool tip message, but there may be
-     a tool tip under preparation at that point (even if there is
-     no visible tool tip) that will call back into us a little
-     bit later.  Perhaps the correct solution to this problem is
-     to make the data related to the tool tips part of a GObject
-     that only gets destroyed when all references are released,
-     but this solution appears to be effective too. */
-  if (!gtk_widget_get_mapped (widget))
-    return FALSE;
-
-  pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view,
-                                                     wx, wy, &bx, &by);
-  if (!pspp_sheet_view_get_path_at_pos (tree_view, bx, by,
-                                      &path, &tree_column, NULL, NULL))
-    return FALSE;
-
-  *columnp = tree_column;
-
-  pspp_sheet_view_set_tooltip_cell (tree_view, tooltip, path, tree_column,
-                                    NULL);
-
-  tree_model = pspp_sheet_view_get_model (tree_view);
-  ok = gtk_tree_model_get_iter (tree_model, &iter, path);
-  gtk_tree_path_free (path);
-  if (!ok)
-    return FALSE;
-
-  *row = GPOINTER_TO_INT (iter.user_data);
-  return TRUE;
-}
-
-static gboolean
-on_query_tooltip (GtkWidget *widget, gint wx, gint wy,
-                  gboolean keyboard_mode UNUSED,
-                  GtkTooltip *tooltip, gpointer data UNUSED)
-{
-  PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget);
-  PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
-  PsppSheetViewColumn *column;
-  struct variable *var;
-  const char *label;
-  union value v;
-  size_t row;
-  int width;
-
-  g_return_val_if_fail (data_store != NULL, FALSE);
-  g_return_val_if_fail (data_store->datasheet != NULL, FALSE);
-
-  if (!get_tooltip_location (widget, tooltip, wx, wy, &row, &column))
-    return FALSE;
-
-  var = g_object_get_data (G_OBJECT (column), "variable");
-  if (var == NULL)
-    {
-      if (g_object_get_data (G_OBJECT (column), "new-var-column") == NULL)
-        return FALSE;
-
-      gtk_tooltip_set_text (tooltip,
-                            _("Enter a number to add a new variable."));
-      return TRUE;
-    }
-  else if (row >= datasheet_get_n_rows (data_store->datasheet))
-    {
-      gtk_tooltip_set_text (tooltip, _("Enter a number to add a new case."));
-      return TRUE;
-    }
-
-  width = var_get_width (var);
-
-  value_init (&v, width);
-  datasheet_get_value (data_store->datasheet, row, var_get_case_index (var),
-                       &v);
-
-  label = var_lookup_value_label (var, &v);
-  if (label != NULL)
-    {
-      if (data_sheet->show_value_labels)
-        {
-          char *s = value_to_text (v, var);
-          gtk_tooltip_set_text (tooltip, s);
-          free (s);
-        }
-      else
-        gtk_tooltip_set_text (tooltip, label);
-    }
-  value_destroy (&v, width);
-
-  return label != NULL;
-}
-
-static void
-render_row_number_cell (PsppSheetViewColumn *tree_column,
-                        GtkCellRenderer *cell,
-                        GtkTreeModel *model,
-                        GtkTreeIter *iter,
-                        gpointer store_)
-{
-  PsppireDataStore *store = store_;
-  GValue gvalue = { 0, };
-  gint row = GPOINTER_TO_INT (iter->user_data);
-
-  g_return_if_fail (store->datasheet);
-
-  g_value_init (&gvalue, G_TYPE_INT);
-  g_value_set_int (&gvalue, row + 1);
-  g_object_set_property (G_OBJECT (cell), "label", &gvalue);
-  g_value_unset (&gvalue);
-
-  if (row < datasheet_get_n_rows (store->datasheet))
-    g_object_set (cell, "editable", TRUE, NULL);
-  else
-    g_object_set (cell, "editable", FALSE, NULL);
-
-  g_object_set (cell,
-                "slash", psppire_data_store_filtered (store, row),
-                NULL);
-}
-
-static void
-on_row_number_clicked (PsppireCellRendererButton *button,
-                       gchar *path_string,
-                       PsppSheetView *sheet_view)
-{
-  PsppSheetSelection *selection;
-  GtkTreePath *path;
-
-  path = gtk_tree_path_new_from_string (path_string);
-
-  selection = pspp_sheet_view_get_selection (sheet_view);
-  pspp_sheet_selection_unselect_all (selection);
-  pspp_sheet_selection_select_path (selection, path);
-  pspp_sheet_selection_select_all_columns (selection);
-
-  gtk_tree_path_free (path);
-}
-
-static void
-make_row_number_column (PsppireDataSheet *data_sheet,
-                        PsppireDataStore *ds)
-{
-  PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
-  PsppSheetViewColumn *column;
-  GtkCellRenderer *renderer;
-
-  renderer = psppire_cell_renderer_button_new ();
-  g_object_set (renderer, "xalign", 1.0, NULL);
-  g_signal_connect (renderer, "clicked", G_CALLBACK (on_row_number_clicked),
-                    sheet_view);
-
-  column = pspp_sheet_view_column_new_with_attributes (_("Case"),
-                                                       renderer, NULL);
-  pspp_sheet_view_column_set_selectable (column, TRUE);
-  pspp_sheet_view_column_set_row_head (column, TRUE);
-  pspp_sheet_view_column_set_tabbable (column, FALSE);
-  pspp_sheet_view_column_set_clickable (column, TRUE);
-  pspp_sheet_view_column_set_cell_data_func (
-    column, renderer, render_row_number_cell, ds, NULL);
-  pspp_sheet_view_column_set_fixed_width (column, 50);
-  pspp_sheet_view_column_set_visible (column, data_sheet->show_case_numbers);
-  pspp_sheet_view_append_column (sheet_view, column);
-}
-
-static void
-render_data_cell (PsppSheetViewColumn *tree_column,
-                  GtkCellRenderer *cell,
-                  GtkTreeModel *model,
-                  GtkTreeIter *iter,
-                  gpointer data_sheet_)
-{
-  PsppireDataSheet *data_sheet = data_sheet_;
-  PsppireDataStore *store = psppire_data_sheet_get_data_store (data_sheet);
-  struct variable *var;
-  gchar *string;
-  gint row;
-
-  double xalign;
-
-  row = GPOINTER_TO_INT (iter->user_data);
-  var = g_object_get_data (G_OBJECT (tree_column), "variable");
-
-  string = psppire_data_store_get_string (store, row, var,
-                                          data_sheet->show_value_labels);
-  if (string != NULL)
-    {
-      GValue gvalue = { 0 };
-
-      g_value_init (&gvalue, G_TYPE_STRING);
-      g_value_take_string (&gvalue, string);
-      g_object_set_property (G_OBJECT (cell), "text", &gvalue);
-      g_value_unset (&gvalue);
-    }
-  else
-    g_object_set (G_OBJECT (cell), "text", "", NULL);
-
-  switch (var_get_alignment (var))
-    {
-    case ALIGN_LEFT: xalign = 0.0; break;
-    case ALIGN_RIGHT: xalign = 1.0; break;
-    case ALIGN_CENTRE: xalign = 0.5; break;
-    default: xalign = 0.0; break;
-    }
-  g_object_set (cell,
-                "xalign", xalign,
-                "editable", TRUE,
-                NULL);
-}
-
-static gint
-get_string_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
-                  const char *string)
-{
-  gint width;
-  g_object_set (G_OBJECT (renderer), "text", string, (void *) NULL);
-  gtk_cell_renderer_get_preferred_width (renderer, GTK_WIDGET (treeview),
-                                        NULL, &width);
-
-  return width;
-}
-
-static gint
-get_monospace_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
-                     size_t char_cnt)
-{
-  struct string s;
-  gint width;
-
-  ds_init_empty (&s);
-  ds_put_byte_multiple (&s, '0', char_cnt);
-  ds_put_byte (&s, ' ');
-  width = get_string_width (treeview, renderer, ds_cstr (&s));
-  ds_destroy (&s);
-
-  return width;
-}
-
-static void
-on_data_column_editing_started (GtkCellRenderer *cell,
-                                GtkCellEditable *editable,
-                                const gchar     *path,
-                                gpointer         user_data)
-{
-  PsppSheetViewColumn *column = g_object_get_data (G_OBJECT (cell), "column");
-  PsppireDataSheet *data_sheet = g_object_get_data (G_OBJECT (cell), "data-sheet");
-  PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
-  struct variable *var;
-
-  g_return_if_fail (column);
-  g_return_if_fail (data_sheet);
-  g_return_if_fail (data_store);
-
-
-  g_object_ref (editable);
-  g_object_set_data_full (G_OBJECT (cell), "data-sheet-editable",
-                          editable, g_object_unref);
-
-  var = g_object_get_data (G_OBJECT (column), "variable");
-  g_return_if_fail (var);
-
-  if (var_has_value_labels (var) && GTK_IS_COMBO_BOX (editable))
-    {
-      const struct val_labs *labels = var_get_value_labels (var);
-      const struct val_lab **vls = val_labs_sorted (labels);
-      size_t n_vls = val_labs_count (labels);
-      GtkListStore *list_store;
-      int i;
-
-      list_store = gtk_list_store_new (1, G_TYPE_STRING);
-      for (i = 0; i < n_vls; ++i)
-        {
-          const struct val_lab *vl = vls[i];
-          GtkTreeIter iter;
-
-          gtk_list_store_append (list_store, &iter);
-          gtk_list_store_set (list_store, &iter,
-                              0, val_lab_get_label (vl),
-                              -1);
-        }
-      free (vls);
-
-      gtk_combo_box_set_model (GTK_COMBO_BOX (editable),
-                               GTK_TREE_MODEL (list_store));
-      g_object_unref (list_store);
-    }
-}
-
-static void
-scroll_to_bottom (GtkWidget      *widget,
-                  GtkRequisition *requisition,
-                  gpointer        unused UNUSED)
-{
-  PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget);
-  PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
-  GtkAdjustment *vadjust;
-
-  vadjust = pspp_sheet_view_get_vadjustment (sheet_view);
-  gtk_adjustment_set_value (vadjust, gtk_adjustment_get_upper (vadjust));
-
-  if (data_sheet->scroll_to_bottom_signal)
-    {
-      g_signal_handler_disconnect (data_sheet,
-                                   data_sheet->scroll_to_bottom_signal);
-      data_sheet->scroll_to_bottom_signal = 0;
-    }
-}
-
-static void
-on_data_column_edited (GtkCellRendererText *cell,
-                       gchar               *path_string,
-                       gchar               *new_text,
-                       gpointer             user_data)
-{
-  PsppSheetViewColumn *column = g_object_get_data (G_OBJECT (cell), "column");
-  PsppireDataSheet *data_sheet = g_object_get_data (G_OBJECT (cell), "data-sheet");
-  PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
-  GtkEditable *editable;
-  struct variable *var;
-  GtkTreePath *path;
-  gboolean is_val_lab;
-  gboolean new_row;
-  gint row;
-
-  path = gtk_tree_path_new_from_string (path_string);
-  row = gtk_tree_path_get_indices (path)[0];
-  gtk_tree_path_free (path);
-
-  var = g_object_get_data (G_OBJECT (column), "variable");
-
-  new_row = row == psppire_data_store_get_case_count (data_store);
-  if (new_row && new_text[0] == '\0')
-    return;
-
-  editable = g_object_steal_data (G_OBJECT (cell), "data-sheet-editable");
-  g_return_if_fail (editable != NULL);
-  is_val_lab = (GTK_IS_COMBO_BOX (editable)
-                && gtk_combo_box_get_active (GTK_COMBO_BOX (editable)) >= 0);
-  g_object_unref (editable);
-
-  psppire_data_store_set_string (data_store, new_text, row, var, is_val_lab);
-
-  if (new_row && !data_sheet->scroll_to_bottom_signal)
-    {
-      gtk_widget_queue_resize (GTK_WIDGET (data_sheet));
-      data_sheet->scroll_to_bottom_signal =
-        g_signal_connect (data_sheet, "size-allocate",
-                          G_CALLBACK (scroll_to_bottom), NULL);
-    }
-  else
-    {
-      /* We could be more specific about what to redraw, if it seems
-         important for performance. */
-      gtk_widget_queue_draw (GTK_WIDGET (data_sheet));
-    }
-}
-
-static void
-scroll_to_right (GtkWidget      *widget,
-                 PsppireDataSheet  *data_sheet)
-{
-  PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
-  PsppSheetViewColumn *column, *prev;
-  GList *columns, *iter;
-
-  column = NULL;
-  prev = NULL;
-  columns = pspp_sheet_view_get_columns (sheet_view);
-  for (iter = columns; iter; iter = iter->next)
-    {
-      PsppSheetViewColumn *c = iter->data;
-      if (g_object_get_data (G_OBJECT (c), "new-var-column"))
-        {
-          column = c;
-          break;
-        }
-      prev = c;
-    }
-  g_list_free (columns);
-
-  if (column == NULL)
-    return;
-
-  pspp_sheet_view_scroll_to_cell (sheet_view, NULL, column, FALSE, 0, 0);
-
-  if (prev)
-    {
-      GtkTreePath *path;
-
-      pspp_sheet_view_get_cursor (sheet_view, &path, NULL);
-      if (path)
-        {
-          pspp_sheet_view_set_cursor (sheet_view, path, prev, TRUE);
-          gtk_tree_path_free (path);
-        }
-    }
-
-  if (data_sheet->scroll_to_right_signal)
-    {
-      g_signal_handler_disconnect (widget, data_sheet->scroll_to_right_signal);
-      data_sheet->scroll_to_right_signal = 0;
-    }
-}
-
-static void
-on_new_variable_column_edited (GtkCellRendererText *cell,
-                               gchar               *path_string,
-                               gchar               *new_text,
-                               gpointer             user_data)
-{
-  PsppireDataSheet *data_sheet = g_object_get_data (G_OBJECT (cell), "data-sheet");
-  PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
-  PsppireDict *dict = data_store->dict;
-  struct variable *var;
-  GtkTreePath *path;
-  char name[64];
-  gint row;
-
-  if (new_text[0] == '\0')
-    {
-      /* User didn't enter anything so don't create a variable. */
-      return;
-    }
-
-  path = gtk_tree_path_new_from_string (path_string);
-  row = gtk_tree_path_get_indices (path)[0];
-  gtk_tree_path_free (path);
-
-  if (!psppire_dict_generate_name (dict, name, sizeof name))
-    return;
-
-  var = psppire_dict_insert_variable (dict, psppire_dict_get_var_cnt (dict),
-                                      name);
-  g_return_if_fail (var != NULL);
-
-  psppire_data_store_set_string (data_store, new_text, row, var, FALSE);
-
-  if (!data_sheet->scroll_to_right_signal)
-    {
-      gtk_widget_queue_resize (GTK_WIDGET (data_sheet));
-      data_sheet->scroll_to_right_signal =
-        g_signal_connect_after (gtk_widget_get_toplevel (GTK_WIDGET (data_sheet)), "check-resize",
-                                G_CALLBACK (scroll_to_right), data_sheet);
-    }
-  else
-    {
-      /* We could be more specific about what to redraw, if it seems
-         important for performance. */
-      gtk_widget_queue_draw (GTK_WIDGET (data_sheet));
-    }
-}
-
-static void
-calc_width_conversion (PsppireDataSheet *data_sheet,
-                       gint *base_width, gint *incr_width)
-{
-  GtkCellRenderer *cell;
-  gint w1, w10;
-
-  cell = gtk_cell_renderer_text_new ();
-  w1 = get_monospace_width (PSPP_SHEET_VIEW (data_sheet), cell, 1);
-  w10 = get_monospace_width (PSPP_SHEET_VIEW (data_sheet), cell, 10);
-  *incr_width = MAX (1, (w10 - w1) / 9);
-  *base_width = MAX (0, w10 - *incr_width * 10);
-  g_object_ref_sink (cell);
-  g_object_unref (cell);
-}
-
-static gint
-display_width_from_pixel_width (PsppireDataSheet *data_sheet,
-                                gint pixel_width)
-{
-  gint base_width, incr_width;
-
-  calc_width_conversion (data_sheet, &base_width, &incr_width);
-  return MAX ((pixel_width - base_width + incr_width / 2) / incr_width, 1);
-}
-
-static gint
-display_width_to_pixel_width (PsppireDataSheet *data_sheet,
-                              gint display_width,
-                              gint base_width,
-                              gint incr_width)
-{
-  return base_width + incr_width * display_width;
-}
-
-static void
-on_data_column_resized (GObject    *gobject,
-                        GParamSpec *pspec,
-                        gpointer    user_data)
-{
-  PsppireDataSheet *data_sheet = user_data;
-  PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
-  PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (gobject);
-  struct variable *var;
-  gint pixel_width;
-  int display_width;
-
-  if (data_store == NULL)
-    return;
-
-  pixel_width = pspp_sheet_view_column_get_width (column);
-  if (pixel_width == pspp_sheet_view_column_get_fixed_width (column))
-    {
-      /* Short-circuit the expensive display_width_from_pixel_width()
-         calculation, to make loading .sav files with 2000 columns visibly
-         faster. */
-      return;
-    }
-
-  var = g_object_get_data (G_OBJECT (column), "variable");
-  display_width = display_width_from_pixel_width (data_sheet, pixel_width);
-  var_set_display_width (var, display_width);
-}
-
-static void
-do_data_column_popup_menu (PsppSheetViewColumn *column,
-                           guint button, guint32 time)
-{
-  GtkWidget *sheet_view = pspp_sheet_view_column_get_tree_view (column);
-  PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (sheet_view);
-  GtkWidget *menu;
-
-  menu = get_widget_assert (data_sheet->builder, "datasheet-variable-popup");
-  gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time);
-}
-
-static void
-on_data_column_popup_menu (PsppSheetViewColumn *column,
-                           gpointer user_data UNUSED)
-{
-  do_data_column_popup_menu (column, 0, gtk_get_current_event_time ());
-}
-
-static gboolean
-on_column_button_press_event (PsppSheetViewColumn *column,
-                              GdkEventButton *event,
-                              gpointer user_data UNUSED)
-{
-  PsppSheetSelection *selection;
-  PsppSheetView *sheet_view;
-
-  sheet_view = PSPP_SHEET_VIEW (pspp_sheet_view_column_get_tree_view (
-                                  column));
-  g_return_val_if_fail (sheet_view != NULL, FALSE);
-
-  selection = pspp_sheet_view_get_selection (sheet_view);
-  g_return_val_if_fail (selection != NULL, FALSE);
-
-  if (event->type == GDK_BUTTON_PRESS && event->button == 3)
-    {
-      do_data_column_popup_menu (column, event->button, event->time);
-      return TRUE;
-    }
-  else if (event->type == GDK_2BUTTON_PRESS && event->button == 1)
-    {
-      PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (sheet_view);
-      struct variable *var;
-
-      var = g_object_get_data (G_OBJECT (column), "variable");
-      if (var != NULL)
-        {
-          gboolean handled;
-
-          g_signal_emit_by_name (data_sheet, "var-double-clicked",
-                                 var_get_dict_index (var), &handled);
-          return handled;
-        }
-    }
-
-  return FALSE;
-}
-
-static gboolean
-on_data_column_query_tooltip (PsppSheetViewColumn *column,
-                              GtkTooltip *tooltip,
-                              gpointer user_data UNUSED)
-{
-  struct variable *var;
-  const char *text;
-
-  var = g_object_get_data (G_OBJECT (column), "variable");
-  g_return_val_if_fail (var != NULL, FALSE);
-
-  text = var_has_label (var) ? var_get_label (var) : var_get_name (var);
-  gtk_tooltip_set_text (tooltip, text);
-
-  return TRUE;
-}
-
-static void
-add_data_column_cell_renderer (PsppireDataSheet *data_sheet,
-                               PsppSheetViewColumn *column)
-{
-  GtkCellRenderer *cell;
-  struct variable *var;
-
-  var = g_object_get_data (G_OBJECT (column), "variable");
-  g_return_if_fail (var != NULL);
-
-  if (data_sheet->show_value_labels && var_has_value_labels (var))
-    {
-      cell = gtk_cell_renderer_combo_new ();
-      g_object_set (G_OBJECT (cell),
-                    "has-entry", TRUE,
-                    "text-column", 0,
-                    NULL);
-    }
-  else
-    cell = gtk_cell_renderer_text_new ();
-
-  g_signal_connect (cell, "editing-started",
-                    G_CALLBACK (on_data_column_editing_started), NULL);
-  g_signal_connect (cell, "edited", G_CALLBACK (on_data_column_edited), NULL);
-
-  g_object_set_data (G_OBJECT (cell), "column", column);
-  g_object_set_data (G_OBJECT (cell), "data-sheet", data_sheet);
-
-  pspp_sheet_view_column_clear (column);
-  pspp_sheet_view_column_pack_start (column, cell, TRUE);
-
-  pspp_sheet_view_column_set_cell_data_func (
-    column, cell, render_data_cell, data_sheet, NULL);
-}
-
-static PsppSheetViewColumn *
-make_data_column (PsppireDataSheet *data_sheet, gint dict_idx,
-                  gint base_width, gint incr_width)
-{
-  PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
-  struct variable *var;
-  PsppSheetViewColumn *column;
-  char *name;
-  int width;
-
-  var = psppire_dict_get_variable (data_store->dict, dict_idx);
-
-  column = pspp_sheet_view_column_new ();
-
-  name = escape_underscores (var_get_name (var));
-  pspp_sheet_view_column_set_title (column, name);
-  free (name);
-
-  g_object_set_data (G_OBJECT (column), "variable", var);
-
-  width = display_width_to_pixel_width (data_sheet,
-                                        var_get_display_width (var),
-                                        base_width, incr_width);
-  pspp_sheet_view_column_set_min_width (column, 10);
-  pspp_sheet_view_column_set_fixed_width (column, width);
-  pspp_sheet_view_column_set_resizable (column, TRUE);
-
-  pspp_sheet_view_column_set_clickable (column, TRUE);
-  g_signal_connect (column, "notify::width",
-                    G_CALLBACK (on_data_column_resized), data_sheet);
-
-  g_signal_connect (column, "button-press-event",
-                    G_CALLBACK (on_column_button_press_event),
-                    data_sheet);
-  g_signal_connect (column, "query-tooltip",
-                    G_CALLBACK (on_data_column_query_tooltip), NULL);
-  g_signal_connect (column, "popup-menu",
-                    G_CALLBACK (on_data_column_popup_menu), data_sheet);
-
-  add_data_column_cell_renderer (data_sheet, column);
-
-  return column;
-}
-
-static void
-make_new_variable_column (PsppireDataSheet *data_sheet,
-                          gint base_width, gint incr_width)
-{
-  PsppSheetViewColumn *column;
-  GtkCellRenderer *cell;
-  int width;
-
-  cell = gtk_cell_renderer_text_new ();
-  g_object_set (cell, "editable", TRUE, NULL);
-
-  g_signal_connect (cell, "edited", G_CALLBACK (on_new_variable_column_edited),
-                    NULL);
-
-  column = pspp_sheet_view_column_new_with_attributes ("", cell, NULL);
-  g_object_set_data (G_OBJECT (column), "new-var-column", column);
-
-  width = display_width_to_pixel_width (data_sheet, 8, base_width, incr_width);
-  pspp_sheet_view_column_set_min_width (column, 10);
-  pspp_sheet_view_column_set_fixed_width (column, width);
-  pspp_sheet_view_column_set_tabbable (column, FALSE);
-
-  g_object_set_data (G_OBJECT (cell), "data-sheet", data_sheet);
-  g_signal_connect (column, "button-press-event",
-                    G_CALLBACK (on_column_button_press_event),
-                    data_sheet);
-  g_signal_connect (column, "popup-menu",
-                    G_CALLBACK (on_data_column_popup_menu), data_sheet);
-
-  pspp_sheet_view_column_set_visible (column, data_sheet->may_create_vars);
-
-  pspp_sheet_view_append_column (PSPP_SHEET_VIEW (data_sheet), column);
-  data_sheet->new_variable_column = column;
-}
-
-static void
-psppire_data_sheet_model_changed (GObject    *gobject,
-                                  GParamSpec *pspec,
-                                  gpointer    user_data)
-{
-  PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (gobject);
-  PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
-  PsppireDataStore *data_store;
-
-  /* Remove old columns. */
-  for (;;)
-    {
-      PsppSheetViewColumn *column = pspp_sheet_view_get_column (sheet_view, 0);
-      if (column == NULL)
-        break;
-
-      pspp_sheet_view_remove_column (sheet_view, column);
-    }
-  data_sheet->new_variable_column = NULL;
-
-  if (pspp_sheet_view_get_model (sheet_view) == NULL)
-    {
-      /* Don't create any columns at all if there's no model.  Otherwise we'll
-         create some columns as part of the "dispose" callback for the sheet
-         view, which sets the model to NULL.  That causes warnings to be
-         logged and is obviously undesirable in any case. */
-      return;
-    }
-
-  /* Add new columns. */
-  data_store = psppire_data_sheet_get_data_store (data_sheet);
-  if (data_store != NULL)
-    {
-      gint base_width, incr_width;
-      int i;
-
-      calc_width_conversion (data_sheet, &base_width, &incr_width);
-
-      make_row_number_column (data_sheet, data_store);
-      for (i = 0; i < psppire_dict_get_var_cnt (data_store->dict); i++)
-        {
-          PsppSheetViewColumn *column;
-
-          column = make_data_column (data_sheet, i, base_width, incr_width);
-          pspp_sheet_view_append_column (sheet_view, column);
-        }
-      make_new_variable_column (data_sheet, base_width, incr_width);
-    }
-}
-
-enum
-  {
-    PROP_0,
-    PROP_DATA_STORE,
-    PROP_VALUE_LABELS,
-    PROP_CASE_NUMBERS,
-    PROP_CURRENT_CASE,
-    PROP_MAY_CREATE_VARS,
-    PROP_MAY_DELETE_VARS,
-    PROP_UI_MANAGER
-  };
-
-static void
-psppire_data_sheet_set_property (GObject      *object,
-                                 guint         prop_id,
-                                 const GValue *value,
-                                 GParamSpec   *pspec)
-{
-  PsppireDataSheet *obj = PSPPIRE_DATA_SHEET (object);
-
-  switch (prop_id)
-    {
-    case PROP_DATA_STORE:
-      psppire_data_sheet_set_data_store (
-        obj, PSPPIRE_DATA_STORE (g_value_get_object (value)));
-      break;
-
-    case PROP_VALUE_LABELS:
-      psppire_data_sheet_set_value_labels (obj, g_value_get_boolean (value));
-      break;
-
-    case PROP_CASE_NUMBERS:
-      psppire_data_sheet_set_case_numbers (obj, g_value_get_boolean (value));
-      break;
-
-    case PROP_CURRENT_CASE:
-      psppire_data_sheet_goto_case (obj, g_value_get_long (value));
-      break;
-
-    case PROP_MAY_CREATE_VARS:
-      psppire_data_sheet_set_may_create_vars (obj,
-                                              g_value_get_boolean (value));
-      break;
-
-    case PROP_MAY_DELETE_VARS:
-      psppire_data_sheet_set_may_delete_vars (obj,
-                                              g_value_get_boolean (value));
-      break;
-
-    case PROP_UI_MANAGER:
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
-    }
-}
-
-static void
-psppire_data_sheet_get_property (GObject      *object,
-                                 guint         prop_id,
-                                 GValue       *value,
-                                 GParamSpec   *pspec)
-{
-  PsppireDataSheet *obj = PSPPIRE_DATA_SHEET (object);
-
-  switch (prop_id)
-    {
-    case PROP_DATA_STORE:
-      g_value_set_object (value, psppire_data_sheet_get_data_store (obj));
-      break;
-
-    case PROP_VALUE_LABELS:
-      g_value_set_boolean (value, psppire_data_sheet_get_value_labels (obj));
-      break;
-
-    case PROP_CASE_NUMBERS:
-      g_value_set_boolean (value, psppire_data_sheet_get_case_numbers (obj));
-      break;
-
-    case PROP_CURRENT_CASE:
-      g_value_set_long (value, psppire_data_sheet_get_selected_case (obj));
-      break;
-
-    case PROP_MAY_CREATE_VARS:
-      g_value_set_boolean (value, obj->may_create_vars);
-      break;
-
-    case PROP_MAY_DELETE_VARS:
-      g_value_set_boolean (value, obj->may_delete_vars);
-      break;
-
-    case PROP_UI_MANAGER:
-      g_value_set_object (value, psppire_data_sheet_get_ui_manager (obj));
-      break;
-
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
-    }
-}
-
-gboolean
-psppire_data_sheet_get_value_labels (const PsppireDataSheet *ds)
-{
-  return ds->show_value_labels;
-}
-
-void
-psppire_data_sheet_set_value_labels (PsppireDataSheet *ds,
-                                  gboolean show_value_labels)
-{
-  show_value_labels = !!show_value_labels;
-  if (show_value_labels != ds->show_value_labels)
-    {
-      ds->show_value_labels = show_value_labels;
-      g_object_notify (G_OBJECT (ds), "value-labels");
-
-      /* Pretend the model changed, to force the columns to be rebuilt.
-         Otherwise cell renderers won't get changed from combo boxes to text
-         entries or vice versa. */
-      g_object_notify (G_OBJECT (ds), "model");
-    }
-}
-
-gboolean
-psppire_data_sheet_get_case_numbers (const PsppireDataSheet *ds)
-{
-  return ds->show_case_numbers;
-}
-
-void
-psppire_data_sheet_set_case_numbers (PsppireDataSheet *ds,
-                                     gboolean show_case_numbers)
-{
-  show_case_numbers = !!show_case_numbers;
-  if (show_case_numbers != ds->show_case_numbers)
-    {
-      PsppSheetViewColumn *column;
-
-      ds->show_case_numbers = show_case_numbers;
-      column = pspp_sheet_view_get_column (PSPP_SHEET_VIEW (ds), 0);
-      if (column)
-        pspp_sheet_view_column_set_visible (column, show_case_numbers);
-
-      g_object_notify (G_OBJECT (ds), "case-numbers");
-      gtk_widget_queue_draw (GTK_WIDGET (ds));
-    }
-}
-
-gboolean
-psppire_data_sheet_get_may_create_vars (PsppireDataSheet *data_sheet)
-{
-  return data_sheet->may_create_vars;
-}
-
-void
-psppire_data_sheet_set_may_create_vars (PsppireDataSheet *data_sheet,
-                                       gboolean may_create_vars)
-{
-  if (data_sheet->may_create_vars != may_create_vars)
-    {
-      data_sheet->may_create_vars = may_create_vars;
-      if (data_sheet->new_variable_column)
-        pspp_sheet_view_column_set_visible (data_sheet->new_variable_column,
-                                            may_create_vars);
-
-      on_selection_changed (pspp_sheet_view_get_selection (
-                              PSPP_SHEET_VIEW (data_sheet)), NULL);
-    }
-}
-
-gboolean
-psppire_data_sheet_get_may_delete_vars (PsppireDataSheet *data_sheet)
-{
-  return data_sheet->may_delete_vars;
-}
-
-void
-psppire_data_sheet_set_may_delete_vars (PsppireDataSheet *data_sheet,
-                                       gboolean may_delete_vars)
-{
-  if (data_sheet->may_delete_vars != may_delete_vars)
-    {
-      data_sheet->may_delete_vars = may_delete_vars;
-      on_selection_changed (pspp_sheet_view_get_selection (
-                              PSPP_SHEET_VIEW (data_sheet)), NULL);
-    }
-}
-
-static PsppSheetViewColumn *
-psppire_data_sheet_find_column_for_variable (PsppireDataSheet *data_sheet,
-                                             gint dict_index)
-{
-  PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
-  PsppireDataStore *data_store;
-  PsppSheetViewColumn *column;
-  struct variable *var;
-  GList *list, *iter;
-
-  data_store = psppire_data_sheet_get_data_store (data_sheet);
-  g_return_val_if_fail (data_store != NULL, NULL);
-  g_return_val_if_fail (data_store->dict != NULL, NULL);
-
-  var = psppire_dict_get_variable (data_store->dict, dict_index);
-  g_return_val_if_fail (var != NULL, NULL);
-
-  column = NULL;
-  list = pspp_sheet_view_get_columns (sheet_view);
-  for (iter = list; iter != NULL; iter = iter->next)
-    {
-      PsppSheetViewColumn *c = iter->data;
-      struct variable *v;
-
-      v = g_object_get_data (G_OBJECT (c), "variable");
-      if (v == var)
-        {
-          column = c;
-          break;
-        }
-    }
-  g_list_free (list);
-
-  return column;
-}
-
-void
-psppire_data_sheet_goto_variable (PsppireDataSheet *data_sheet,
-                                  gint dict_index)
-{
-  PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
-  PsppSheetViewColumn *column;
-
-  column = psppire_data_sheet_find_column_for_variable (data_sheet,
-                                                        dict_index);
-  if (column != NULL)
-    {
-      GtkTreePath *path;
-
-      gint row = psppire_data_sheet_get_current_case (data_sheet);
-      path = gtk_tree_path_new_from_indices (row >= 0 ? row : 0, -1);
-
-      pspp_sheet_view_scroll_to_cell (sheet_view, path, column,
-                                      FALSE, 0.0, 0.0);
-      pspp_sheet_view_set_cursor (sheet_view, path, column, FALSE);
-      gtk_tree_path_free (path);
-    }
-}
-
-struct variable *
-psppire_data_sheet_get_current_variable (const PsppireDataSheet *data_sheet)
-{
-  PsppSheetSelection *selection;
-  struct variable *var;
-  GList *selected_columns;
-  GList *iter;
-
-  selection = pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (data_sheet));
-  selected_columns = pspp_sheet_selection_get_selected_columns (selection);
-
-  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)
-        {
-          if (var)
-            {
-              var = NULL;
-              break;
-            }
-          else
-            var = v;
-        }
-    }
-
-  g_list_free (selected_columns);
-
-  return var;
-
-}
-void
-psppire_data_sheet_goto_case (PsppireDataSheet *data_sheet, gint case_index)
-{
-  PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
-  PsppireDataStore *store = data_sheet->data_store;
-  PsppSheetSelection *selection;
-  GtkTreePath *path;
-
-  g_return_if_fail (case_index >= 0);
-  g_return_if_fail (case_index < psppire_data_store_get_case_count (store));
-
-  path = gtk_tree_path_new_from_indices (case_index, -1);
-
-  /* Select the case. */
-  selection = pspp_sheet_view_get_selection (sheet_view);
-  pspp_sheet_selection_unselect_all (selection);
-  pspp_sheet_selection_select_path (selection, path);
-  pspp_sheet_selection_select_all_columns (selection);
-
-  /* Scroll so that the case is visible. */
-  pspp_sheet_view_scroll_to_cell (sheet_view, path, NULL, FALSE, 0.0, 0.0);
-
-  gtk_tree_path_free (path);
-}
-
-/* Returns the 0-based index of a selected case, if there is at least one, and
-   -1 otherwise.
-
-   If more than one case is selected, returns the one with the smallest index,
-   that is, the index of the case closest to the beginning of the file.  The
-   row that can be used to insert a new case is not considered a case. */
-gint
-psppire_data_sheet_get_selected_case (const PsppireDataSheet *data_sheet)
-{
-  PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
-  PsppireDataStore *store = data_sheet->data_store;
-  const struct range_set_node *node;
-  PsppSheetSelection *selection;
-  struct range_set *rows;
-  gint row;
-
-  selection = pspp_sheet_view_get_selection (sheet_view);
-  rows = pspp_sheet_selection_get_range_set (selection);
-  node = range_set_first (rows);
-  row = (node && node->start < psppire_data_store_get_case_count (store)
-         ? node->start
-         : -1);
-  range_set_destroy (rows);
-
-  return row;
-}
-
-/* Returns the 0-based index of a selected case, if exactly one case is
-   selected, and -1 otherwise.  Returns -1 if the row that can be used to
-   insert a new case is selected. */
-gint
-psppire_data_sheet_get_current_case (const PsppireDataSheet *data_sheet)
-{
-  PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
-  PsppireDataStore *store = data_sheet->data_store;
-  const struct range_set_node *node;
-  PsppSheetSelection *selection;
-  struct range_set *rows;
-  gint row;
-
-  selection = pspp_sheet_view_get_selection (sheet_view);
-  if (pspp_sheet_selection_count_selected_rows (selection) != 1)
-    return -1;
-
-  rows = pspp_sheet_selection_get_range_set (selection);
-  node = range_set_first (rows);
-  row = (node && node->start < psppire_data_store_get_case_count (store)
-         ? node->start
-         : -1);
-  range_set_destroy (rows);
-
-  return row;
-}
-
-GtkUIManager *
-psppire_data_sheet_get_ui_manager (PsppireDataSheet *data_sheet)
-{
-  if (data_sheet->uim == NULL)
-    {
-      data_sheet->uim = 
-       GTK_UI_MANAGER (get_object_assert (data_sheet->builder,
-                                          "data_sheet_uim",
-                                          GTK_TYPE_UI_MANAGER));
-      g_object_ref (data_sheet->uim);
-    }
-
-  return data_sheet->uim;
-}
-
-static void
-psppire_data_sheet_dispose (GObject *object)
-{
-  PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (object);
-
-  if (data_sheet->clip != NULL && data_sheet->on_owner_change_signal != 0)
-    {
-      g_signal_handler_disconnect (data_sheet->clip,
-                                   data_sheet->on_owner_change_signal);
-      data_sheet->on_owner_change_signal = 0;
-    }
-
-  if (data_sheet->dispose_has_run)
-    return;
-
-  data_sheet->dispose_has_run = TRUE;
-
-  psppire_data_sheet_unset_data_store (data_sheet);
-
-  g_object_unref (data_sheet->builder);
-
-  if (data_sheet->uim)
-    g_object_unref (data_sheet->uim);
-
-  G_OBJECT_CLASS (psppire_data_sheet_parent_class)->dispose (object);
-}
-
-static void
-psppire_data_sheet_map (GtkWidget *widget)
-{
-  PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget);
-
-  GTK_WIDGET_CLASS (psppire_data_sheet_parent_class)->map (widget);
-
-  data_sheet->clip = gtk_widget_get_clipboard (widget,
-                                               GDK_SELECTION_CLIPBOARD);
-  if (data_sheet->on_owner_change_signal)
-    g_signal_handler_disconnect (data_sheet->clip,
-                                 data_sheet->on_owner_change_signal);
-  data_sheet->on_owner_change_signal
-    = g_signal_connect (data_sheet->clip, "owner-change",
-                        G_CALLBACK (on_owner_change), widget);
-  on_owner_change (data_sheet->clip, NULL, widget);
-}
-
-static void
-psppire_data_sheet_class_init (PsppireDataSheetClass *class)
-{
-  GObjectClass *gobject_class;
-  GtkWidgetClass *widget_class;
-
-  gobject_class = G_OBJECT_CLASS (class);
-  gobject_class->set_property = psppire_data_sheet_set_property;
-  gobject_class->get_property = psppire_data_sheet_get_property;
-  gobject_class->dispose = psppire_data_sheet_dispose;
-
-  widget_class = GTK_WIDGET_CLASS (class);
-  widget_class->map = psppire_data_sheet_map;
-
-  g_signal_new ("var-double-clicked",
-                G_OBJECT_CLASS_TYPE (gobject_class),
-                G_SIGNAL_RUN_LAST,
-                0,
-                g_signal_accumulator_true_handled, NULL,
-                psppire_marshal_BOOLEAN__INT,
-                G_TYPE_BOOLEAN, 1, G_TYPE_INT);
-
-  g_object_class_install_property (
-    gobject_class, PROP_DATA_STORE,
-    g_param_spec_object ("data-store",
-                         "Data Store",
-                         "The data store for the data sheet to display.",
-                         PSPPIRE_TYPE_DATA_STORE,
-                         G_PARAM_WRITABLE | G_PARAM_READABLE));
-
-  g_object_class_install_property (
-    gobject_class, PROP_VALUE_LABELS,
-    g_param_spec_boolean ("value-labels",
-                          "Value Labels",
-                          "Whether or not the data sheet should display labels instead of values",
-                         FALSE,
-                          G_PARAM_WRITABLE | G_PARAM_READABLE));
-
-  g_object_class_install_property (
-    gobject_class, PROP_CASE_NUMBERS,
-    g_param_spec_boolean ("case-numbers",
-                          "Case Numbers",
-                          "Whether or not the data sheet should display case numbers",
-                         FALSE,
-                          G_PARAM_WRITABLE | G_PARAM_READABLE));
-
-  g_object_class_install_property (
-    gobject_class,
-    PROP_CURRENT_CASE,
-    g_param_spec_long ("current-case",
-                      "Current Case",
-                      "Zero based number of the selected case",
-                      0, CASENUMBER_MAX,
-                      0,
-                      G_PARAM_WRITABLE | G_PARAM_READABLE));
-
-  g_object_class_install_property (
-    gobject_class,
-    PROP_MAY_CREATE_VARS,
-    g_param_spec_boolean ("may-create-vars",
-                          "May create variables",
-                          "Whether the user may create more variables",
-                          TRUE,
-                          G_PARAM_READWRITE));
-
-  g_object_class_install_property (
-    gobject_class,
-    PROP_MAY_DELETE_VARS,
-    g_param_spec_boolean ("may-delete-vars",
-                          "May delete variables",
-                          "Whether the user may delete variables",
-                          TRUE,
-                          G_PARAM_READWRITE));
-
-  g_object_class_install_property (
-    gobject_class,
-    PROP_UI_MANAGER,
-    g_param_spec_object ("ui-manager",
-                         "UI Manager",
-                         "UI manager for the data sheet.  The client should merge this UI manager with the active UI manager to obtain data sheet specific menu items and tool bar items.",
-                         GTK_TYPE_UI_MANAGER,
-                         G_PARAM_READABLE));
-}
-
-static void
-do_popup_menu (GtkWidget *widget, guint button, guint32 time)
-{
-  PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget);
-  GtkWidget *menu;
-
-  menu = get_widget_assert (data_sheet->builder, "datasheet-cases-popup");
-  gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time);
-}
-
-static void
-on_popup_menu (GtkWidget *widget, gpointer user_data UNUSED)
-{
-  do_popup_menu (widget, 0, gtk_get_current_event_time ());
-}
-
-static gboolean
-on_button_pressed (GtkWidget *widget, GdkEventButton *event,
-                   gpointer user_data UNUSED)
-{
-  PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
-
-  if (event->type == GDK_BUTTON_PRESS && event->button == 3)
-    {
-      PsppSheetSelection *selection;
-
-      selection = pspp_sheet_view_get_selection (sheet_view);
-      if (pspp_sheet_selection_count_selected_rows (selection) <= 1)
-        {
-          GtkTreePath *path;
-
-          if (pspp_sheet_view_get_path_at_pos (sheet_view, event->x, event->y,
-                                               &path, NULL, NULL, NULL))
-            {
-              pspp_sheet_selection_unselect_all (selection);
-              pspp_sheet_selection_select_path (selection, path);
-              pspp_sheet_selection_select_all_columns (selection);
-              gtk_tree_path_free (path);
-            }
-        }
-
-      do_popup_menu (widget, event->button, event->time);
-
-      return TRUE;
-    }
-
-  return FALSE;
-}
-
-static void
-on_edit_clear_cases (GtkAction *action, PsppireDataSheet *data_sheet)
-{
-  PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
-  PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
-  const struct range_set_node *node;
-  struct range_set *selected;
-
-  selected = pspp_sheet_selection_get_range_set (selection);
-  for (node = range_set_last (selected); node != NULL;
-       node = range_set_prev (selected, node))
-    {
-      unsigned long int start = range_set_node_get_start (node);
-      unsigned long int count = range_set_node_get_width (node);
-
-      psppire_data_store_delete_cases (data_sheet->data_store, start, count);
-    }
-  range_set_destroy (selected);
-}
-
-static void
-on_selection_changed (PsppSheetSelection *selection,
-                      gpointer user_data UNUSED)
-{
-  PsppSheetView *sheet_view = pspp_sheet_selection_get_tree_view (selection);
-  PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (sheet_view);
-  gint n_selected_rows;
-  gboolean any_variables_selected;
-  gboolean may_delete_cases, may_delete_vars, may_insert_vars;
-  GList *list, *iter;
-  GtkTreePath *path;
-  GtkAction *action;
-
-  n_selected_rows = pspp_sheet_selection_count_selected_rows (selection);
-
-  action = get_action_assert (data_sheet->builder, "edit_insert-case");
-  gtk_action_set_sensitive (action, n_selected_rows > 0);
-
-  switch (n_selected_rows)
-    {
-    case 0:
-      may_delete_cases = FALSE;
-      break;
-
-    case 1:
-      /* The row used for inserting new cases cannot be deleted. */
-      path = gtk_tree_path_new_from_indices (
-        psppire_data_store_get_case_count (data_sheet->data_store), -1);
-      may_delete_cases = !pspp_sheet_selection_path_is_selected (selection,
-                                                                 path);
-      gtk_tree_path_free (path);
-      break;
-
-    default:
-      may_delete_cases = TRUE;
-      break;
-    }
-  action = get_action_assert (data_sheet->builder, "edit_clear-cases");
-  gtk_action_set_sensitive (action, may_delete_cases);
-
-  any_variables_selected = FALSE;
-  may_delete_vars = may_insert_vars = FALSE;
-  list = pspp_sheet_selection_get_selected_columns (selection);
-  for (iter = list; iter != NULL; iter = iter->next)
-    {
-      PsppSheetViewColumn *column = iter->data;
-      struct variable *var = g_object_get_data (G_OBJECT (column), "variable");
-
-      if (var != NULL)
-        {
-          may_delete_vars = may_insert_vars = TRUE;
-          any_variables_selected = TRUE;
-          break;
-        }
-      if (g_object_get_data (G_OBJECT (column), "new-var-column") != NULL)
-        may_insert_vars = TRUE;
-    }
-  g_list_free (list);
-
-  may_insert_vars = may_insert_vars && data_sheet->may_create_vars;
-  may_delete_vars = may_delete_vars && data_sheet->may_delete_vars;
-
-  action = get_action_assert (data_sheet->builder, "edit_insert-variable");
-  gtk_action_set_sensitive (action, may_insert_vars);
-
-  action = get_action_assert (data_sheet->builder, "edit_clear-variables");
-  gtk_action_set_sensitive (action, may_delete_vars);
-
-  action = get_action_assert (data_sheet->builder, "sort-up");
-  gtk_action_set_sensitive (action, may_delete_vars);
-
-  action = get_action_assert (data_sheet->builder, "sort-down");
-  gtk_action_set_sensitive (action, may_delete_vars);
-
-  psppire_data_sheet_update_clip_actions (data_sheet);
-  psppire_data_sheet_update_primary_selection (data_sheet,
-                                               (n_selected_rows > 0
-                                                && any_variables_selected));
-}
-
-static gboolean
-psppire_data_sheet_get_selected_range (PsppireDataSheet *data_sheet,
-                                    struct range_set **rowsp,
-                                    struct range_set **colsp)
-{
-  PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
-  PsppireDataStore *data_store = data_sheet->data_store;
-  PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
-  unsigned long n_cases;
-  struct range_set *rows, *cols;
-  GList *list, *iter;
-
-  if (data_store == NULL)
-    return FALSE;
-  n_cases = psppire_data_store_get_case_count (data_store);
-
-  rows = pspp_sheet_selection_get_range_set (selection);
-  range_set_set0 (rows, n_cases, ULONG_MAX - n_cases);
-  if (range_set_is_empty (rows))
-    {
-      range_set_destroy (rows);
-      return FALSE;
-    }
-
-  cols = range_set_create ();
-  list = pspp_sheet_selection_get_selected_columns (selection);
-  for (iter = list; iter != NULL; iter = iter->next)
-    {
-      PsppSheetViewColumn *column = iter->data;
-      struct variable *var = g_object_get_data (G_OBJECT (column), "variable");
-
-      if (var != NULL)
-        range_set_set1 (cols, var_get_dict_index (var), 1);
-    }
-  g_list_free (list);
-  if (range_set_is_empty (cols))
-    {
-      range_set_destroy (rows);
-      range_set_destroy (cols);
-      return FALSE;
-    }
-
-  *rowsp = rows;
-  *colsp = cols;
-  return TRUE;
-}
-
-static void
-on_edit_insert_case (GtkAction *action, PsppireDataSheet *data_sheet)
-{
-  PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
-  PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
-  PsppireDataStore *data_store = data_sheet->data_store;
-  struct range_set *selected;
-  unsigned long row;
-
-  selected = pspp_sheet_selection_get_range_set (selection);
-  row = range_set_scan (selected, 0);
-  range_set_destroy (selected);
-
-  if (row <= psppire_data_store_get_case_count (data_store))
-    psppire_data_store_insert_new_case (data_store, row);
-}
-
-static void
-on_edit_insert_variable (GtkAction *action, PsppireDataSheet *data_sheet)
-{
-  PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
-  PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
-  PsppireDict *dict = data_sheet->data_store->dict;
-  PsppSheetViewColumn *column;
-  struct variable *var;
-  gchar name[64];
-  GList *list;
-  gint index;
-
-  list = pspp_sheet_selection_get_selected_columns (selection);
-  if (list == NULL)
-    return;
-  column = list->data;
-  g_list_free (list);
-
-  var = g_object_get_data (G_OBJECT (column), "variable");
-  index = var ? var_get_dict_index (var) : psppire_dict_get_var_cnt (dict);
-  if (psppire_dict_generate_name (dict, name, sizeof name))
-    psppire_dict_insert_variable (dict, index, name);
-}
-
-static void
-on_edit_clear_variables (GtkAction *action, PsppireDataSheet *data_sheet)
-{
-  PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
-  PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
-  PsppireDict *dict = data_sheet->data_store->dict;
-  GList *list, *iter;
-
-  list = pspp_sheet_selection_get_selected_columns (selection);
-  if (list == NULL)
-    return;
-  list = g_list_reverse (list);
-  for (iter = list; iter; iter = iter->next)
-    {
-      PsppSheetViewColumn *column = iter->data;
-      struct variable *var;
-
-      var = g_object_get_data (G_OBJECT (column), "variable");
-      if (var != NULL)
-        psppire_dict_delete_variables (dict, var_get_dict_index (var), 1);
-    }
-  g_list_free (list);
-}
-
-enum sort_order
-  {
-    SORT_ASCEND,
-    SORT_DESCEND
-  };
-
-static void
-do_sort (PsppireDataSheet *data_sheet, enum sort_order order)
-{
-  PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
-  PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
-  PsppireDataWindow *pdw;
-  GList *list, *iter;
-  GString *syntax;
-  int n_vars;
-
-  pdw = psppire_data_window_for_data_store (data_sheet->data_store);
-  g_return_if_fail (pdw != NULL);
-
-  list = pspp_sheet_selection_get_selected_columns (selection);
-
-  syntax = g_string_new ("SORT CASES BY");
-  n_vars = 0;
-  for (iter = list; iter; iter = iter->next)
-    {
-      PsppSheetViewColumn *column = iter->data;
-      struct variable *var;
-
-      var = g_object_get_data (G_OBJECT (column), "variable");
-      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);
-}
-
-void
-on_sort_up (GtkAction *action, PsppireDataSheet *data_sheet)
-{
-  do_sort (data_sheet, SORT_ASCEND);
-}
-
-void
-on_sort_down (GtkAction *action, PsppireDataSheet *data_sheet)
-{
-  do_sort (data_sheet, SORT_DESCEND);
-}
-
-void
-on_edit_goto_case (GtkAction *action, PsppireDataSheet *data_sheet)
-{
-  goto_case_dialog (data_sheet);
-}
-
-void
-on_edit_find (GtkAction *action, PsppireDataSheet *data_sheet)
-{
-  PsppireDataWindow *pdw;
-
-  pdw = psppire_data_window_for_data_store (data_sheet->data_store);
-  g_return_if_fail (pdw != NULL);
-
-  find_dialog (pdw);
-}
-
-void
-on_edit_copy (GtkAction *action, PsppireDataSheet *data_sheet)
-{
-  psppire_data_sheet_set_clip (data_sheet, FALSE);
-}
-
-void
-on_edit_cut (GtkAction *action, PsppireDataSheet *data_sheet)
-{
-  psppire_data_sheet_set_clip (data_sheet, TRUE);
-}
-
-void
-on_edit_paste (GtkAction *action, PsppireDataSheet *data_sheet)
-{
-  GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (data_sheet));
-  GtkClipboard *clipboard =
-    gtk_clipboard_get_for_display (display, GDK_SELECTION_CLIPBOARD);
-
-  gtk_clipboard_request_contents (clipboard,
-                                  gdk_atom_intern ("UTF8_STRING", TRUE),
-                                  psppire_data_sheet_clip_received_cb,
-                                  data_sheet);
-}
-
-static void
-psppire_data_sheet_init (PsppireDataSheet *obj)
-{
-  PsppSheetView *sheet_view = PSPP_SHEET_VIEW (obj);
-  GtkAction *action;
-
-  obj->show_value_labels = FALSE;
-  obj->show_case_numbers = TRUE;
-  obj->may_create_vars = TRUE;
-  obj->may_delete_vars = TRUE;
-
-  obj->owns_primary_selection = FALSE;
-
-  obj->scroll_to_bottom_signal = 0;
-  obj->scroll_to_right_signal = 0;
-  obj->on_owner_change_signal = 0;
-  obj->new_variable_column = NULL;
-  obj->container = NULL;
-
-  obj->uim = NULL;
-  obj->dispose_has_run = FALSE;
-
-  pspp_sheet_view_set_special_cells (sheet_view, PSPP_SHEET_VIEW_SPECIAL_CELLS_YES);
-
-  g_signal_connect (obj, "notify::model",
-                    G_CALLBACK (psppire_data_sheet_model_changed), NULL);
-
-  pspp_sheet_view_set_rubber_banding (sheet_view, TRUE);
-  pspp_sheet_selection_set_mode (pspp_sheet_view_get_selection (sheet_view),
-                                 PSPP_SHEET_SELECTION_RECTANGLE);
-
-  g_object_set (G_OBJECT (obj), "has-tooltip", TRUE, (void *) NULL);
-  g_signal_connect (obj, "query-tooltip",
-                    G_CALLBACK (on_query_tooltip), NULL);
-  g_signal_connect (obj, "button-press-event",
-                    G_CALLBACK (on_button_pressed), NULL);
-  g_signal_connect (obj, "popup-menu", G_CALLBACK (on_popup_menu), NULL);
-
-  obj->builder = builder_new ("data-sheet.ui");
-
-  action = get_action_assert (obj->builder, "edit_clear-cases");
-  g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_cases),
-                    obj);
-  gtk_action_set_sensitive (action, FALSE);
-  g_signal_connect (pspp_sheet_view_get_selection (sheet_view),
-                    "changed", G_CALLBACK (on_selection_changed), NULL);
-
-  action = get_action_assert (obj->builder, "edit_insert-case");
-  g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_case),
-                    obj);
-
-  action = get_action_assert (obj->builder, "edit_insert-variable");
-  g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_variable),
-                    obj);
-
-  action = get_action_assert (obj->builder, "edit_goto-case");
-  g_signal_connect (action, "activate", G_CALLBACK (on_edit_goto_case),
-                    obj);
-
-  action = get_action_assert (obj->builder, "edit_copy");
-  g_signal_connect (action, "activate", G_CALLBACK (on_edit_copy), obj);
-
-  action = get_action_assert (obj->builder, "edit_cut");
-  g_signal_connect (action, "activate", G_CALLBACK (on_edit_cut), obj);
-
-  action = get_action_assert (obj->builder, "edit_paste");
-  g_signal_connect (action, "activate", G_CALLBACK (on_edit_paste), obj);
-
-  action = get_action_assert (obj->builder, "edit_clear-variables");
-  g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_variables),
-                    obj);
-
-  action = get_action_assert (obj->builder, "edit_find");
-  g_signal_connect (action, "activate", G_CALLBACK (on_edit_find), obj);
-
-  action = get_action_assert (obj->builder, "sort-up");
-  g_signal_connect (action, "activate", G_CALLBACK (on_sort_up), obj);
-
-  action = get_action_assert (obj->builder, "sort-down");
-  g_signal_connect (action, "activate", G_CALLBACK (on_sort_down), obj);
-
-}
-
-GtkWidget *
-psppire_data_sheet_new (void)
-{
-  return g_object_new (PSPP_TYPE_DATA_SHEET, NULL);
-}
-
-PsppireDataStore *
-psppire_data_sheet_get_data_store (PsppireDataSheet *data_sheet)
-{
-  return data_sheet->data_store;
-}
-
-static void
-refresh_model (PsppireDataSheet *data_sheet)
-{
-  pspp_sheet_view_set_model (PSPP_SHEET_VIEW (data_sheet), NULL);
-
-  if (data_sheet->data_store != NULL)
-    {
-      PsppireEmptyListStore *model;
-      GtkAction *action;
-      int n_rows;
-
-      n_rows = psppire_data_store_get_case_count (data_sheet->data_store) + 1;
-      model = psppire_empty_list_store_new (n_rows);
-      pspp_sheet_view_set_model (PSPP_SHEET_VIEW (data_sheet),
-                                 GTK_TREE_MODEL (model));
-      g_object_unref (model);
-
-      action = get_action_assert (data_sheet->builder, "edit_copy");
-      g_signal_connect (action, "activate", G_CALLBACK (on_edit_copy),
-                        data_sheet);
-    }
-}
-
-static void
-on_case_inserted (PsppireDataStore *data_store, gint row,
-                  PsppireDataSheet *data_sheet)
-{
-  PsppireEmptyListStore *empty_list_store;
-  GtkTreeModel *tree_model;
-  gint n_rows;
-
-  g_return_if_fail (data_store == data_sheet->data_store);
-
-  n_rows = psppire_data_store_get_case_count (data_store) + 1;
-  if (row == n_rows - 1)
-    row++;
-
-  tree_model = pspp_sheet_view_get_model (PSPP_SHEET_VIEW (data_sheet));
-  empty_list_store = PSPPIRE_EMPTY_LIST_STORE (tree_model);
-  psppire_empty_list_store_set_n_rows (empty_list_store, n_rows);
-  psppire_empty_list_store_row_inserted (empty_list_store, row);
-}
-
-static void
-on_cases_deleted (PsppireDataStore *data_store, gint first, gint n_cases,
-                  PsppireDataSheet *data_sheet)
-{
-
-  g_return_if_fail (data_store == data_sheet->data_store);
-
-  if (n_cases > 1)
-    {
-      /* This is a bit of a cop-out.  We could do better, if it ever turns out
-         that this performs too poorly. */
-      refresh_model (data_sheet);
-    }
-  else
-    {
-      PsppireEmptyListStore *empty_list_store;
-      GtkTreeModel *tree_model;
-      gint n_rows = psppire_data_store_get_case_count (data_store) + 1;
-
-      tree_model = pspp_sheet_view_get_model (PSPP_SHEET_VIEW (data_sheet));
-      empty_list_store = PSPPIRE_EMPTY_LIST_STORE (tree_model);
-      psppire_empty_list_store_set_n_rows (empty_list_store, n_rows);
-      psppire_empty_list_store_row_deleted (empty_list_store, first);
-    }
-}
-
-static void
-on_case_change (PsppireDataStore *data_store, gint row,
-                PsppireDataSheet *data_sheet)
-{
-  PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
-
-  pspp_sheet_view_stop_editing (sheet_view, TRUE);
-  gtk_widget_queue_draw (GTK_WIDGET (data_sheet));
-}
-
-static void
-on_backend_changed (PsppireDataStore *data_store,
-                    PsppireDataSheet *data_sheet)
-{
-  g_return_if_fail (data_store == data_sheet->data_store);
-  refresh_model (data_sheet);
-}
-
-static void
-on_variable_display_width_changed (PsppireDict *dict, int dict_index,
-                                   PsppireDataSheet *data_sheet)
-{
-  PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
-  PsppSheetViewColumn *column;
-  struct variable *var;
-  int display_width;
-  gint pixel_width;
-
-  g_return_if_fail (data_sheet->data_store != NULL);
-  g_return_if_fail (dict == data_sheet->data_store->dict);
-
-  column = psppire_data_sheet_find_column_for_variable (data_sheet,
-                                                        dict_index);
-  if (column == NULL)
-    return;
-
-  var = psppire_dict_get_variable (data_store->dict, dict_index);
-  g_return_if_fail (var != NULL);
-
-  pixel_width = pspp_sheet_view_column_get_fixed_width (column);
-  display_width = display_width_from_pixel_width (data_sheet, pixel_width);
-  if (display_width != var_get_display_width (var))
-    {
-      gint base_width, incr_width;
-
-      display_width = var_get_display_width (var);
-      calc_width_conversion (data_sheet, &base_width, &incr_width);
-      pixel_width = display_width_to_pixel_width (data_sheet, display_width,
-                                                  base_width, incr_width);
-      pspp_sheet_view_column_set_fixed_width (column, pixel_width);
-    }
-}
-
-static void
-on_variable_changed (PsppireDict *dict, int dict_index,
-                    guint what, const struct variable *oldvar,
-                     PsppireDataSheet *data_sheet)
-{
-  PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
-  PsppSheetViewColumn *column;
-  GtkCellRenderer *cell;
-  struct variable *var;
-  GList *cells;
-  char *name;
-
-  g_return_if_fail (data_sheet->data_store != NULL);
-  g_return_if_fail (dict == data_sheet->data_store->dict);
-
-
-  if (what & VAR_TRAIT_DISPLAY_WIDTH)
-    on_variable_display_width_changed (dict, dict_index, data_sheet);
-
-  column = psppire_data_sheet_find_column_for_variable (data_sheet,
-                                                        dict_index);
-  if (column == NULL)
-    return;
-
-
-  var = psppire_dict_get_variable (data_store->dict, dict_index);
-  g_return_if_fail (var != NULL);
-
-  name = escape_underscores (var_get_name (var));
-  if (strcmp (name, pspp_sheet_view_column_get_title (column)))
-    pspp_sheet_view_column_set_title (column, name);
-  free (name);
-
-  cells = pspp_sheet_view_column_get_cell_renderers (column);
-  g_return_if_fail (cells);
-  cell = cells->data;
-  g_list_free (cells);
-
-  if (var_has_value_labels (var) != GTK_IS_CELL_RENDERER_COMBO (cell))
-    {
-      /* Stop editing before we delete and replace the cell renderers.
-         Otherwise if this column is currently being edited, an eventual call
-         to pspp_sheet_view_stop_editing() will obtain a NULL cell and pass
-         that to gtk_cell_renderer_stop_editing(), which causes a critical.
-
-         It's possible that this is a bug in PsppSheetView, and it's possible
-         that PsppSheetView inherits that from GtkTreeView, but I haven't
-         investigated yet. */
-      pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (data_sheet), TRUE);
-
-      add_data_column_cell_renderer (data_sheet, column);
-    }
-}
-
-static void
-on_variable_inserted (PsppireDict *dict, int var_index,
-                      PsppireDataSheet *data_sheet)
-{
-  PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
-  gint base_width, incr_width;
-  PsppSheetViewColumn *column;
-
-  calc_width_conversion (data_sheet, &base_width, &incr_width);
-  column = make_data_column (data_sheet, var_index, base_width, incr_width);
-  pspp_sheet_view_insert_column (sheet_view, column, var_index + 1);
-}
-
-static void
-on_variable_deleted (PsppireDict *dict,
-                     const struct variable *var, int case_idx, int width,
-                     PsppireDataSheet *data_sheet)
-{
-  PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
-  GList *columns, *iter;
-
-  columns = pspp_sheet_view_get_columns (sheet_view);
-  for (iter = columns; iter != NULL; iter = iter->next)
-    {
-      PsppSheetViewColumn *column = iter->data;
-      const struct variable *column_var;
-
-      column_var = g_object_get_data (G_OBJECT (column), "variable");
-      if (column_var == var)
-        pspp_sheet_view_remove_column (sheet_view, column);
-    }
-  g_list_free (columns);
-}
-
-static void
-psppire_data_sheet_unset_data_store (PsppireDataSheet *data_sheet)
-{
-  PsppireDataStore *store = data_sheet->data_store;
-
-  if (store == NULL)
-    return;
-
-  data_sheet->data_store = NULL;
-
-  g_signal_handlers_disconnect_by_func (
-    store, G_CALLBACK (on_backend_changed), data_sheet);
-  g_signal_handlers_disconnect_by_func (
-    store, G_CALLBACK (on_case_inserted), data_sheet);
-  g_signal_handlers_disconnect_by_func (
-    store, G_CALLBACK (on_cases_deleted), data_sheet);
-  g_signal_handlers_disconnect_by_func (
-    store, G_CALLBACK (on_case_change), data_sheet);
-
-  g_signal_handlers_disconnect_by_func (
-    store->dict, G_CALLBACK (on_variable_changed), data_sheet);
-  g_signal_handlers_disconnect_by_func (
-    store->dict, G_CALLBACK (on_variable_display_width_changed), data_sheet);
-  g_signal_handlers_disconnect_by_func (
-    store->dict, G_CALLBACK (on_variable_inserted), data_sheet);
-  g_signal_handlers_disconnect_by_func (
-    store->dict, G_CALLBACK (on_variable_deleted), data_sheet);
-
-  g_object_unref (store);
-}
-
-void
-psppire_data_sheet_set_data_store (PsppireDataSheet *data_sheet,
-                                PsppireDataStore *data_store)
-{
-  psppire_data_sheet_unset_data_store (data_sheet);
-
-  data_sheet->data_store = data_store;
-  if (data_store != NULL)
-    {
-      g_object_ref (data_store);
-      g_signal_connect (data_store, "backend-changed",
-                        G_CALLBACK (on_backend_changed), data_sheet);
-      g_signal_connect (data_store, "case-inserted",
-                        G_CALLBACK (on_case_inserted), data_sheet);
-      g_signal_connect (data_store, "cases-deleted",
-                        G_CALLBACK (on_cases_deleted), data_sheet);
-      g_signal_connect (data_store, "case-changed",
-                        G_CALLBACK (on_case_change), data_sheet);
-
-      /* XXX it's unclean to hook into the dict this way--what if the dict
-         changes?  As of this writing, though, nothing ever changes the
-         data_store's dict. */
-      g_signal_connect (data_store->dict, "variable-changed",
-                        G_CALLBACK (on_variable_changed),
-                        data_sheet);
-      g_signal_connect (data_store->dict, "variable-inserted",
-                        G_CALLBACK (on_variable_inserted), data_sheet);
-      g_signal_connect (data_store->dict, "variable-deleted",
-                        G_CALLBACK (on_variable_deleted), data_sheet);
-    }
-  refresh_model (data_sheet);
-}
-\f
-/* Clipboard stuff */
-
-/* A casereader and dictionary holding the data currently in the clip */
-static struct casereader *clip_datasheet = NULL;
-static struct dictionary *clip_dict = NULL;
-
-
-static void psppire_data_sheet_update_clipboard (PsppireDataSheet *);
-
-static gboolean
-psppire_data_sheet_fetch_clip (PsppireDataSheet *data_sheet, gboolean cut,
-                               struct casereader **readerp,
-                               struct dictionary **dictp)
-{
-  struct casewriter *writer ;
-  PsppireDataStore *ds = psppire_data_sheet_get_data_store (data_sheet);
-  struct case_map *map = NULL;
-  struct range_set *rows, *cols;
-  const struct range_set_node *node;
-  struct dictionary *dict;
-
-  if (!psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols))
-    {
-      *readerp = NULL;
-      *dictp = NULL;
-      return FALSE;
-    }
-
-  /* Construct clip dictionary. */
-  *dictp = dict = dict_create (dict_get_encoding (ds->dict->dict));
-  RANGE_SET_FOR_EACH (node, cols)
-    {
-      int dict_index;
-
-      for (dict_index = node->start; dict_index < node->end; dict_index++)
-        {
-          struct variable *var = dict_get_var (ds->dict->dict, dict_index);
-          dict_clone_var_assert (dict, var);
-        }
-    }
-
-  /* Construct clip data. */
-  map = case_map_by_name (ds->dict->dict, dict);
-  writer = autopaging_writer_create (dict_get_proto (dict));
-  RANGE_SET_FOR_EACH (node, rows)
-    {
-      unsigned long int row;
-
-      for (row = node->start; row < node->end; row++)
-        {
-          struct ccase *old = psppire_data_store_get_case (ds, row);
-          if (old != NULL)
-            casewriter_write (writer, case_map_execute (map, old));
-          else
-            casewriter_force_error (writer);
-        }
-    }
-  case_map_destroy (map);
-
-  /* Clear data that we copied out, if we're doing a "cut". */
-  if (cut && !casewriter_error (writer))
-    {
-      RANGE_SET_FOR_EACH (node, rows)
-        {
-          unsigned long int row;
-
-          for (row = node->start; row < node->end; row++)
-            {
-              const struct range_set_node *node2;
-
-              RANGE_SET_FOR_EACH (node2, cols)
-                {
-                  int dict_index;
-
-                  for (dict_index = node2->start; dict_index < node2->end;
-                       dict_index++)
-                    {
-                      struct variable *var;
-
-                      var = dict_get_var (ds->dict->dict, dict_index);
-                      psppire_data_store_set_string (ds, "", row,
-                                                     var, false);
-                    }
-                }
-            }
-        }
-    }
-
-  range_set_destroy (rows);
-  range_set_destroy (cols);
-
-  *readerp = casewriter_make_reader (writer);
-
-  return TRUE;
-}
-
-/* Set the clip from the currently selected range in DATA_SHEET.  If CUT is
-   true, clears the original data from DATA_SHEET, otherwise leaves the
-   original data in-place. */
-static void
-psppire_data_sheet_set_clip (PsppireDataSheet *data_sheet,
-                             gboolean cut)
-{
-  struct casereader *reader;
-  struct dictionary *dict;
-
-  if (psppire_data_sheet_fetch_clip (data_sheet, cut, &reader, &dict))
-    {
-      casereader_destroy (clip_datasheet);
-      dict_destroy (clip_dict);
-
-      clip_datasheet = reader;
-      clip_dict = dict;
-
-      psppire_data_sheet_update_clipboard (data_sheet);
-    }
-}
-
-enum {
-  SELECT_FMT_NULL,
-  SELECT_FMT_TEXT,
-  SELECT_FMT_HTML
-};
-
-
-/* Perform data_out for case CC, variable V, appending to STRING */
-static void
-data_out_g_string (GString *string, const struct variable *v,
-                  const struct ccase *cc)
-{
-  const struct fmt_spec *fs = var_get_print_format (v);
-  const union value *val = case_data (cc, v);
-
-  char *s = data_out (val, var_get_encoding (v), fs);
-
-  g_string_append (string, s);
-
-  g_free (s);
-}
-
-static GString *
-clip_to_text (struct casereader *datasheet, struct dictionary *dict)
-{
-  casenumber r;
-  GString *string;
-
-  const size_t val_cnt = caseproto_get_n_widths (casereader_get_proto (datasheet));
-  const casenumber case_cnt = casereader_get_case_cnt (datasheet);
-  const size_t var_cnt = dict_get_var_cnt (dict);
-
-  string = g_string_sized_new (10 * val_cnt * case_cnt);
-
-  for (r = 0 ; r < case_cnt ; ++r )
-    {
-      int c;
-      struct ccase *cc;
-
-      cc = casereader_peek (datasheet, r);
-      if (cc == NULL)
-       {
-         g_warning ("Clipboard seems to have inexplicably shrunk");
-         break;
-       }
-
-      for (c = 0 ; c < var_cnt ; ++c)
-       {
-         const struct variable *v = dict_get_var (dict, c);
-         data_out_g_string (string, v, cc);
-         if ( c < val_cnt - 1 )
-           g_string_append (string, "\t");
-       }
-
-      if ( r < case_cnt)
-       g_string_append (string, "\n");
-
-      case_unref (cc);
-    }
-
-  return string;
-}
-
-
-static GString *
-clip_to_html (struct casereader *datasheet, struct dictionary *dict)
-{
-  casenumber r;
-  GString *string;
-
-  const size_t val_cnt = caseproto_get_n_widths (casereader_get_proto (datasheet));
-  const casenumber case_cnt = casereader_get_case_cnt (datasheet);
-  const size_t var_cnt = dict_get_var_cnt (dict);
-
-  /* Guestimate the size needed */
-  string = g_string_sized_new (80 + 20 * val_cnt * case_cnt);
-
-  g_string_append (string,
-                  "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n");
-
-  g_string_append (string, "<table>\n");
-  for (r = 0 ; r < case_cnt ; ++r )
-    {
-      int c;
-      struct ccase *cc = casereader_peek (datasheet, r);
-      if (cc == NULL)
-       {
-         g_warning ("Clipboard seems to have inexplicably shrunk");
-         break;
-       }
-      g_string_append (string, "<tr>\n");
-
-      for (c = 0 ; c < var_cnt ; ++c)
-       {
-         const struct variable *v = dict_get_var (dict, c);
-         g_string_append (string, "<td>");
-         data_out_g_string (string, v, cc);
-         g_string_append (string, "</td>\n");
-       }
-
-      g_string_append (string, "</tr>\n");
-
-      case_unref (cc);
-    }
-  g_string_append (string, "</table>\n");
-
-  return string;
-}
-
-
-
-static void
-psppire_data_sheet_clipboard_set (GtkSelectionData *selection_data,
-                                  guint             info,
-                                  struct casereader *reader,
-                                  struct dictionary *dict)
-{
-  GString *string = NULL;
-
-  switch (info)
-    {
-    case SELECT_FMT_TEXT:
-      string = clip_to_text (reader, dict);
-      break;
-    case SELECT_FMT_HTML:
-      string = clip_to_html (reader, dict);
-      break;
-    default:
-      g_assert_not_reached ();
-    }
-
-  gtk_selection_data_set (selection_data, gtk_selection_data_get_target (selection_data),
-                         8,
-                         (const guchar *) string->str, string->len);
-
-  g_string_free (string, TRUE);
-}
-
-static void
-psppire_data_sheet_clipboard_get_cb (GtkClipboard     *clipboard,
-                                     GtkSelectionData *selection_data,
-                                     guint             info,
-                                     gpointer          data)
-{
-  psppire_data_sheet_clipboard_set (selection_data, info,
-                                    clip_datasheet, clip_dict);
-}
-
-static void
-psppire_data_sheet_clipboard_clear_cb (GtkClipboard *clipboard,
-                                       gpointer data)
-{
-  dict_destroy (clip_dict);
-  clip_dict = NULL;
-
-  casereader_destroy (clip_datasheet);
-  clip_datasheet = NULL;
-}
-
-
-static const GtkTargetEntry targets[] = {
-  { "UTF8_STRING",   0, SELECT_FMT_TEXT },
-  { "STRING",        0, SELECT_FMT_TEXT },
-  { "TEXT",          0, SELECT_FMT_TEXT },
-  { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
-  { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
-  { "text/plain",    0, SELECT_FMT_TEXT },
-  { "text/html",     0, SELECT_FMT_HTML }
-};
-
-
-
-static void
-psppire_data_sheet_update_clipboard (PsppireDataSheet *sheet)
-{
-  GtkClipboard *clipboard =
-    gtk_widget_get_clipboard (GTK_WIDGET (sheet),
-                             GDK_SELECTION_CLIPBOARD);
-
-  if (!gtk_clipboard_set_with_owner (clipboard, targets,
-                                    G_N_ELEMENTS (targets),
-                                    psppire_data_sheet_clipboard_get_cb,
-                                     psppire_data_sheet_clipboard_clear_cb,
-                                    G_OBJECT (sheet)))
-    psppire_data_sheet_clipboard_clear_cb (clipboard, sheet);
-}
-
-static void
-psppire_data_sheet_update_clip_actions (PsppireDataSheet *data_sheet)
-{
-  struct range_set *rows, *cols;
-  GtkAction *action;
-  gboolean enable;
-
-  enable = psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols);
-  if (enable)
-    {
-      range_set_destroy (rows);
-      range_set_destroy (cols);
-    }
-
-  action = get_action_assert (data_sheet->builder, "edit_copy");
-  gtk_action_set_sensitive (action, enable);
-
-  action = get_action_assert (data_sheet->builder, "edit_cut");
-  gtk_action_set_sensitive (action, enable);
-}
-
-static void
-psppire_data_sheet_primary_get_cb (GtkClipboard     *clipboard,
-                                   GtkSelectionData *selection_data,
-                                   guint             info,
-                                   gpointer          data)
-{
-  PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (data);
-  struct casereader *reader;
-  struct dictionary *dict;
-
-  if (psppire_data_sheet_fetch_clip (data_sheet, FALSE, &reader, &dict))
-    {
-      psppire_data_sheet_clipboard_set (selection_data, info,
-                                        reader, dict);
-      casereader_destroy (reader);
-      dict_destroy (dict);
-    }
-}
-
-static void
-psppire_data_sheet_update_primary_selection (PsppireDataSheet *data_sheet,
-                                             gboolean should_own)
-{
-  GtkClipboard *clipboard;
-  GdkDisplay *display;
-
-  display = gtk_widget_get_display (GTK_WIDGET (data_sheet));
-  clipboard = gtk_clipboard_get_for_display (display, GDK_SELECTION_PRIMARY);
-  g_return_if_fail (clipboard != NULL);
-
-  if (data_sheet->owns_primary_selection && !should_own)
-    {
-      data_sheet->owns_primary_selection = FALSE;
-      gtk_clipboard_clear (clipboard);
-    }
-  else if (should_own)
-    data_sheet->owns_primary_selection =
-      gtk_clipboard_set_with_owner (clipboard, targets, G_N_ELEMENTS (targets),
-                                    psppire_data_sheet_primary_get_cb,
-                                    NULL, G_OBJECT (data_sheet));
-}
-\f
-/* A callback for when the clipboard contents have been received. */
-static void
-psppire_data_sheet_clip_received_cb (GtkClipboard *clipboard,
-                                     GtkSelectionData *sd,
-                                     gpointer data)
-{
-  PsppireDataSheet *data_sheet = data;
-  PsppireDataStore *store = data_sheet->data_store;
-  struct range_set *rows, *cols;
-  gint count = 0;
-  gint next_row, next_column;
-  gint first_column;
-  char *c;
-
-  if ( gtk_selection_data_get_length (sd) < 0 )
-    return;
-
-  if ( gtk_selection_data_get_data_type (sd) != gdk_atom_intern ("UTF8_STRING", FALSE))
-    return;
-
-  c = (char *) gtk_selection_data_get_data (sd);
-
-  /* Get the starting selected position in the data sheet.  (Possibly we should
-     only paste into the selected range if it's larger than one cell?) */
-  if (!psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols))
-    return;
-  next_row = range_set_first (rows)->start;
-  first_column = next_column = range_set_first (cols)->start;
-  range_set_destroy (rows);
-  range_set_destroy (cols);
-
-  g_return_if_fail (next_row >= 0);
-  g_return_if_fail (next_column >= 0);
-
-  while (count < gtk_selection_data_get_length (sd))
-    {
-      gint row = next_row;
-      gint column = next_column;
-      struct variable *var;
-      char *s = c;
-
-      while (*c != '\t' && *c != '\n' && count < gtk_selection_data_get_length (sd))
-        {
-          c++;
-          count++;
-        }
-      if ( *c == '\t')
-        {
-          next_row = row ;
-          next_column = column + 1;
-        }
-      else if ( *c == '\n')
-        {
-          next_row = row + 1;
-          next_column = first_column;
-        }
-      *c++ = '\0';
-      count++;
-
-      var = psppire_dict_get_variable (store->dict, column);
-      if (var != NULL)
-        psppire_data_store_set_string (store, s, row, var, FALSE);
-    }
-}
-
-static void
-psppire_data_sheet_targets_received_cb (GtkClipboard *clipboard,
-                                        GdkAtom *atoms,
-                                        gint n_atoms,
-                                        gpointer data)
-{
-  GtkAction *action = GTK_ACTION (data);
-  gboolean compatible_target;
-  gint i;
-
-  compatible_target = FALSE;
-  for (i = 0; i < G_N_ELEMENTS (targets); i++)
-    {
-      GdkAtom target = gdk_atom_intern (targets[i].target, TRUE);
-      gint j;
-
-      for (j = 0; j < n_atoms; j++)
-        if (target == atoms[j])
-          {
-            compatible_target = TRUE;
-            break;
-          }
-    }
-
-  gtk_action_set_sensitive (action, compatible_target);
-  g_object_unref (action);
-}
-
-static void
-on_owner_change (GtkClipboard *clip, GdkEventOwnerChange *event, gpointer data)
-{
-  PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (data);
-  GtkAction *action = get_action_assert (data_sheet->builder, "edit_paste");
-
-  g_object_ref (action);
-  gtk_clipboard_request_targets (clip, psppire_data_sheet_targets_received_cb,
-                                 action);
-}
index 8497e2c1155ff23a5bc5cc1e5b3bdaa76433c5c0..05fa69c7b1aca64b80a6b59d37c9c1c363b69ca9 100644 (file)
@@ -17,6 +17,8 @@
 #ifndef PSPPIRE_DATA_SHEET_H
 #define PSPPIRE_DATA_SHEET_H 1
 
+#error "Do not include this file"
+
 /* PsppireDataSheet is a PsppSheetView that displays the data in a dataset,
    with one column per variable and one row per case.
 
index 8832e5175e73160989175ff255be8c9dc73af7c7..74677259f72ed65d0ab03744f810681ab82cac7f 100644 (file)
@@ -67,7 +67,7 @@ static GObjectClass *parent_class = NULL;
 
 enum
   {
-    BACKEND_CHANGED,
+    ITEMS_CHANGED,
     CASES_DELETED,
     CASE_INSERTED,
     CASE_CHANGED,
@@ -76,6 +76,122 @@ enum
 
 static guint signals [n_SIGNALS];
 
+static gint
+__tree_model_iter_n_children (GtkTreeModel *tree_model,
+                            GtkTreeIter *iter)
+{
+  PsppireDataStore *store  = PSPPIRE_DATA_STORE (tree_model);
+
+  gint n =  datasheet_get_n_rows (store->datasheet);
+
+  return n;
+}
+
+static GtkTreeModelFlags
+__tree_model_get_flags (GtkTreeModel *model)
+{
+  g_return_val_if_fail (PSPPIRE_IS_DATA_STORE (model), (GtkTreeModelFlags) 0);
+
+  return GTK_TREE_MODEL_LIST_ONLY;
+}
+
+static gint
+__tree_model_get_n_columns (GtkTreeModel *tree_model)
+{
+  PsppireDataStore *store  = PSPPIRE_DATA_STORE (tree_model);
+
+  return psppire_dict_get_value_cnt (store->dict);
+}
+
+
+static gboolean
+__iter_nth_child (GtkTreeModel *tree_model,
+                 GtkTreeIter *iter,
+                 GtkTreeIter *parent,
+                 gint n)
+{
+  PsppireDataStore *store  = PSPPIRE_DATA_STORE (tree_model);
+  
+  g_assert (parent == NULL);
+
+  g_return_val_if_fail (store, FALSE);
+  g_return_val_if_fail (store->datasheet, FALSE);
+
+  if (n >= datasheet_get_n_rows (store->datasheet))
+    {
+      iter->stamp = -1;
+      iter->user_data = NULL;
+      return FALSE;
+    }
+  
+  iter->user_data = n;
+  return TRUE;
+}
+
+
+
+static void
+__get_value (GtkTreeModel *tree_model,
+            GtkTreeIter *iter,
+            gint column,
+            GValue *value)
+{
+  PsppireDataStore *store  = PSPPIRE_DATA_STORE (tree_model);
+
+  const struct variable *variable = psppire_dict_get_variable (store->dict, column);
+
+  if (var_is_numeric (variable))
+    g_value_init (value, G_TYPE_DOUBLE);
+  else
+    g_value_init (value, G_TYPE_STRING);
+
+  gint row = GPOINTER_TO_INT (iter->user_data);
+
+  struct ccase *cc = datasheet_get_row (store->datasheet, row);
+  
+  if (var_is_numeric (variable))
+    g_value_set_double (value, case_data_idx (cc, column)->f);
+  else
+    {
+      const gchar *ss = value_str (case_data_idx (cc, column),
+                            var_get_width (variable));
+      g_value_set_string (value, ss);
+    }
+  case_unref (cc);
+}
+
+
+static GType
+__get_type (GtkTreeModel *tree_model,   gint idx)
+{
+  PsppireDataStore *store  = PSPPIRE_DATA_STORE (tree_model);
+
+  const struct variable *variable = psppire_dict_get_variable (store->dict, idx);
+
+  if (var_is_numeric (variable))
+    return G_TYPE_DOUBLE;
+
+  return G_TYPE_STRING;
+}
+
+static void
+__tree_model_init (GtkTreeModelIface *iface)
+{
+  iface->get_flags       = __tree_model_get_flags;
+  iface->get_n_columns   = __tree_model_get_n_columns ;
+  iface->get_column_type = __get_type;
+  iface->get_iter        = NULL; 
+  iface->iter_next       = NULL; 
+  iface->get_path        = NULL; 
+  iface->get_value       = __get_value;
+
+  iface->iter_children   = NULL; 
+  iface->iter_has_child  = NULL; 
+  iface->iter_n_children = __tree_model_iter_n_children;
+  iface->iter_nth_child  = __iter_nth_child;
+  iface->iter_parent     = NULL; 
+}
+
 
 GType
 psppire_data_store_get_type (void)
@@ -97,9 +213,18 @@ psppire_data_store_get_type (void)
         (GInstanceInitFunc) psppire_data_store_init,
       };
 
+      static const GInterfaceInfo tree_model_info = {
+       (GInterfaceInitFunc) __tree_model_init,
+       NULL,
+       NULL
+      };
+
       data_store_type = g_type_register_static (G_TYPE_OBJECT,
                                                "PsppireDataStore",
                                                &data_store_info, 0);
+
+      g_type_add_interface_static (data_store_type, GTK_TYPE_TREE_MODEL,
+                                  &tree_model_info);
     }
 
   return data_store_type;
@@ -117,15 +242,18 @@ psppire_data_store_class_init (PsppireDataStoreClass *class)
   object_class->finalize = psppire_data_store_finalize;
   object_class->dispose = psppire_data_store_dispose;
 
-  signals [BACKEND_CHANGED] =
-    g_signal_new ("backend-changed",
+    signals [ITEMS_CHANGED] =
+    g_signal_new ("changed",
                  G_TYPE_FROM_CLASS (class),
                  G_SIGNAL_RUN_FIRST,
                  0,
                  NULL, NULL,
-                 g_cclosure_marshal_VOID__VOID,
+                 psppire_marshal_VOID__UINT_UINT_UINT,
                  G_TYPE_NONE,
-                 0);
+                 3,
+                 G_TYPE_UINT,
+                 G_TYPE_UINT,
+                 G_TYPE_UINT);
 
   signals [CASE_INSERTED] =
     g_signal_new ("case-inserted",
@@ -292,12 +420,17 @@ psppire_data_store_set_reader (PsppireDataStore *ds,
                               struct casereader *reader)
 {
   gint i;
-
+  gint old_n = 0;
   if ( ds->datasheet)
-    datasheet_destroy (ds->datasheet);
+    {
+      old_n = datasheet_get_n_rows (ds->datasheet);
+      datasheet_destroy (ds->datasheet);
+    }
 
   ds->datasheet = datasheet_create (reader);
 
+  gint new_n = datasheet_get_n_rows (ds->datasheet);
+  
   if ( ds->dict )
     for (i = 0 ; i < n_dict_signals; ++i )
       {
@@ -308,7 +441,7 @@ psppire_data_store_set_reader (PsppireDataStore *ds,
          }
       }
 
-  g_signal_emit (ds, signals[BACKEND_CHANGED], 0);
+  g_signal_emit (ds, signals[ITEMS_CHANGED], 0, 0, old_n, new_n);
 }
 
 
index 7917274da56a619284018ef89cc5f970d06db4c9..c2a27c04f4f53c974d0ebf8bc43d27a26e2f2abe 100644 (file)
@@ -1009,7 +1009,7 @@ psppire_data_window_finish_init (PsppireDataWindow *de,
                    G_CALLBACK (on_split_change),
                    de);
 
-  g_signal_connect_swapped (de->dict, "backend-changed",
+  g_signal_connect_swapped (de->dict, "changed",
                             G_CALLBACK (enable_save), de);
   g_signal_connect_swapped (de->dict, "variable-inserted",
                             G_CALLBACK (enable_save), de);
index b88bbe7766300a6a110bd211902c5e5aa95691c7..53ba83aa1d62aaa1909a5aea246197c9a424cb2e 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPPIRE - a graphical user interface for PSPP.
-   Copyright (C) 2004, 2006, 2007, 2009, 2010, 2011, 2012  Free Software Foundation
+   Copyright (C) 2004, 2006, 2007, 2009, 2010, 2011, 2012, 2016  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
 #include "ui/gui/psppire-marshal.h"
 #include "ui/gui/psppire-var-ptr.h"
 
+#include "ui/gui/efficient-sheet/jmd-datum.h"
+
+#include <gobject/genums.h>
+
 #include <gettext.h>
 #define _(msgid) gettext (msgid)
 #define N_(msgid) msgid
 
+
+
+GType align_enum_type;
+GType measure_enum_type;
+GType role_enum_type;
+
+
 enum  {
-  BACKEND_CHANGED,
+  ITEMS_CHANGED,
 
   VARIABLE_CHANGED,
   VARIABLE_INSERTED,
@@ -59,6 +70,53 @@ static void psppire_dict_dispose     (GObject                *object);
 static void dictionary_tree_model_init (GtkTreeModelIface *iface);
 
 
+
+static guint
+gni (GListModel *list)
+{
+  PsppireDict *dict = PSPPIRE_DICT (list);
+
+  return psppire_dict_get_var_cnt (dict);
+}
+
+static GType
+git (GListModel *list)
+{
+  return JMD_TYPE_DATUM;
+}
+
+static gpointer
+gi (GListModel *list, guint id)
+{
+  JmdDatum *gd = JMD_DATUM (g_object_new (JMD_TYPE_DATUM, NULL));
+
+  PsppireDict *dict = PSPPIRE_DICT (list);
+  
+  if (id >= psppire_dict_get_var_cnt (dict))
+    {
+      gd->text = g_strdup (_("Var"));
+    }
+  else
+    {
+      const struct variable *v =  psppire_dict_get_variable (dict, id);
+
+      gd->text = g_strdup (var_get_name (v));
+      gd->label = g_strdup (var_get_label (v));
+    }
+
+  return gd;
+}
+
+
+static void
+jmd_init_iface (GListModelInterface *iface)
+{
+  iface->get_n_items = gni;
+  iface->get_item = gi;
+  iface->get_item_type = git;
+}
+
+
 /* --- variables --- */
 static GObjectClass     *parent_class = NULL;
 
@@ -94,12 +152,21 @@ psppire_dict_get_type (void)
        NULL
       };
 
+      static const GInterfaceInfo list_model_info = {
+       (GInterfaceInitFunc) jmd_init_iface,
+       NULL,
+       NULL
+      };
+
       object_type = g_type_register_static (G_TYPE_OBJECT,
                                            "PsppireDict",
                                            &object_info, 0);
-
+      
       g_type_add_interface_static (object_type, GTK_TYPE_TREE_MODEL,
                                   &tree_model_info);
+
+      g_type_add_interface_static (object_type, G_TYPE_LIST_MODEL,
+                                  &list_model_info);
     }
 
   return object_type;
@@ -114,16 +181,19 @@ psppire_dict_class_init (PsppireDictClass *class)
   parent_class = g_type_class_peek_parent (class);
 
   object_class->dispose = psppire_dict_dispose;
-
-  signals [BACKEND_CHANGED] =
-    g_signal_new ("backend-changed",
+  
+  signals [ITEMS_CHANGED] =
+    g_signal_new ("changed",
                  G_TYPE_FROM_CLASS (class),
                  G_SIGNAL_RUN_FIRST,
                  0,
                  NULL, NULL,
-                 g_cclosure_marshal_VOID__VOID,
+                 psppire_marshal_VOID__UINT_UINT_UINT,
                  G_TYPE_NONE,
-                 0);
+                 3,
+                 G_TYPE_UINT,
+                 G_TYPE_UINT,
+                 G_TYPE_UINT);
 
 
   signals [VARIABLE_CHANGED] =
@@ -221,7 +291,10 @@ addcb (struct dictionary *d, int idx, void *pd)
   PsppireDict *dict = PSPPIRE_DICT (pd);
 
   if ( ! dict->disable_insert_signal)
-    g_signal_emit (dict, signals [VARIABLE_INSERTED], 0, idx);
+    {
+      g_signal_emit (dict, signals [VARIABLE_INSERTED], 0, idx);
+      g_signal_emit (dict, signals [ITEMS_CHANGED], 0, idx, 1, 1);
+    }
 }
 
 static void
@@ -230,12 +303,14 @@ delcb (struct dictionary *d, const struct variable *var,
 {
   g_signal_emit (pd, signals [VARIABLE_DELETED], 0,
                  var, dict_idx, case_idx);
+  g_signal_emit (pd, signals [ITEMS_CHANGED], 0, dict_idx, 1, 0);
 }
 
 static void
 mutcb (struct dictionary *d, int idx, unsigned int what, const struct variable *oldvar, void *pd)
 {
   g_signal_emit (pd, signals [VARIABLE_CHANGED], 0, idx, what, oldvar);
+  g_signal_emit (pd, signals [ITEMS_CHANGED], 0, idx, 1, 1);
 }
 
 static void
@@ -284,7 +359,7 @@ psppire_dict_new_from_dict (struct dictionary *d)
 {
   PsppireDict *new_dict = g_object_new (PSPPIRE_TYPE_DICT, NULL);
   new_dict->dict = d;
-
+  
   dict_set_callbacks (new_dict->dict, &gui_callbacks, new_dict);
 
   return new_dict;
@@ -296,6 +371,9 @@ psppire_dict_replace_dictionary (PsppireDict *dict, struct dictionary *d)
 {
   struct variable *var =  dict_get_weight (d);
 
+  guint old_n = dict_get_var_cnt (dict->dict);
+  guint new_n = dict_get_var_cnt (d);
+  
   dict->dict = d;
 
   weight_changed_callback (d, var ? var_get_dict_index (var) : -1, dict);
@@ -307,7 +385,7 @@ psppire_dict_replace_dictionary (PsppireDict *dict, struct dictionary *d)
 
   dict_set_callbacks (dict->dict, &gui_callbacks, dict);
 
-  g_signal_emit (dict, signals [BACKEND_CHANGED], 0);
+  g_signal_emit (dict, signals [ITEMS_CHANGED], 0, 0, old_n, new_n);
 }
 
 
@@ -371,7 +449,8 @@ psppire_dict_insert_variable (PsppireDict *d, gint idx, const gchar *name)
   d->disable_insert_signal = FALSE;
 
   g_signal_emit (d, signals[VARIABLE_INSERTED], 0, idx);
-
+  g_signal_emit (d, signals [ITEMS_CHANGED], 0, idx, 0, 1);
+  
   return var;
 }
 
@@ -617,24 +696,34 @@ tree_model_column_type (GtkTreeModel *model, gint index)
 {
   g_return_val_if_fail (PSPPIRE_IS_DICT (model), (GType) 0);
 
+  GType t = 0;
+  
   switch (index)
     {
     case DICT_TVM_COL_NAME:
-      return G_TYPE_STRING;
+    case DICT_TVM_COL_LABEL:
+      t = G_TYPE_STRING;
+      break;
+    case DICT_TVM_COL_DECIMAL:
+    case DICT_TVM_COL_WIDTH:
+    case DICT_TVM_COL_COLUMNS:
+      t = G_TYPE_INT;
       break;
     case DICT_TVM_COL_VAR:
-      return PSPPIRE_VAR_PTR_TYPE;
+      t = PSPPIRE_VAR_PTR_TYPE;
       break;
-    case DICT_TVM_COL_LABEL:
-      return G_TYPE_STRING;
+    case DICT_TVM_COL_ALIGNMENT:
+      t = align_enum_type;
       break;
-    default:
-      g_return_val_if_reached ((GType)0);
+    case DICT_TVM_COL_MEASURE:
+      t = measure_enum_type;
+      break;
+    case DICT_TVM_COL_ROLE:
+      t = role_enum_type;
       break;
     }
 
-  g_assert_not_reached ();
-  return ((GType)0);
+  return t;
 }
 
 static gboolean
@@ -722,6 +811,7 @@ tree_model_get_path (GtkTreeModel *model, GtkTreeIter *iter)
   return path;
 }
 
+const struct fmt_spec *var_get_write_format (const struct variable *);
 
 static void
 tree_model_get_value (GtkTreeModel *model, GtkTreeIter *iter,
@@ -732,22 +822,51 @@ tree_model_get_value (GtkTreeModel *model, GtkTreeIter *iter,
 
   g_return_if_fail (iter->stamp == dict->stamp);
 
-  var =  iter->user_data;
+  var = iter->user_data;
 
+  const struct fmt_spec *fs = var_get_write_format (var);
+  
   switch (column)
     {
     case DICT_TVM_COL_NAME:
-      {
-       g_value_init (value, G_TYPE_STRING);
-       g_value_set_string (value, var_get_name (var));
-      }
+      g_value_init (value, G_TYPE_STRING);
+      g_value_set_string (value, var_get_name (var));
+      break;
+    case DICT_TVM_COL_WIDTH:
+      g_value_init (value, G_TYPE_INT);
+      g_value_set_int (value, fs->w);
+      break;
+    case DICT_TVM_COL_DECIMAL:
+      g_value_init (value, G_TYPE_INT);
+      g_value_set_int (value, fs->d);
+      break;
+    case DICT_TVM_COL_LABEL:
+      g_value_init (value, G_TYPE_STRING);
+      g_value_set_string (value, var_get_label (var));
+      break;
+    case DICT_TVM_COL_COLUMNS:
+      g_value_init (value, G_TYPE_INT);
+      g_value_set_int (value, var_get_display_width (var));
+      break;
+    case DICT_TVM_COL_ALIGNMENT:
+      g_value_init (value, align_enum_type);
+      g_value_set_enum (value, var_get_alignment (var));
+      break;
+    case DICT_TVM_COL_MEASURE:
+      g_value_init (value, measure_enum_type);
+      g_value_set_enum (value, var_get_measure (var));
+      break;
+    case DICT_TVM_COL_ROLE:
+      g_value_init (value, role_enum_type);
+      g_value_set_enum (value, var_get_role (var));
       break;
     case DICT_TVM_COL_VAR:
       g_value_init (value, PSPPIRE_VAR_PTR_TYPE);
       g_value_set_boxed (value, var);
       break;
     default:
-      g_return_if_reached ();
+      g_value_init (value, G_TYPE_STRING);
+      g_value_set_string (value, "????");
       break;
     }
 }
@@ -766,7 +885,7 @@ tree_model_n_children (GtkTreeModel *model,
 {
   PsppireDict *dict = PSPPIRE_DICT (model);
 
-  if ( iter == NULL )
+  if (iter == NULL)
     return psppire_dict_get_var_cnt (dict);
 
   return 0;
index ecd5de063fbe4d37ec4b4dcc500c2793ce2e99d6..4d78807cba2d97464d9b9a052b8df5904207587d 100644 (file)
@@ -42,7 +42,19 @@ G_BEGIN_DECLS
 typedef struct _PsppireDict       PsppireDict;
 typedef struct _PsppireDictClass PsppireDictClass;
 
-enum {DICT_TVM_COL_NAME=0, DICT_TVM_COL_VAR, DICT_TVM_COL_LABEL, n_DICT_COLS} ;
+enum {DICT_TVM_COL_NAME=0,
+      DICT_TVM_COL_TYPE,
+      DICT_TVM_COL_WIDTH,
+      DICT_TVM_COL_DECIMAL,
+      DICT_TVM_COL_LABEL,
+      DICT_TVM_COL_VALUE_LABELS,
+      DICT_TVM_COL_MISSING_VALUES,
+      DICT_TVM_COL_COLUMNS,
+      DICT_TVM_COL_ALIGNMENT,
+      DICT_TVM_COL_MEASURE,
+      DICT_TVM_COL_ROLE,
+      DICT_TVM_COL_VAR,
+      n_DICT_COLS} ;
 
 struct _PsppireDict
 {
index 1b6cf570afd10b99ff95a2c1c34ccc4fec43694a..d3131a3a2ca795b6dc38965e1d87606988587931 100644 (file)
@@ -1951,200 +1951,6 @@ on_variable_change (PsppireDict *dict, int dict_idx,
 static void
 prepare_formats_page (PsppireImportAssistant *ia)
 {
-  PsppireDict *psppire_dict = NULL;
-  PsppireVarSheet *var_sheet;
-  GtkBin *vars_scroller;
-  GtkWidget *old_var_sheet;
-
-  
-  push_watch_cursor (ia);
-
-  if (ia->spreadsheet == NULL)
-    {
-      struct fmt_guesser *fg;
-      unsigned long int number = 0;
-      size_t column_idx;
-
-      
-      ia->dict = dict_create (get_default_encoding ());
-      fg = fmt_guesser_create ();
-      for (column_idx = 0; column_idx < ia->column_cnt; column_idx++)
-       {
-         struct variable *modified_var = 
-           (column_idx < ia->modified_var_cnt ? ia->modified_vars[column_idx] : NULL);
-
-         if (modified_var == NULL)
-           {
-             struct column *column = &ia->columns[column_idx];
-             struct variable *var;
-             struct fmt_spec format;
-             char *name;
-             size_t row;
-
-             /* Choose variable name. */
-             name = dict_make_unique_var_name (ia->dict, column->name, &number);
-
-             /* Choose variable format. */
-             fmt_guesser_clear (fg);
-             for (row = ia->skip_lines; row < ia->line_cnt; row++)
-               fmt_guesser_add (fg, column->contents[row]);
-             fmt_guesser_guess (fg, &format);
-             fmt_fix_input (&format);
-
-             /* Create variable. */
-             var = dict_create_var_assert (ia->dict, name, fmt_var_width (&format));
-             var_set_both_formats (var, &format);
-
-             free (name);
-           }
-         else
-           {
-             char *name;
-
-             name = dict_make_unique_var_name (ia->dict, var_get_name (modified_var),
-                                               &number);
-             dict_clone_var_as_assert (ia->dict, modified_var, name);
-             free (name);
-           }
-       }
-      fmt_guesser_destroy (fg);
-    }
-  else
-    {
-      int row_start = -1;
-      int row_stop = -1;
-      int col_start = -1;
-      int col_stop = -1;
-
-      GtkBuilder *builder = ia->builder;
-
-      struct casereader *reader = NULL;
-
-      GtkWidget *readnames_checkbox = get_widget_assert (builder, "readnames-checkbox");
-      GtkWidget *range_entry = get_widget_assert (builder, "cell-range-entry");
-      const gchar *range = gtk_entry_get_text (GTK_ENTRY (range_entry));
-      GtkWidget *combo_box = get_widget_assert (builder, "sheet-entry");
-
-      gint num = gtk_combo_box_get_active (GTK_COMBO_BOX (combo_box));
-
-      struct spreadsheet_read_options sro;
-  
-      sro.sheet_name = NULL;
-      sro.cell_range = NULL;
-      sro.sheet_index = num + 1;
-
-      if ( convert_cell_ref (range, &col_start, &row_start, &col_stop, &row_stop))
-       {
-         sro.cell_range = g_strdup (range);
-       }
-
-      sro.read_names = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (readnames_checkbox));
-      sro.asw = -1;
-  
-      switch (ia->spreadsheet->type)
-       {
-       case SPREADSHEET_ODS:
-       case SPREADSHEET_GNUMERIC:
-         {
-           reader = spreadsheet_make_reader (ia->spreadsheet, &sro);
-           ia->dict = dict_clone (ia->spreadsheet->dict);
-         }
-         break;
-       default:
-         g_assert_not_reached ();
-         break;
-       }
-      g_free (sro.cell_range);
-
-      if (reader && ia->dict)
-       {
-         struct ccase *c;
-         int col;
-
-         ia->column_cnt = dict_get_var_cnt (ia->dict);
-         ia->columns = xcalloc (ia->column_cnt, sizeof (*ia->columns));
-         for (col = 0; col < ia->column_cnt ; ++col)
-           {
-             const struct variable *var = dict_get_var (ia->dict, col);
-             ia->columns[col].name = xstrdup (var_get_name (var));
-             ia->columns[col].contents = NULL;
-           }
-
-         casenumber rows = 0;
-         for (; (c = casereader_read (reader)) != NULL; case_unref (c))
-           {
-             rows++;
-             for (col = 0; col < ia->column_cnt ; ++col)
-               {
-                 char *ss;
-                 const struct variable *var = dict_get_var (ia->dict, col);
-             
-                 ia->columns[col].contents = xrealloc (ia->columns[col].contents,
-                                                       sizeof (struct substring) * rows);
-             
-                 ss = data_out (case_data (c, var), dict_get_encoding (ia->dict), 
-                                var_get_print_format (var));
-             
-                 ia->columns[col].contents[rows - 1] = ss_cstr (ss);
-               }
-         
-             if (rows > MAX_PREVIEW_LINES)
-               {
-                 case_unref (c);
-                 break;
-               }
-           }
-         casereader_destroy (reader);
-         ia->line_cnt = rows;
-       }
-      else
-       {
-         GtkWidget * dialog = gtk_message_dialog_new (NULL,
-                                                      GTK_DIALOG_MODAL,
-                                                      GTK_MESSAGE_ERROR,
-                                                      GTK_BUTTONS_CLOSE,
-                                                      _("An error occurred reading the spreadsheet file."));
-
-         gtk_dialog_run (GTK_DIALOG (dialog));
-         gtk_widget_destroy (dialog);
-       }
-    }
-
-  psppire_dict = psppire_dict_new_from_dict (ia->dict);
-  g_signal_connect (psppire_dict, "variable-changed",
-                   G_CALLBACK (on_variable_change), ia);
-  ia->psppire_dict = psppire_dict;
-
-  
-  /* XXX: PsppireVarStore doesn't hold a reference to
-     psppire_dict for now, but it should.  After it does, we
-     should g_object_ref the psppire_dict here, since we also
-     hold a reference via ia->formats->dict. */
-  var_sheet = PSPPIRE_VAR_SHEET (psppire_var_sheet_new ());
-  g_object_set (var_sheet,
-               "dictionary", psppire_dict,
-               "may-create-vars", FALSE,
-               "may-delete-vars", FALSE,
-               "format-use", FMT_FOR_INPUT,
-               "enable-grid-lines", PSPP_SHEET_VIEW_GRID_LINES_BOTH,
-               (void *) NULL);
-
-  vars_scroller = GTK_BIN (get_widget_assert (ia->builder, "vars-scroller"));
-  old_var_sheet = gtk_bin_get_child (GTK_BIN (vars_scroller));
-  if (old_var_sheet != NULL)
-    gtk_container_remove (GTK_CONTAINER (vars_scroller),  old_var_sheet);
-  gtk_container_add (GTK_CONTAINER (vars_scroller), GTK_WIDGET (var_sheet));
-  gtk_widget_show (GTK_WIDGET (var_sheet));
-
-  ia->data_tree_view =
-    GTK_WIDGET (create_data_tree_view (
-                                      FALSE,
-                                      GTK_CONTAINER (get_widget_assert (ia->builder, "data-scroller")),
-                                      ia));
-
-  gtk_widget_show (ia->paste_button);
-
-  pop_watch_cursor (ia);
 }
 
 static void
diff --git a/src/ui/gui/psppire-var-sheet-header.c b/src/ui/gui/psppire-var-sheet-header.c
new file mode 100644 (file)
index 0000000..cd2121f
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+    A candidate replacement for Pspp's sheet
+    Copyright (C) 2016  John Darrington
+
+    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
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+#include <gtk/gtk.h>
+
+#include "psppire-var-sheet-header.h"
+
+#include "efficient-sheet/jmd-axis-model.h"
+#include "efficient-sheet/jmd-datum.h"
+
+enum  {CHANGED,
+       n_SIGNALS};
+
+static guint signals [n_SIGNALS];
+
+static guint
+gni (GListModel *list)
+{
+  return 11;
+}
+
+static GType
+git (GListModel *list)
+{
+  return JMD_TYPE_DATUM;
+}
+
+static gpointer
+gi (GListModel *list, guint position)
+{
+  JmdDatum *gd = JMD_DATUM (g_object_new (JMD_TYPE_DATUM, NULL));
+
+  switch (position)
+    {
+    case 0:
+      gd->text = g_strdup ("Name");
+      break;
+    case 1:
+      gd->text = g_strdup ("Type");
+      break;
+    case 2:
+      gd->text = g_strdup ("Width");
+      break;
+    case 3:
+      gd->text = g_strdup ("Decimal");
+      break;
+    case 4:
+      gd->text = g_strdup ("Label");
+      break;
+    case 5:
+      gd->text = g_strdup ("Value Labels");
+      break;
+    case 6:
+      gd->text = g_strdup ("Missing Values");
+      break;
+    case 7:
+      gd->text = g_strdup ("Columns");
+      break;
+    case 8:
+      gd->text = g_strdup ("Align");
+      break;
+    case 9:
+      gd->text = g_strdup ("Measure");
+      break;
+    case 10:
+      gd->text = g_strdup ("Role");
+      break;
+    default:
+      //      g_assert_not_reached ();
+      g_print ("Bug: Request for item %d\n", position);
+      break;
+    }
+
+  return gd;
+}
+
+
+static void
+psppire_init_iface (GListModelInterface *iface)
+{
+  iface->get_n_items = gni;
+  iface->get_item = gi;
+  iface->get_item_type = git;
+}
+
+
+G_DEFINE_TYPE_WITH_CODE (PsppireVarSheetHeader, psppire_var_sheet_header,
+                         G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, psppire_init_iface));
+
+static void
+psppire_var_sheet_header_init (PsppireVarSheetHeader *d)
+{
+}
+
+
+
+static void
+psppire_var_sheet_header_class_init (PsppireVarSheetHeaderClass *dc)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (dc);
+
+  /* This signal is never emitted.  It is just to satisfy the interface. */
+  signals [CHANGED] =
+    g_signal_new ("changed",
+                 G_TYPE_FROM_CLASS (object_class),
+                 G_SIGNAL_RUN_FIRST,
+                 0,
+                 NULL, NULL,
+                 g_cclosure_marshal_VOID__VOID,
+                 G_TYPE_NONE,
+                 0);
+}
+
diff --git a/src/ui/gui/psppire-var-sheet-header.h b/src/ui/gui/psppire-var-sheet-header.h
new file mode 100644 (file)
index 0000000..476faaf
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+    A candidate replacement for Pspp's sheet
+    Copyright (C) 2016  John Darrington
+
+    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
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _PSPPIRE_VAR_SHEET_HEADER_H
+#define _PSPPIRE_VAR_SHEET_HEADER_H
+
+
+G_DECLARE_FINAL_TYPE (PsppireVarSheetHeader, psppire_var_sheet_header, PSPPIRE, VAR_SHEET_HEADER, GObject)
+
+
+struct _PsppireVarSheetHeader
+{
+  GObject parent_instance;
+};
+
+struct _PsppireVarSheetHeaderClass
+{
+  GObjectClass parent_instance;
+};
+
+
+
+#define PSPPIRE_TYPE_VAR_SHEET_HEADER psppire_var_sheet_header_get_type ()
+
+#endif
diff --git a/src/ui/gui/psppire-var-sheet.c b/src/ui/gui/psppire-var-sheet.c
deleted file mode 100644 (file)
index 66a95de..0000000
+++ /dev/null
@@ -1,1654 +0,0 @@
-/* PSPPIRE - a graphical user interface for PSPP.
-   Copyright (C) 2008, 2009, 2011, 2012, 2013, 2014 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
-   the Free Software Foundation, either version 3 of the License, or
-   (at your option) any later version.
-
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
-
-#include <config.h>
-
-#include "ui/gui/psppire-var-sheet.h"
-
-#include "data/format.h"
-#include "data/value-labels.h"
-#include "libpspp/range-set.h"
-#include "ui/gui/builder-wrapper.h"
-#include "ui/gui/helper.h"
-#include "ui/gui/missing-val-dialog.h"
-#include "ui/gui/pspp-sheet-selection.h"
-#include "ui/gui/psppire-cell-renderer-button.h"
-#include "ui/gui/psppire-data-editor.h"
-#include "ui/gui/psppire-data-window.h"
-#include "ui/gui/psppire-dialog-action-var-info.h"
-#include "ui/gui/psppire-dictview.h"
-#include "ui/gui/psppire-empty-list-store.h"
-#include "ui/gui/psppire-marshal.h"
-#include "ui/gui/val-labs-dialog.h"
-#include "ui/gui/var-type-dialog.h"
-#include "ui/gui/var-display.h"
-#include "ui/gui/var-type-dialog.h"
-
-#include "gl/intprops.h"
-
-#include <gettext.h>
-#define _(msgid) gettext (msgid)
-#define N_(msgid) msgid
-
-enum vs_column
-  {
-    VS_NAME,
-    VS_TYPE,
-    VS_WIDTH,
-    VS_DECIMALS,
-    VS_LABEL,
-    VS_VALUES,
-    VS_MISSING,
-    VS_COLUMNS,
-    VS_ALIGN,
-    VS_MEASURE,
-    VS_ROLE
-  };
-
-G_DEFINE_TYPE (PsppireVarSheet, psppire_var_sheet, PSPP_TYPE_SHEET_VIEW);
-
-static void
-set_spin_cell (GtkCellRenderer *cell, int value, int min, int max, int step)
-{
-  char text[INT_BUFSIZE_BOUND (int)];
-  GtkAdjustment *adjust;
-
-  if (max > min)
-    adjust = GTK_ADJUSTMENT (gtk_adjustment_new (value, min, max,
-                                                 step, step, 0.0));
-  else
-    adjust = NULL;
-
-  sprintf (text, "%d", value);
-  g_object_set (cell,
-                "text", text,
-                "adjustment", adjust,
-                "editable", TRUE,
-                NULL);
-}
-
-static void
-error_dialog (GtkWindow *w, gchar *primary_text, gchar *secondary_text)
-{
-  GtkWidget *dialog =
-    gtk_message_dialog_new (w,
-                           GTK_DIALOG_DESTROY_WITH_PARENT,
-                           GTK_MESSAGE_ERROR,
-                           GTK_BUTTONS_CLOSE, "%s", primary_text);
-
-  g_object_set (dialog, "icon-name", "psppicon", NULL);
-
-  gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
-                                           "%s", secondary_text);
-
-  gtk_dialog_run (GTK_DIALOG (dialog));
-
-  gtk_widget_destroy (dialog);
-}
-
-static void
-on_name_column_editing_started (GtkCellRenderer *cell,
-                                GtkCellEditable *editable,
-                                const gchar     *path,
-                                gpointer         user_data)
-{
-  PsppireVarSheet *var_sheet = g_object_get_data (G_OBJECT (cell), "var-sheet");
-  PsppireDict *dict = psppire_var_sheet_get_dictionary (var_sheet);
-
-  g_return_if_fail (var_sheet);
-          g_return_if_fail (dict);
-
-  if (GTK_IS_ENTRY (editable))
-    {
-      GtkEntry *entry = GTK_ENTRY (editable);
-      if (gtk_entry_get_text (entry)[0] == '\0')
-        {
-          gchar name[64];
-          if (psppire_dict_generate_name (dict, name, sizeof name))
-            gtk_entry_set_text (entry, name);
-        }
-    }
-}
-
-static void
-scroll_to_bottom (GtkWidget      *widget,
-                  GtkRequisition *requisition,
-                  gpointer        unused UNUSED)
-{
-  PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (widget);
-  PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
-  GtkAdjustment *vadjust;
-
-  vadjust = pspp_sheet_view_get_vadjustment (sheet_view);
-  gtk_adjustment_set_value (vadjust, gtk_adjustment_get_upper (vadjust));
-
-  if (var_sheet->scroll_to_bottom_signal)
-    {
-      g_signal_handler_disconnect (var_sheet,
-                                   var_sheet->scroll_to_bottom_signal);
-      var_sheet->scroll_to_bottom_signal = 0;
-    }
-}
-
-static struct variable *
-path_string_to_var (PsppireVarSheet *var_sheet, gchar *path_string)
-{
-  GtkTreePath *path;
-  gint row;
-
-  path = gtk_tree_path_new_from_string (path_string);
-  row = gtk_tree_path_get_indices (path)[0];
-  gtk_tree_path_free (path);
-
-  return psppire_dict_get_variable (var_sheet->dict, row);
-}
-
-static void
-on_var_column_edited (GtkCellRendererText *cell,
-                      gchar               *path_string,
-                      gchar               *new_text,
-                      gpointer             user_data)
-{
-  PsppireVarSheet *var_sheet = user_data;
-  GtkWindow *window = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (var_sheet)));
-  struct dictionary *dict = var_sheet->dict->dict;
-  enum vs_column column_id;
-  struct variable *var;
-  int width, decimals;
-
-  column_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (cell),
-                                                  "column-id"));
-
-  var = path_string_to_var (var_sheet, path_string);
-  if (var == NULL)
-    {
-      g_return_if_fail (column_id == VS_NAME);
-
-      if (!dict_id_is_valid (dict, new_text, false))
-        error_dialog (window,
-                      g_strdup (_("Cannot create variable.")),
-                      g_strdup_printf (_("\"%s\" is not a valid variable "
-                                         "name."), new_text));
-      else if (dict_lookup_var (dict, new_text) != NULL)
-        error_dialog (window,
-                      g_strdup (_("Cannot create variable.")),
-                      g_strdup_printf (_("This dictionary already contains "
-                                         "a variable named \"%s\"."),
-                                         new_text));
-      else
-        {
-          dict_create_var (var_sheet->dict->dict, new_text, 0);
-          if (!var_sheet->scroll_to_bottom_signal)
-            {
-              gtk_widget_queue_resize (GTK_WIDGET (var_sheet));
-              var_sheet->scroll_to_bottom_signal =
-                g_signal_connect (var_sheet, "size-allocate",
-                                  G_CALLBACK (scroll_to_bottom), NULL);
-            }
-        }
-
-      return;
-    }
-
-  switch (column_id)
-    {
-    case VS_NAME:
-      if (!dict_id_is_valid (dict, new_text, false))
-        error_dialog (window,
-                      g_strdup (_("Cannot rename variable.")),
-                      g_strdup_printf (_("\"%s\" is not a valid variable "
-                                         "name."), new_text));
-      else if (dict_lookup_var (dict, new_text) != NULL
-               && dict_lookup_var (dict, new_text) != var)
-        error_dialog (window,
-                      g_strdup (_("Cannot rename variable.")),
-                      g_strdup_printf (_("This dictionary already contains "
-                                         "a variable named \"%s\"."),
-                                         new_text));
-      else
-        dict_rename_var (dict, var, new_text);
-      break;
-
-    case VS_TYPE:
-      /* Not reachable. */
-      break;
-
-    case VS_WIDTH:
-      width = atoi (new_text);
-      if (width > 0)
-        {
-          struct fmt_spec format;
-
-          format = *var_get_print_format (var);
-          fmt_change_width (&format, width, var_sheet->format_use);
-          var_set_width (var, fmt_var_width (&format));
-          var_set_both_formats (var, &format);
-        }
-      break;
-
-    case VS_DECIMALS:
-      decimals = atoi (new_text);
-      if (decimals >= 0)
-        {
-          struct fmt_spec format;
-
-          format = *var_get_print_format (var);
-          fmt_change_decimals (&format, decimals, var_sheet->format_use);
-          var_set_print_format (var, &format);
-        }
-      break;
-
-    case VS_LABEL:
-      var_set_label (var, new_text);
-      break;
-
-    case VS_VALUES:
-    case VS_MISSING:
-      break;
-
-    case VS_COLUMNS:
-      width = atoi (new_text);
-      if (width > 0 && width < 2 * MAX_STRING)
-        var_set_display_width (var, width);
-      break;
-
-    case VS_ALIGN:
-      if (!strcmp (new_text, alignment_to_string (ALIGN_LEFT)))
-        var_set_alignment (var, ALIGN_LEFT);
-      else if (!strcmp (new_text, alignment_to_string (ALIGN_CENTRE)))
-        var_set_alignment (var, ALIGN_CENTRE);
-      else if (!strcmp (new_text, alignment_to_string (ALIGN_RIGHT)))
-        var_set_alignment (var, ALIGN_RIGHT);
-      break;
-
-    case VS_MEASURE:
-      if (!strcmp (new_text, measure_to_string (MEASURE_NOMINAL)))
-        var_set_measure (var, MEASURE_NOMINAL);
-      else if (!strcmp (new_text, measure_to_string (MEASURE_ORDINAL)))
-        var_set_measure (var, MEASURE_ORDINAL);
-      else if (!strcmp (new_text, measure_to_string (MEASURE_SCALE)))
-        var_set_measure (var, MEASURE_SCALE);
-      break;
-
-    case VS_ROLE:
-      if (!strcmp (new_text, var_role_to_string (ROLE_INPUT)))
-        var_set_role (var, ROLE_INPUT);
-      else if (!strcmp (new_text, var_role_to_string (ROLE_TARGET)))
-        var_set_role (var, ROLE_TARGET);
-      else if (!strcmp (new_text, var_role_to_string (ROLE_BOTH)))
-        var_set_role (var, ROLE_BOTH);
-      else if (!strcmp (new_text, var_role_to_string (ROLE_NONE)))
-        var_set_role (var, ROLE_NONE);
-      else if (!strcmp (new_text, var_role_to_string (ROLE_PARTITION)))
-        var_set_role (var, ROLE_PARTITION);
-      else if (!strcmp (new_text, var_role_to_string (ROLE_SPLIT)))
-        var_set_role (var, ROLE_SPLIT);
-      break;
-    }
-}
-
-static void
-render_popup_cell (PsppSheetViewColumn *tree_column,
-                   GtkCellRenderer *cell,
-                   GtkTreeModel *model,
-                   GtkTreeIter *iter,
-                   void *user_data)
-{
-  PsppireVarSheet *var_sheet = user_data;
-  gint row;
-
-  row = GPOINTER_TO_INT (iter->user_data);
-  g_object_set (cell,
-                "editable", row < psppire_dict_get_var_cnt (var_sheet->dict),
-                NULL);
-}
-
-const char *
-get_var_align_stock_id (enum alignment alignment)
-{
-  switch (alignment)
-    {
-    case ALIGN_LEFT: return "align-left";
-    case ALIGN_CENTRE: return "align-center";
-    case ALIGN_RIGHT: return "align-right";
-    default:
-      g_return_val_if_reached ("");
-    }
-}
-
-const char *
-get_var_role_stock_id (enum var_role role)
-{
-  switch (role)
-    {
-    case ROLE_INPUT: return "role-input";
-    case ROLE_TARGET: return "role-target";
-    case ROLE_BOTH: return "role-both";
-    case ROLE_NONE: return "role-none";
-    case ROLE_PARTITION: return "role-partition";
-    case ROLE_SPLIT: return "role-split";
-    default:
-      g_return_val_if_reached ("");
-    }
-}
-
-static void
-render_var_cell (PsppSheetViewColumn *tree_column,
-                 GtkCellRenderer *cell,
-                 GtkTreeModel *model,
-                 GtkTreeIter *iter,
-                 void *user_data)
-{
-  PsppireVarSheet *var_sheet = user_data;
-  const struct fmt_spec *print;
-  enum vs_column column_id;
-  struct variable *var;
-  gint row;
-
-  column_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
-                                                  "column-number")) - 1;
-  row = GPOINTER_TO_INT (iter->user_data);
-
-  gtk_cell_renderer_set_visible (cell, true);
-  if (row >= psppire_dict_get_var_cnt (var_sheet->dict))
-    {
-      if (GTK_IS_CELL_RENDERER_TEXT (cell))
-        {
-          g_object_set (cell,
-                        "text", "",
-                        "editable", column_id == VS_NAME,
-                        NULL);
-          if (column_id == VS_WIDTH
-              || column_id == VS_DECIMALS
-              || column_id == VS_COLUMNS)
-            g_object_set (cell, "adjustment", NULL, NULL);
-        }
-      else
-       {
-         gtk_cell_renderer_set_visible (cell, false);
-       }
-      return;
-    }
-
-  var = psppire_dict_get_variable (var_sheet->dict, row);
-
-  print = var_get_print_format (var);
-  switch (column_id)
-    {
-    case VS_NAME:
-      g_object_set (cell,
-                    "text", var_get_name (var),
-                    "editable", TRUE,
-                    NULL);
-      break;
-
-    case VS_TYPE:
-      g_object_set (cell,
-                    "text", fmt_gui_name (print->type),
-                    "editable", FALSE,
-                    NULL);
-      break;
-
-    case VS_WIDTH:
-      set_spin_cell (cell, print->w,
-                     fmt_min_width (print->type, var_sheet->format_use),
-                     fmt_max_width (print->type, var_sheet->format_use),
-                     fmt_step_width (print->type));
-      break;
-
-    case VS_DECIMALS:
-      if (fmt_takes_decimals (print->type))
-        {
-          int max_w = fmt_max_width (print->type, var_sheet->format_use);
-          int max_d = fmt_max_decimals (print->type, max_w,
-                                        var_sheet->format_use);
-          set_spin_cell (cell, print->d, 0, max_d, 1);
-        }
-      else
-        g_object_set (cell,
-                      "text", "",
-                      "editable", FALSE,
-                      "adjustment", NULL,
-                      NULL);
-      break;
-
-    case VS_LABEL:
-      g_object_set (cell,
-                    "text", var_has_label (var) ? var_get_label (var) : "",
-                    "editable", TRUE,
-                    NULL);
-      break;
-
-    case VS_VALUES:
-      g_object_set (cell, "editable", FALSE, NULL);
-      if ( ! var_has_value_labels (var))
-        g_object_set (cell, "text", _("None"), NULL);
-      else
-        {
-          const struct val_labs *vls = var_get_value_labels (var);
-          const struct val_lab **labels = val_labs_sorted (vls);
-          const struct val_lab *vl = labels[0];
-          gchar *vstr = value_to_text (vl->value, var);
-          char *text = xasprintf (_("{%s, %s}..."), vstr,
-                                  val_lab_get_escaped_label (vl));
-          free (vstr);
-
-          g_object_set (cell, "text", text, NULL);
-          free (text);
-          free (labels);
-        }
-      break;
-
-    case VS_MISSING:
-      {
-        char *text = missing_values_to_string (var, NULL);
-        g_object_set (cell,
-                      "text", text,
-                      "editable", FALSE,
-                      NULL);
-        free (text);
-      }
-      break;
-
-    case VS_COLUMNS:
-      set_spin_cell (cell, var_get_display_width (var), 1, 2 * MAX_STRING, 1);
-      break;
-
-    case VS_ALIGN:
-      if (GTK_IS_CELL_RENDERER_TEXT (cell))
-        g_object_set (cell,
-                      "text", alignment_to_string (var_get_alignment (var)),
-                      "editable", TRUE,
-                      NULL);
-      else
-        g_object_set (cell, "stock-id",
-                      get_var_align_stock_id (var_get_alignment (var)), NULL);
-      break;
-
-    case VS_MEASURE:
-      if (GTK_IS_CELL_RENDERER_TEXT (cell))
-        g_object_set (cell,
-                      "text", measure_to_string (var_get_measure (var)),
-                      "editable", TRUE,
-                      NULL);
-      else
-        {
-          enum fmt_type type = var_get_print_format (var)->type;
-          enum measure measure = var_get_measure (var);
-
-          g_object_set (cell, "stock-id",
-                        get_var_measurement_stock_id (type, measure),
-                        NULL);
-        }
-      break;
-
-    case VS_ROLE:
-      if (GTK_IS_CELL_RENDERER_TEXT (cell))
-        g_object_set (cell,
-                      "text", var_role_to_string (var_get_role (var)),
-                      "editable", TRUE,
-                      NULL);
-      else
-        g_object_set (cell, "stock-id",
-                      get_var_role_stock_id (var_get_role (var)), NULL);
-      break;
-    }
-}
-
-static struct variable *
-path_string_to_variable (PsppireVarSheet *var_sheet, gchar *path_string)
-{
-  PsppireDict *dict;
-  GtkTreePath *path;
-  gint row;
-
-  path = gtk_tree_path_new_from_string (path_string);
-  row = gtk_tree_path_get_indices (path)[0];
-  gtk_tree_path_free (path);
-
-  dict = psppire_var_sheet_get_dictionary (var_sheet);
-  g_return_val_if_fail (dict != NULL, NULL);
-
-  return psppire_dict_get_variable (dict, row);
-}
-
-static void
-on_type_click (PsppireCellRendererButton *cell,
-               gchar *path,
-               PsppireVarSheet *var_sheet)
-{
-  GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (var_sheet));
-  struct fmt_spec format;
-  struct variable *var;
-
-  var = path_string_to_variable (var_sheet, path);
-  g_return_if_fail (var != NULL);
-
-  format = *var_get_print_format (var);
-  psppire_var_type_dialog_run (GTK_WINDOW (toplevel), &format);
-
-  var_set_width_and_formats (var, fmt_var_width (&format), &format, &format);
-}
-
-static void
-on_value_labels_click (PsppireCellRendererButton *cell,
-                       gchar *path,
-                       PsppireVarSheet *var_sheet)
-{
-  GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (var_sheet));
-  struct val_labs *labels;
-  struct variable *var;
-
-  var = path_string_to_variable (var_sheet, path);
-  g_return_if_fail (var != NULL);
-
-  labels = psppire_val_labs_dialog_run (GTK_WINDOW (toplevel), var);
-  if (labels)
-    {
-      var_set_value_labels (var, labels);
-      val_labs_destroy (labels);
-    }
-}
-
-static void
-on_missing_values_click (PsppireCellRendererButton *cell,
-                         gchar *path,
-                         PsppireVarSheet *var_sheet)
-{
-  GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (var_sheet));
-  struct missing_values mv;
-  struct variable *var;
-
-  var = path_string_to_variable (var_sheet, path);
-  g_return_if_fail (var != NULL);
-
-  psppire_missing_val_dialog_run (GTK_WINDOW (toplevel), var, &mv);
-  var_set_missing_values (var, &mv);
-  mv_destroy (&mv);
-}
-
-static gint
-get_string_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
-                  const char *string)
-{
-  gint width;
-  g_object_set (G_OBJECT (renderer),
-                PSPPIRE_IS_CELL_RENDERER_BUTTON (renderer) ? "label" : "text",
-                string, (void *) NULL);
-
-  gtk_cell_renderer_get_preferred_width (renderer, GTK_WIDGET (treeview), NULL, &width);
-
-  return width;
-}
-
-static gint
-get_monospace_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
-                     size_t char_cnt)
-{
-  struct string s;
-  gint width;
-
-  ds_init_empty (&s);
-  ds_put_byte_multiple (&s, '0', char_cnt);
-  ds_put_byte (&s, ' ');
-  width = get_string_width (treeview, renderer, ds_cstr (&s));
-  ds_destroy (&s);
-
-  return width;
-}
-
-static PsppSheetViewColumn *
-add_var_sheet_column (PsppireVarSheet *var_sheet, GtkCellRenderer *renderer,
-                      enum vs_column column_id,
-                      const char *title, int width)
-{
-  PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
-  int title_width, content_width;
-  PsppSheetViewColumn *column;
-
-  column = pspp_sheet_view_column_new_with_attributes (title, renderer, NULL);
-  g_object_set_data (G_OBJECT (column), "column-number",
-                     GINT_TO_POINTER (column_id) + 1);
-
-  pspp_sheet_view_column_set_cell_data_func (
-    column, renderer, render_var_cell, var_sheet, NULL);
-
-  title_width = get_string_width (sheet_view, renderer, title);
-  content_width = get_monospace_width (sheet_view, renderer, width);
-  g_object_set_data (G_OBJECT (column), "content-width",
-                     GINT_TO_POINTER (content_width));
-
-  pspp_sheet_view_column_set_fixed_width (column,
-                                          MAX (title_width, content_width));
-  pspp_sheet_view_column_set_resizable (column, TRUE);
-
-  pspp_sheet_view_append_column (sheet_view, column);
-
-  g_signal_connect (renderer, "edited",
-                    G_CALLBACK (on_var_column_edited),
-                    var_sheet);
-  g_object_set_data (G_OBJECT (renderer), "column-id",
-                     GINT_TO_POINTER (column_id));
-  g_object_set_data (G_OBJECT (renderer), "var-sheet", var_sheet);
-
-  return column;
-}
-
-static PsppSheetViewColumn *
-add_text_column (PsppireVarSheet *var_sheet, enum vs_column column_id,
-                 const char *title, int width)
-{
-  return add_var_sheet_column (var_sheet, gtk_cell_renderer_text_new (),
-                               column_id, title, width);
-}
-
-static PsppSheetViewColumn *
-add_spin_column (PsppireVarSheet *var_sheet, enum vs_column column_id,
-                 const char *title, int width)
-{
-  return add_var_sheet_column (var_sheet, gtk_cell_renderer_spin_new (),
-                               column_id, title, width);
-}
-
-static const char *
-measure_to_stock_id (enum fmt_type type, int measure)
-{
-  return get_var_measurement_stock_id (type, measure);
-}
-
-static const char *
-alignment_to_stock_id (enum fmt_type type, int alignment)
-{
-  return get_var_align_stock_id (alignment);
-}
-
-static const char *
-role_to_stock_id (enum fmt_type type, int role)
-{
-  return get_var_role_stock_id (role);
-}
-
-static void
-render_var_pixbuf (GtkCellLayout *cell_layout,
-                   GtkCellRenderer *cell,
-                   GtkTreeModel *tree_model,
-                   GtkTreeIter *iter,
-                   gpointer data)
-{
-  const char *(*value_to_stock_id) (enum fmt_type, int value);
-  enum fmt_type type = GPOINTER_TO_INT (data);
-  gint value;
-
-  value_to_stock_id = g_object_get_data (G_OBJECT (cell), "value-to-stock-id");
-
-  gtk_tree_model_get (tree_model, iter, 0, &value, -1);
-  g_object_set (cell, "stock-id", value_to_stock_id (type, value), NULL);
-}
-
-static void
-on_combo_editing_started (GtkCellRenderer *renderer,
-                          GtkCellEditable *editable,
-                          gchar           *path_string,
-                          gpointer         user_data)
-{
-  PsppireVarSheet *var_sheet = user_data;
-
-  if (GTK_IS_COMBO_BOX (editable))
-    {
-      struct variable *var = path_string_to_variable (var_sheet, path_string);
-      const struct fmt_spec *format = var_get_print_format (var);
-      const char *(*value_to_stock_id) (enum fmt_type, int value);
-      GtkCellRenderer *cell;
-
-      value_to_stock_id = g_object_get_data (G_OBJECT (renderer),
-                                             "value-to-stock-id");
-
-      cell = gtk_cell_renderer_pixbuf_new ();
-      g_object_set (cell, "width", 16, "height", 16, NULL);
-      g_object_set_data (G_OBJECT (cell),
-                         "value-to-stock-id", value_to_stock_id);
-      gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (editable), cell, FALSE);
-      gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (editable), cell,
-                                          render_var_pixbuf,
-                                          GINT_TO_POINTER (format->type),
-                                          NULL);
-    }
-}
-
-static void
-add_combo_column (PsppireVarSheet *var_sheet, enum vs_column column_id,
-                  const char *title, int width,
-                  const char *(*value_to_stock_id) (enum fmt_type, int value),
-                  ...)
-{
-  PsppSheetViewColumn *column;
-  GtkCellRenderer *cell;
-  GtkListStore *store;
-  const char *name;
-  va_list args;
-
-  store = gtk_list_store_new (2, G_TYPE_INT, G_TYPE_STRING);
-  va_start (args, value_to_stock_id);
-  while ((name = va_arg (args, const char *)) != NULL)
-    {
-      int value = va_arg (args, int);
-      gtk_list_store_insert_with_values (store, NULL, G_MAXINT,
-                                         0, value,
-                                         1, name,
-                                         -1);
-    }
-  va_end (args);
-
-  cell = gtk_cell_renderer_combo_new ();
-  g_object_set (cell,
-                "has-entry", FALSE,
-                "model", GTK_TREE_MODEL (store),
-                "text-column", 1,
-                NULL);
-  if (value_to_stock_id != NULL)
-    {
-      g_object_set_data (G_OBJECT (cell),
-                         "value-to-stock-id", value_to_stock_id);
-      g_signal_connect (cell, "editing-started",
-                        G_CALLBACK (on_combo_editing_started),
-                        var_sheet);
-    }
-
-  column = add_var_sheet_column (var_sheet, cell, column_id, title, width);
-
-  cell = gtk_cell_renderer_pixbuf_new ();
-  g_object_set (cell, "width", 16, "height", 16, NULL);
-  pspp_sheet_view_column_pack_end (column, cell, FALSE);
-  pspp_sheet_view_column_set_cell_data_func (
-    column, cell, render_var_cell, var_sheet, NULL);
-}
-
-static void
-add_popup_menu (PsppireVarSheet *var_sheet,
-                PsppSheetViewColumn *column,
-                void (*on_click) (PsppireCellRendererButton *,
-                                  gchar *path,
-                                  PsppireVarSheet *var_sheet))
-{
-  PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
-  const char *button_label = "...";
-  GtkCellRenderer *button_renderer;
-  gint content_width;
-
-  button_renderer = psppire_cell_renderer_button_new ();
-  g_object_set (button_renderer,
-                "label", button_label,
-                "editable", TRUE,
-                NULL);
-  g_signal_connect (button_renderer, "clicked", G_CALLBACK (on_click),
-                    var_sheet);
-  pspp_sheet_view_column_pack_start (column, button_renderer, FALSE);
-  pspp_sheet_view_column_set_cell_data_func (
-    column, button_renderer, render_popup_cell, var_sheet, NULL);
-
-  content_width = GPOINTER_TO_INT (g_object_get_data (
-                                     G_OBJECT (column), "content-width"));
-  content_width += get_string_width (sheet_view, button_renderer,
-                                     button_label);
-  if (content_width > pspp_sheet_view_column_get_fixed_width (column))
-    pspp_sheet_view_column_set_fixed_width (column, content_width);
-}
-
-static gboolean
-get_tooltip_location (GtkWidget *widget, GtkTooltip *tooltip,
-                      gint wx, gint wy, size_t *row, size_t *column)
-{
-  PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
-  gint bx, by;
-  GtkTreePath *path;
-  GtkTreeIter iter;
-  PsppSheetViewColumn *tree_column;
-  GtkTreeModel *tree_model;
-  gpointer column_ptr;
-  bool ok;
-
-  /* Check that WIDGET is really visible on the screen before we
-     do anything else.  This is a bug fix for a sticky situation:
-     when text_data_import_assistant() returns, it frees the data
-     necessary to compose the tool tip message, but there may be
-     a tool tip under preparation at that point (even if there is
-     no visible tool tip) that will call back into us a little
-     bit later.  Perhaps the correct solution to this problem is
-     to make the data related to the tool tips part of a GObject
-     that only gets destroyed when all references are released,
-     but this solution appears to be effective too. */
-  if (!gtk_widget_get_mapped (widget))
-    return FALSE;
-
-  pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view,
-                                                     wx, wy, &bx, &by);
-  if (!pspp_sheet_view_get_path_at_pos (tree_view, bx, by,
-                                      &path, &tree_column, NULL, NULL))
-    return FALSE;
-
-  column_ptr = g_object_get_data (G_OBJECT (tree_column), "column-number");
-  if (column_ptr == NULL)
-    return FALSE;
-  *column = GPOINTER_TO_INT (column_ptr) - 1;
-
-  pspp_sheet_view_set_tooltip_cell (tree_view, tooltip, path, tree_column,
-                                    NULL);
-
-  tree_model = pspp_sheet_view_get_model (tree_view);
-  ok = gtk_tree_model_get_iter (tree_model, &iter, path);
-  gtk_tree_path_free (path);
-  if (!ok)
-    return FALSE;
-
-  *row = GPOINTER_TO_INT (iter.user_data);
-  return TRUE;
-}
-
-static gboolean
-on_query_var_tooltip (GtkWidget *widget, gint wx, gint wy,
-                      gboolean keyboard_mode UNUSED,
-                      GtkTooltip *tooltip, gpointer *user_data UNUSED)
-{
-  PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (widget);
-  PsppireDict *dict;
-  struct variable *var;
-  size_t row, column;
-
-  if (!get_tooltip_location (widget, tooltip, wx, wy, &row, &column))
-    return FALSE;
-
-  dict = psppire_var_sheet_get_dictionary (var_sheet);
-  g_return_val_if_fail (dict != NULL, FALSE);
-
-  if (row >= psppire_dict_get_var_cnt (dict))
-    {
-      gtk_tooltip_set_text (tooltip, _("Enter a variable name to add a "
-                                       "new variable."));
-      return TRUE;
-    }
-
-  var = psppire_dict_get_variable (dict, row);
-  g_return_val_if_fail (var != NULL, FALSE);
-
-  switch (column)
-    {
-    case VS_TYPE:
-      {
-        char text[FMT_STRING_LEN_MAX + 1];
-
-        fmt_to_string (var_get_print_format (var), text);
-        gtk_tooltip_set_text (tooltip, text);
-        return TRUE;
-      }
-
-    case VS_VALUES:
-      if (var_has_value_labels (var))
-        {
-          const struct val_labs *vls = var_get_value_labels (var);
-          const struct val_lab **labels = val_labs_sorted (vls);
-          struct string s;
-          size_t i;
-
-          ds_init_empty (&s);
-          for (i = 0; i < val_labs_count (vls); i++)
-            {
-              const struct val_lab *vl = labels[i];
-              gchar *vstr;
-
-              if (i >= 10 || ds_length (&s) > 500)
-                {
-                  ds_put_cstr (&s, "...");
-                  break;
-                }
-
-              vstr = value_to_text (vl->value, var);
-              ds_put_format (&s, _("{%s, %s}\n"), vstr,
-                             val_lab_get_escaped_label (vl));
-              free (vstr);
-
-            }
-          ds_chomp_byte (&s, '\n');
-
-          gtk_tooltip_set_text (tooltip, ds_cstr (&s));
-          ds_destroy (&s);
-          free (labels);
-
-          return TRUE;
-        }
-    }
-
-  return FALSE;
-}
-
-static void
-do_popup_menu (GtkWidget *widget, guint button, guint32 time)
-{
-  PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (widget);
-  GtkWidget *menu;
-
-  menu = get_widget_assert (var_sheet->builder, "varsheet-variable-popup");
-  gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time);
-}
-
-static void
-on_popup_menu (GtkWidget *widget, gpointer user_data UNUSED)
-{
-  do_popup_menu (widget, 0, gtk_get_current_event_time ());
-}
-
-static gboolean
-on_button_pressed (GtkWidget *widget, GdkEventButton *event,
-                   gpointer user_data UNUSED)
-{
-  PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
-
-  if (event->type == GDK_BUTTON_PRESS && event->button == 3)
-    {
-      PsppSheetSelection *selection;
-
-      selection = pspp_sheet_view_get_selection (sheet_view);
-      if (pspp_sheet_selection_count_selected_rows (selection) <= 1)
-        {
-          GtkTreePath *path;
-
-          if (pspp_sheet_view_get_path_at_pos (sheet_view, event->x, event->y,
-                                               &path, NULL, NULL, NULL))
-            {
-              pspp_sheet_selection_unselect_all (selection);
-              pspp_sheet_selection_select_path (selection, path);
-              gtk_tree_path_free (path);
-            }
-        }
-
-      do_popup_menu (widget, event->button, event->time);
-      return TRUE;
-    }
-
-  return FALSE;
-}
-\f
-GType
-psppire_fmt_use_get_type (void)
-{
-  static GType etype = 0;
-  if (etype == 0)
-    {
-      static const GEnumValue values[] =
-       {
-         { FMT_FOR_INPUT, "FMT_FOR_INPUT", "input" },
-         { FMT_FOR_OUTPUT, "FMT_FOR_OUTPUT", "output" },
-         { 0, NULL, NULL }
-       };
-
-      etype = g_enum_register_static
-       (g_intern_static_string ("PsppireFmtUse"), values);
-    }
-  return etype;
-}
-
-enum
-  {
-    PROP_0,
-    PROP_DICTIONARY,
-    PROP_MAY_CREATE_VARS,
-    PROP_MAY_DELETE_VARS,
-    PROP_FORMAT_TYPE,
-    PROP_UI_MANAGER
-  };
-
-static void
-psppire_var_sheet_set_property (GObject      *object,
-                             guint         prop_id,
-                             const GValue *value,
-                             GParamSpec   *pspec)
-{
-  PsppireVarSheet *obj = PSPPIRE_VAR_SHEET (object);
-
-  switch (prop_id)
-    {
-    case PROP_DICTIONARY:
-      psppire_var_sheet_set_dictionary (obj,
-                                        PSPPIRE_DICT (g_value_get_object (
-                                                        value)));
-      break;
-
-    case PROP_MAY_CREATE_VARS:
-      psppire_var_sheet_set_may_create_vars (obj,
-                                             g_value_get_boolean (value));
-      break;
-
-    case PROP_MAY_DELETE_VARS:
-      psppire_var_sheet_set_may_delete_vars (obj,
-                                             g_value_get_boolean (value));
-      break;
-
-    case PROP_FORMAT_TYPE:
-      obj->format_use = g_value_get_enum (value);
-      break;
-
-    case PROP_UI_MANAGER:
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
-    }
-}
-
-static void
-psppire_var_sheet_get_property (GObject      *object,
-                                guint         prop_id,
-                                GValue       *value,
-                                GParamSpec   *pspec)
-{
-  PsppireVarSheet *obj = PSPPIRE_VAR_SHEET (object);
-
-  switch (prop_id)
-    {
-    case PROP_DICTIONARY:
-      g_value_set_object (value,
-                          G_OBJECT (psppire_var_sheet_get_dictionary (obj)));
-      break;
-
-    case PROP_MAY_CREATE_VARS:
-      g_value_set_boolean (value, obj->may_create_vars);
-      break;
-
-    case PROP_MAY_DELETE_VARS:
-      g_value_set_boolean (value, obj->may_delete_vars);
-      break;
-
-    case PROP_FORMAT_TYPE:
-      g_value_set_enum (value, obj->format_use);
-      break;
-
-    case PROP_UI_MANAGER:
-      g_value_set_object (value, psppire_var_sheet_get_ui_manager (obj));
-      break;
-
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
-    }
-}
-
-static void
-psppire_var_sheet_dispose (GObject *obj)
-{
-  PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (obj);
-  int i;
-
-  if (var_sheet->dispose_has_run)
-    return;
-
-  var_sheet->dispose_has_run = TRUE;
-
-  for (i = 0; i < PSPPIRE_VAR_SHEET_N_SIGNALS; i++)
-    if ( var_sheet->dict_signals[i])
-      g_signal_handler_disconnect (var_sheet->dict,
-                                  var_sheet->dict_signals[i]);
-
-  if (var_sheet->dict)
-    g_object_unref (var_sheet->dict);
-  
-  if (var_sheet->uim)
-    g_object_unref (var_sheet->uim);
-
-  /* These dialogs are not GObjects (although they should be!)
-    But for now, unreffing them only causes a GCritical Error
-    so comment them out for now. (and accept the memory leakage)
-
-  g_object_unref (var_sheet->val_labs_dialog);
-  g_object_unref (var_sheet->missing_val_dialog);
-  g_object_unref (var_sheet->var_type_dialog);
-  */
-
-  g_object_unref (var_sheet->builder);
-
-  
-  G_OBJECT_CLASS (psppire_var_sheet_parent_class)->dispose (obj);
-}
-
-static void
-psppire_var_sheet_class_init (PsppireVarSheetClass *class)
-{
-  GObjectClass *gobject_class = G_OBJECT_CLASS (class);
-  GParamSpec *pspec;
-
-  gobject_class->set_property = psppire_var_sheet_set_property;
-  gobject_class->get_property = psppire_var_sheet_get_property;
-  gobject_class->dispose = psppire_var_sheet_dispose;
-
-  g_signal_new ("var-double-clicked",
-                G_OBJECT_CLASS_TYPE (gobject_class),
-                G_SIGNAL_RUN_LAST,
-                0,
-                g_signal_accumulator_true_handled, NULL,
-                psppire_marshal_BOOLEAN__INT,
-                G_TYPE_BOOLEAN, 1, G_TYPE_INT);
-
-  pspec = g_param_spec_object ("dictionary",
-                               "Dictionary displayed by the sheet",
-                               "The PsppireDict that the sheet displays "
-                               "may allow the user to edit",
-                               PSPPIRE_TYPE_DICT,
-                               G_PARAM_READWRITE);
-  g_object_class_install_property (gobject_class, PROP_DICTIONARY, pspec);
-
-  pspec = g_param_spec_boolean ("may-create-vars",
-                                "May create variables",
-                                "Whether the user may create more variables",
-                                TRUE,
-                                G_PARAM_READWRITE);
-  g_object_class_install_property (gobject_class, PROP_MAY_CREATE_VARS, pspec);
-
-  pspec = g_param_spec_boolean ("may-delete-vars",
-                                "May delete variables",
-                                "Whether the user may delete variables",
-                                TRUE,
-                                G_PARAM_READWRITE);
-  g_object_class_install_property (gobject_class, PROP_MAY_DELETE_VARS, pspec);
-
-  pspec = g_param_spec_enum ("format-use",
-                             "Use of variable format",
-                             ("Whether variables have input or output "
-                              "formats"),
-                             PSPPIRE_TYPE_FMT_USE,
-                             FMT_FOR_OUTPUT,
-                             G_PARAM_READWRITE);
-  g_object_class_install_property (gobject_class, PROP_FORMAT_TYPE, pspec);
-
-  pspec = g_param_spec_object ("ui-manager",
-                               "UI Manager",
-                               "UI manager for the variable sheet.  The client should merge this UI manager with the active UI manager to obtain variable sheet specific menu items and tool bar items.",
-                               GTK_TYPE_UI_MANAGER,
-                               G_PARAM_READABLE);
-  g_object_class_install_property (gobject_class, PROP_UI_MANAGER, pspec);
-}
-
-static void
-render_row_number_cell (PsppSheetViewColumn *tree_column,
-                        GtkCellRenderer *cell,
-                        GtkTreeModel *model,
-                        GtkTreeIter *iter,
-                        gpointer user_data)
-{
-  PsppireVarSheet *var_sheet = user_data;
-  GValue gvalue = { 0, };
-  gint row;
-
-  row = GPOINTER_TO_INT (iter->user_data);
-
-  g_value_init (&gvalue, G_TYPE_INT);
-  g_value_set_int (&gvalue, row + 1);
-  g_object_set_property (G_OBJECT (cell), "label", &gvalue);
-  g_value_unset (&gvalue);
-
-  if (!var_sheet->dict || row < psppire_dict_get_var_cnt (var_sheet->dict))
-    g_object_set (cell, "editable", TRUE, NULL);
-  else
-    g_object_set (cell, "editable", FALSE, NULL);
-}
-
-static void
-psppire_var_sheet_row_number_double_clicked (PsppireCellRendererButton *button,
-                                             gchar *path_string,
-                                             PsppireVarSheet *var_sheet)
-{
-  GtkTreePath *path;
-
-  g_return_if_fail (var_sheet->dict != NULL);
-
-  path = gtk_tree_path_new_from_string (path_string);
-  if (gtk_tree_path_get_depth (path) == 1)
-    {
-      gint *indices = gtk_tree_path_get_indices (path);
-      if (indices[0] < psppire_dict_get_var_cnt (var_sheet->dict))
-        {
-          gboolean handled;
-          g_signal_emit_by_name (var_sheet, "var-double-clicked",
-                                 indices[0], &handled);
-        }
-    }
-  gtk_tree_path_free (path);
-}
-
-static void
-psppire_var_sheet_variables_column_clicked (PsppSheetViewColumn *column,
-                                            PsppireVarSheet *var_sheet)
-{
-  PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
-  PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
-
-  pspp_sheet_selection_select_all (selection);
-}
-
-static PsppSheetViewColumn *
-make_row_number_column (PsppireVarSheet *var_sheet)
-{
-  PsppSheetViewColumn *column;
-  GtkCellRenderer *renderer;
-
-  renderer = psppire_cell_renderer_button_new ();
-  g_object_set (renderer, "xalign", 1.0, NULL);
-  g_signal_connect (renderer, "double-clicked",
-                    G_CALLBACK (psppire_var_sheet_row_number_double_clicked),
-                    var_sheet);
-
-  column = pspp_sheet_view_column_new_with_attributes (_("Variable"),
-                                                       renderer, NULL);
-  pspp_sheet_view_column_set_clickable (column, TRUE);
-  pspp_sheet_view_column_set_cell_data_func (
-    column, renderer, render_row_number_cell, var_sheet, NULL);
-  pspp_sheet_view_column_set_fixed_width (column, 50);
-  g_signal_connect (column, "clicked",
-                    G_CALLBACK (psppire_var_sheet_variables_column_clicked),
-                    var_sheet);
-
-  return column;
-}
-
-static void
-on_edit_clear_variables (GtkAction *action, PsppireVarSheet *var_sheet)
-{
-  PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
-  PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
-  PsppireDict *dict = var_sheet->dict;
-  const struct range_set_node *node;
-  struct range_set *selected;
-
-  selected = pspp_sheet_selection_get_range_set (selection);
-  for (node = range_set_last (selected); node != NULL;
-       node = range_set_prev (selected, node))
-    {
-      int i;
-
-      for (i = 1; i <= range_set_node_get_width (node); i++)
-        {
-          unsigned long row = range_set_node_get_end (node) - i;
-          if (row < psppire_dict_get_var_cnt (dict))
-            psppire_dict_delete_variables (dict, row, 1);
-        }
-    }
-  range_set_destroy (selected);
-}
-
-static void
-on_selection_changed (PsppSheetSelection *selection,
-                      gpointer user_data UNUSED)
-{
-  PsppSheetView *sheet_view = pspp_sheet_selection_get_tree_view (selection);
-  PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (sheet_view);
-  gint n_selected_rows;
-  gboolean may_delete;
-  GtkTreePath *path;
-  GtkAction *action;
-
-  n_selected_rows = pspp_sheet_selection_count_selected_rows (selection);
-
-  action = get_action_assert (var_sheet->builder, "edit_insert-variable");
-  gtk_action_set_sensitive (action, (var_sheet->may_create_vars
-                                     && n_selected_rows > 0));
-
-  switch (n_selected_rows)
-    {
-    case 0:
-      may_delete = FALSE;
-      break;
-
-    case 1:
-      /* The row used for inserting new variables cannot be deleted. */
-      path = gtk_tree_path_new_from_indices (
-        psppire_dict_get_var_cnt (var_sheet->dict), -1);
-      may_delete = !pspp_sheet_selection_path_is_selected (selection, path);
-      gtk_tree_path_free (path);
-      break;
-
-    default:
-      may_delete = TRUE;
-      break;
-    }
-  action = get_action_assert (var_sheet->builder, "edit_clear-variables");
-  gtk_action_set_sensitive (action, var_sheet->may_delete_vars && may_delete);
-}
-
-static void
-on_edit_insert_variable (GtkAction *action, PsppireVarSheet *var_sheet)
-{
-  PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
-  PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
-  PsppireDict *dict = var_sheet->dict;
-  struct range_set *selected;
-  unsigned long row;
-
-  selected = pspp_sheet_selection_get_range_set (selection);
-  row = range_set_scan (selected, 0);
-  range_set_destroy (selected);
-
-  if (row <= psppire_dict_get_var_cnt (dict))
-    {
-      gchar name[64];;
-      if (psppire_dict_generate_name (dict, name, sizeof name))
-        psppire_dict_insert_variable (dict, row, name);
-    }
-}
-
-static void
-psppire_var_sheet_init (PsppireVarSheet *obj)
-{
-  PsppSheetView *sheet_view = PSPP_SHEET_VIEW (obj);
-  PsppSheetViewColumn *column;
-  GtkAction *action;
-  GList *list;
-
-  obj->dict = NULL;
-  obj->format_use = FMT_FOR_OUTPUT;
-  obj->may_create_vars = TRUE;
-  obj->may_delete_vars = TRUE;
-
-  obj->scroll_to_bottom_signal = 0;
-
-  obj->container = NULL;
-  obj->dispose_has_run = FALSE;
-  obj->uim = NULL;
-
-  pspp_sheet_view_append_column (sheet_view, make_row_number_column (obj));
-
-  column = add_text_column (obj, VS_NAME, _("Name"), 12);
-  list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
-  g_signal_connect (list->data, "editing-started",
-                    G_CALLBACK (on_name_column_editing_started), NULL);
-  g_list_free (list);
-
-  column = add_text_column (obj, VS_TYPE, _("Type"), 8);
-  add_popup_menu (obj, column, on_type_click);
-
-  add_spin_column (obj, VS_WIDTH, _("Width"), 5);
-
-  add_spin_column (obj, VS_DECIMALS, _("Decimals"), 2);
-
-  add_text_column (obj, VS_LABEL, _("Label"), 20);
-
-  column = add_text_column (obj, VS_VALUES, _("Value Labels"), 20);
-  add_popup_menu (obj, column, on_value_labels_click);
-
-  column = add_text_column (obj, VS_MISSING, _("Missing Values"), 20);
-  add_popup_menu (obj, column, on_missing_values_click);
-
-  add_spin_column (obj, VS_COLUMNS, _("Columns"), 3);
-
-  add_combo_column (obj, VS_ALIGN, _("Align"), 8, alignment_to_stock_id,
-                    alignment_to_string (ALIGN_LEFT), ALIGN_LEFT,
-                    alignment_to_string (ALIGN_CENTRE), ALIGN_CENTRE,
-                    alignment_to_string (ALIGN_RIGHT), ALIGN_RIGHT,
-                    NULL);
-
-  add_combo_column (obj, VS_MEASURE, _("Measure"), 11, measure_to_stock_id,
-                    measure_to_string (MEASURE_NOMINAL), MEASURE_NOMINAL,
-                    measure_to_string (MEASURE_ORDINAL), MEASURE_ORDINAL,
-                    measure_to_string (MEASURE_SCALE), MEASURE_SCALE,
-                    NULL);
-
-  add_combo_column (obj, VS_ROLE, _("Role"), 11, role_to_stock_id,
-                    var_role_to_string (ROLE_INPUT), ROLE_INPUT,
-                    var_role_to_string (ROLE_TARGET), ROLE_TARGET,
-                    var_role_to_string (ROLE_BOTH), ROLE_BOTH,
-                    var_role_to_string (ROLE_NONE), ROLE_NONE,
-                    var_role_to_string (ROLE_PARTITION), ROLE_PARTITION,
-                    var_role_to_string (ROLE_SPLIT), ROLE_SPLIT,
-                    NULL);
-
-  pspp_sheet_view_set_rubber_banding (sheet_view, TRUE);
-  pspp_sheet_selection_set_mode (pspp_sheet_view_get_selection (sheet_view),
-                                 PSPP_SHEET_SELECTION_MULTIPLE);
-
-  g_object_set (G_OBJECT (obj), "has-tooltip", TRUE, NULL);
-  g_signal_connect (obj, "query-tooltip",
-                    G_CALLBACK (on_query_var_tooltip), NULL);
-  g_signal_connect (obj, "button-press-event",
-                    G_CALLBACK (on_button_pressed), NULL);
-  g_signal_connect (obj, "popup-menu", G_CALLBACK (on_popup_menu), NULL);
-
-  obj->builder = builder_new ("var-sheet.ui");
-
-  action = get_action_assert (obj->builder, "edit_clear-variables");
-  g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_variables),
-                    obj);
-  gtk_action_set_sensitive (action, FALSE);
-  g_signal_connect (pspp_sheet_view_get_selection (sheet_view),
-                    "changed", G_CALLBACK (on_selection_changed), NULL);
-
-  action = get_action_assert (obj->builder, "edit_insert-variable");
-  gtk_action_set_sensitive (action, FALSE);
-  g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_variable),
-                    obj);
-}
-
-GtkWidget *
-psppire_var_sheet_new (void)
-{
-  return g_object_new (PSPPIRE_VAR_SHEET_TYPE, NULL);
-}
-
-PsppireDict *
-psppire_var_sheet_get_dictionary (PsppireVarSheet *var_sheet)
-{
-  return var_sheet->dict;
-}
-
-static void
-refresh_model (PsppireVarSheet *var_sheet)
-{
-  pspp_sheet_view_set_model (PSPP_SHEET_VIEW (var_sheet), NULL);
-
-  if (var_sheet->dict != NULL)
-    {
-      PsppireEmptyListStore *store;
-      int n_rows;
-
-      n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
-                + var_sheet->may_create_vars);
-      store = psppire_empty_list_store_new (n_rows);
-      pspp_sheet_view_set_model (PSPP_SHEET_VIEW (var_sheet),
-                                 GTK_TREE_MODEL (store));
-      g_object_unref (store);
-    }
-}
-
-static void
-on_var_changed (PsppireDict *dict, glong row,
-               guint what, const struct variable *oldvar,
-               PsppireVarSheet *var_sheet)
-{
-  PsppireEmptyListStore *store;
-
-  g_return_if_fail (dict == var_sheet->dict);
-
-  store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
-                                      PSPP_SHEET_VIEW (var_sheet)));
-  g_return_if_fail (store != NULL);
-
-  psppire_empty_list_store_row_changed (store, row);
-}
-
-static void
-on_var_inserted (PsppireDict *dict, glong row, PsppireVarSheet *var_sheet)
-{
-  PsppireEmptyListStore *store;
-  int n_rows;
-
-  g_return_if_fail (dict == var_sheet->dict);
-
-  store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
-                                      PSPP_SHEET_VIEW (var_sheet)));
-  g_return_if_fail (store != NULL);
-
-  n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
-            + var_sheet->may_create_vars);
-  psppire_empty_list_store_set_n_rows (store, n_rows);
-  psppire_empty_list_store_row_inserted (store, row);
-}
-
-static void
-on_var_deleted (PsppireDict *dict,
-                const struct variable *var, int dict_idx, int case_idx,
-                PsppireVarSheet *var_sheet)
-{
-  PsppireEmptyListStore *store;
-  int n_rows;
-
-  g_return_if_fail (dict == var_sheet->dict);
-
-  store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
-                                      PSPP_SHEET_VIEW (var_sheet)));
-  g_return_if_fail (store != NULL);
-
-  n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
-            + var_sheet->may_create_vars);
-  psppire_empty_list_store_set_n_rows (store, n_rows);
-  psppire_empty_list_store_row_deleted (store, dict_idx);
-}
-
-static void
-on_backend_changed (PsppireDict *dict, PsppireVarSheet *var_sheet)
-{
-  g_return_if_fail (dict == var_sheet->dict);
-  refresh_model (var_sheet);
-}
-
-void
-psppire_var_sheet_set_dictionary (PsppireVarSheet *var_sheet,
-                                  PsppireDict *dict)
-{
-  if (var_sheet->dict != NULL)
-    {
-      int i;
-      
-      for (i = 0; i < PSPPIRE_VAR_SHEET_N_SIGNALS; i++)
-       {
-         if (var_sheet->dict_signals[i])
-           g_signal_handler_disconnect (var_sheet->dict,
-                                        var_sheet->dict_signals[i]);
-         
-         var_sheet->dict_signals[i] = 0;
-       }
-
-      g_object_unref (var_sheet->dict);
-    }
-
-  var_sheet->dict = dict;
-
-  if (dict != NULL)
-    {
-      g_object_ref (dict);
-
-      var_sheet->dict_signals[PSPPIRE_VAR_SHEET_BACKEND_CHANGED]
-        = g_signal_connect (dict, "backend-changed",
-                            G_CALLBACK (on_backend_changed), var_sheet);
-
-      var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_CHANGED]
-        = g_signal_connect (dict, "variable-changed",
-                            G_CALLBACK (on_var_changed), var_sheet);
-
-      var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_DELETED]
-        = g_signal_connect (dict, "variable-inserted",
-                            G_CALLBACK (on_var_inserted), var_sheet);
-
-      var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_INSERTED]
-        = g_signal_connect (dict, "variable-deleted",
-                            G_CALLBACK (on_var_deleted), var_sheet);
-    }
-
-  refresh_model (var_sheet);
-}
-
-gboolean
-psppire_var_sheet_get_may_create_vars (PsppireVarSheet *var_sheet)
-{
-  return var_sheet->may_create_vars;
-}
-
-void
-psppire_var_sheet_set_may_create_vars (PsppireVarSheet *var_sheet,
-                                       gboolean may_create_vars)
-{
-  if (var_sheet->may_create_vars != may_create_vars)
-    {
-      PsppireEmptyListStore *store;
-      gint n_rows;
-
-      var_sheet->may_create_vars = may_create_vars;
-
-      store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
-                                          PSPP_SHEET_VIEW (var_sheet)));
-      g_return_if_fail (store != NULL);
-
-      n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
-                + var_sheet->may_create_vars);
-      psppire_empty_list_store_set_n_rows (store, n_rows);
-
-      if (may_create_vars)
-        psppire_empty_list_store_row_inserted (store, n_rows - 1);
-      else
-        psppire_empty_list_store_row_deleted (store, n_rows);
-
-      on_selection_changed (pspp_sheet_view_get_selection (
-                              PSPP_SHEET_VIEW (var_sheet)), NULL);
-    }
-}
-
-gboolean
-psppire_var_sheet_get_may_delete_vars (PsppireVarSheet *var_sheet)
-{
-  return var_sheet->may_delete_vars;
-}
-
-void
-psppire_var_sheet_set_may_delete_vars (PsppireVarSheet *var_sheet,
-                                       gboolean may_delete_vars)
-{
-  if (var_sheet->may_delete_vars != may_delete_vars)
-    {
-      var_sheet->may_delete_vars = may_delete_vars;
-      on_selection_changed (pspp_sheet_view_get_selection (
-                              PSPP_SHEET_VIEW (var_sheet)), NULL);
-    }
-}
-
-void
-psppire_var_sheet_goto_variable (PsppireVarSheet *var_sheet, int dict_index)
-{
-  PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
-  GtkTreePath *path;
-
-  path = gtk_tree_path_new_from_indices (dict_index, -1);
-  pspp_sheet_view_scroll_to_cell (sheet_view, path, NULL, FALSE, 0.0, 0.0);
-  pspp_sheet_view_set_cursor (sheet_view, path, NULL, FALSE);
-  gtk_tree_path_free (path);
-}
-
-GtkUIManager *
-psppire_var_sheet_get_ui_manager (PsppireVarSheet *var_sheet)
-{
-  if (var_sheet->uim == NULL)
-    {
-      var_sheet->uim = GTK_UI_MANAGER (get_object_assert (var_sheet->builder,
-                                                         "var_sheet_uim",
-                                                         GTK_TYPE_UI_MANAGER));
-      g_object_ref (var_sheet->uim);
-    }
-
-  return var_sheet->uim;
-}
-
index e8db83e12ac4e0b4c0598b7f5d383b01eab0ce61..3d10dd0d53950599b89696baec0e513ff86010e7 100644 (file)
@@ -46,7 +46,6 @@ typedef struct _PsppireVarSheetClass  PsppireVarSheetClass;
 
 enum
 {
-    PSPPIRE_VAR_SHEET_BACKEND_CHANGED,
     PSPPIRE_VAR_SHEET_VARIABLE_CHANGED,
     PSPPIRE_VAR_SHEET_VARIABLE_INSERTED,
     PSPPIRE_VAR_SHEET_VARIABLE_DELETED,
index b9a7a1ec3edf2da7741aaa292f9182e684d40c95..3f138291ec0dc43d06f318aa6c40d97840bdab99 100644 (file)
@@ -4,6 +4,7 @@
 
 #include "widgets.h"
 
+#include "gettext.h"
 
 #include "psppire-dialog.h"
 #include "psppire-selector.h"
@@ -121,6 +122,62 @@ preregister_actions (void)
 }
 
 
+static void
+tx_string_to_double (const GValue *src, GValue *dest)
+{
+  const gchar *str = g_value_get_string (src);
+  gdouble dble = g_strtod (str, NULL);
+  g_value_set_double (dest, dble);
+}
+
+
+static void
+tx_string_to_int (const GValue *src, GValue *dest)
+{
+  const gchar *str = g_value_get_string (src);
+  gint x = atoi (str);
+  g_value_set_int (dest, x);
+}
+
+static void
+enum_to_string (const GValue *src, GValue *dest)
+{
+  gint n = g_value_get_enum (src);
+  GType t = G_VALUE_TYPE (src);
+  GEnumClass *ec = g_type_class_ref (t);
+  GEnumValue *ev = g_enum_get_value (ec, n);
+
+  g_value_set_string (dest, gettext (ev->value_nick));
+}
+
+
+
+GType align_enum_type;
+GType measure_enum_type;
+GType role_enum_type;
+
+
+extern const GEnumValue align[];
+extern const GEnumValue measure[];
+extern const GEnumValue role[];
+
+
+
+static void
+preregister_misc (void)
+{
+  align_enum_type = g_enum_register_static ("PsppAlignment", align);
+  measure_enum_type = g_enum_register_static ("PsppMeasure", measure);
+  role_enum_type = g_enum_register_static ("PsppRole", role);
+
+  g_value_register_transform_func (G_TYPE_STRING, G_TYPE_DOUBLE, tx_string_to_double);
+  g_value_register_transform_func (G_TYPE_STRING, G_TYPE_INT, tx_string_to_int);
+  
+  g_value_register_transform_func (measure_enum_type, G_TYPE_STRING, enum_to_string);
+  g_value_register_transform_func (align_enum_type, G_TYPE_STRING, enum_to_string);
+  g_value_register_transform_func (role_enum_type, G_TYPE_STRING, enum_to_string);
+}
+
 
 /* Any custom widgets which are to be used in GtkBuilder ui files
    need to be preregistered, otherwise GtkBuilder refuses to 
@@ -142,6 +199,7 @@ preregister_widgets (void)
   psppire_means_layer_get_type ();
 
   preregister_actions ();
+  preregister_misc ();
 
   /* This seems to be necessary on Cygwin.
      It ought not to be necessary.  Having it here can't do any harm. */