data_out function to dynamically allocate return value.
[pspp-builds.git] / src / ui / gui / psppire-data-store.c
index af205ccde2c8613c6653ccfe61449b5b459435c2..13ad0cc6cd283edfb9656a67f4eb2b3f7ae7873b 100644 (file)
@@ -1,11 +1,9 @@
-/* psppire-data-store.c
+/* PSPPIRE - a graphical user interface for PSPP.
+   Copyright (C) 2006, 2008, 2009  Free Software Foundation
 
 
-   PSPPIRE --- A Graphical User Interface for PSPP
-   Copyright (C) 2006  Free Software Foundation
-
-   This program is free software; you can redistribute it and/or modify
+   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
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   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,
    (at your option) any later version.
 
    This program is distributed in the hope that it will be useful,
@@ -14,9 +12,7 @@
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
    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, write to the Free Software
-   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-   02110-1301, USA. */
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
 #include <config.h>
 #include <string.h>
 
 #include <config.h>
 #include <string.h>
 #define _(msgid) gettext (msgid)
 #define N_(msgid) msgid
 
 #define _(msgid) gettext (msgid)
 #define N_(msgid) msgid
 
-#include <data/casewriter.h>
 #include <data/datasheet.h>
 #include <data/data-out.h>
 #include <data/variable.h>
 
 #include <data/datasheet.h>
 #include <data/data-out.h>
 #include <data/variable.h>
 
-#include <gtksheet/gtksheet.h>
-#include <gtksheet/gsheetmodel.h>
-#include <gtksheet/gsheet-column-iface.h>
+#include <ui/gui/sheet/psppire-sheetmodel.h>
+#include <ui/gui/psppire-marshal.h>
 
 #include <pango/pango-context.h>
 
 #include "psppire-data-store.h"
 
 #include <pango/pango-context.h>
 
 #include "psppire-data-store.h"
-#include "psppire-case-file.h"
+#include <libpspp/i18n.h>
 #include "helper.h"
 
 #include <data/dictionary.h>
 #include <data/missing-values.h>
 #include <data/value-labels.h>
 #include <data/data-in.h>
 #include "helper.h"
 
 #include <data/dictionary.h>
 #include <data/missing-values.h>
 #include <data/value-labels.h>
 #include <data/data-in.h>
+#include <data/format.h>
+
+#include <math/sort.h>
 
 
-#include <data/file-handle-def.h>
-#include <data/sys-file-writer.h>
+#include "xalloc.h"
+#include "xmalloca.h"
 
 
 
 static void psppire_data_store_init            (PsppireDataStore      *data_store);
 static void psppire_data_store_class_init      (PsppireDataStoreClass *class);
 
 
 
 static void psppire_data_store_init            (PsppireDataStore      *data_store);
 static void psppire_data_store_class_init      (PsppireDataStoreClass *class);
-static void psppire_data_store_sheet_model_init (GSheetModelIface *iface);
-static void psppire_data_store_sheet_column_init (GSheetColumnIface *iface);
-static void psppire_data_store_sheet_row_init (GSheetRowIface *iface);
+static void psppire_data_store_sheet_model_init (PsppireSheetModelIface *iface);
 
 static void psppire_data_store_finalize        (GObject           *object);
 
 static void psppire_data_store_finalize        (GObject           *object);
+static void psppire_data_store_dispose        (GObject           *object);
+
+static gboolean psppire_data_store_clear_datum (PsppireSheetModel *model,
+                                         glong row, glong column);
+
+
+static gboolean psppire_data_store_insert_case (PsppireDataStore *ds,
+                                               struct ccase *cc,
+                                               casenumber posn);
 
 
-static gboolean psppire_data_store_clear_datum (GSheetModel *model,
-                                         gint row, gint column);
 
 
+static gboolean psppire_data_store_data_in (PsppireDataStore *ds,
+                                           casenumber casenum, gint idx,
+                                           struct substring input,
+                                           const struct fmt_spec *fmt);
 
 
-#define MIN_COLUMNS 10
 
 
-#define TRAILING_ROWS 10
 
 static GObjectClass *parent_class = NULL;
 
 
 
 static GObjectClass *parent_class = NULL;
 
 
-enum  {FONT_CHANGED,
-       n_SIGNALS};
+enum
+  {
+    BACKEND_CHANGED,
+    CASES_DELETED,
+    CASE_INSERTED,
+    CASE_CHANGED,
+    n_SIGNALS
+  };
 
 static guint signals [n_SIGNALS];
 
 
 
 static guint signals [n_SIGNALS];
 
 
-inline GType
+GType
 psppire_data_store_get_type (void)
 {
   static GType data_store_type = 0;
 psppire_data_store_get_type (void)
 {
   static GType data_store_type = 0;
@@ -102,35 +112,15 @@ psppire_data_store_get_type (void)
        NULL
       };
 
        NULL
       };
 
-      static const GInterfaceInfo sheet_column_info =
-      {
-       (GInterfaceInitFunc) psppire_data_store_sheet_column_init,
-       NULL,
-       NULL
-      };
 
 
-      static const GInterfaceInfo sheet_row_info =
-      {
-       (GInterfaceInitFunc) psppire_data_store_sheet_row_init,
-       NULL,
-       NULL
-      };
-
-
-      data_store_type = g_type_register_static (G_TYPE_OBJECT, "PsppireDataStore",
+      data_store_type = g_type_register_static (G_TYPE_OBJECT,
+                                               "PsppireDataStore",
                                                &data_store_info, 0);
 
       g_type_add_interface_static (data_store_type,
                                                &data_store_info, 0);
 
       g_type_add_interface_static (data_store_type,
-                                  G_TYPE_SHEET_MODEL,
+                                  PSPPIRE_TYPE_SHEET_MODEL,
                                   &sheet_model_info);
 
                                   &sheet_model_info);
 
-      g_type_add_interface_static (data_store_type,
-                                  G_TYPE_SHEET_COLUMN,
-                                  &sheet_column_info);
-
-      g_type_add_interface_static (data_store_type,
-                                  G_TYPE_SHEET_ROW,
-                                  &sheet_row_info);
     }
 
   return data_store_type;
     }
 
   return data_store_type;
@@ -146,9 +136,10 @@ psppire_data_store_class_init (PsppireDataStoreClass *class)
   object_class = (GObjectClass*) class;
 
   object_class->finalize = psppire_data_store_finalize;
   object_class = (GObjectClass*) class;
 
   object_class->finalize = psppire_data_store_finalize;
+  object_class->dispose = psppire_data_store_dispose;
 
 
-  signals [FONT_CHANGED] =
-    g_signal_new ("font_changed",
+  signals [BACKEND_CHANGED] =
+    g_signal_new ("backend-changed",
                  G_TYPE_FROM_CLASS (class),
                  G_SIGNAL_RUN_FIRST,
                  0,
                  G_TYPE_FROM_CLASS (class),
                  G_SIGNAL_RUN_FIRST,
                  0,
@@ -156,56 +147,115 @@ psppire_data_store_class_init (PsppireDataStoreClass *class)
                  g_cclosure_marshal_VOID__VOID,
                  G_TYPE_NONE,
                  0);
                  g_cclosure_marshal_VOID__VOID,
                  G_TYPE_NONE,
                  0);
+
+  signals [CASE_INSERTED] =
+    g_signal_new ("case-inserted",
+                 G_TYPE_FROM_CLASS (class),
+                 G_SIGNAL_RUN_FIRST,
+                 0,
+                 NULL, NULL,
+                 g_cclosure_marshal_VOID__INT,
+                 G_TYPE_NONE,
+                 1,
+                 G_TYPE_INT);
+
+
+  signals [CASE_CHANGED] =
+    g_signal_new ("case-changed",
+                 G_TYPE_FROM_CLASS (class),
+                 G_SIGNAL_RUN_FIRST,
+                 0,
+                 NULL, NULL,
+                 g_cclosure_marshal_VOID__INT,
+                 G_TYPE_NONE,
+                 1,
+                 G_TYPE_INT);
+
+  signals [CASES_DELETED] =
+    g_signal_new ("cases-deleted",
+                 G_TYPE_FROM_CLASS (class),
+                 G_SIGNAL_RUN_FIRST,
+                 0,
+                 NULL, NULL,
+                 psppire_marshal_VOID__INT_INT,
+                 G_TYPE_NONE,
+                 2,
+                 G_TYPE_INT,
+                 G_TYPE_INT);
 }
 
 
 
 }
 
 
 
-static gint
-psppire_data_store_get_var_count (const GSheetModel *model)
+static gboolean
+psppire_data_store_insert_value (PsppireDataStore *ds,
+                                 gint width, gint where);
+
+static bool
+psppire_data_store_get_value (const PsppireDataStore *ds,
+                             casenumber casenum, size_t idx,
+                             union value *value);
+
+
+static gboolean
+psppire_data_store_set_value (PsppireDataStore *ds, casenumber casenum,
+                             gint idx, union value *v);
+
+
+
+
+static glong
+psppire_data_store_get_var_count (const PsppireSheetModel *model)
 {
   const PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
 
   return psppire_dict_get_var_cnt (store->dict);
 }
 
 {
   const PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
 
   return psppire_dict_get_var_cnt (store->dict);
 }
 
-static gint
-psppire_data_store_get_case_count (const GSheetModel *model)
+casenumber
+psppire_data_store_get_case_count (const PsppireDataStore *store)
 {
 {
-  const PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
-
-  return psppire_case_file_get_case_count (store->case_file);
+  return datasheet_get_n_rows (store->datasheet);
 }
 
 }
 
+size_t
+psppire_data_store_get_value_count (const PsppireDataStore *store)
+{
+  return psppire_dict_get_value_cnt (store->dict);
+}
 
 
-static void
-psppire_data_store_init (PsppireDataStore *data_store)
+const struct caseproto *
+psppire_data_store_get_proto (const PsppireDataStore *store)
 {
 {
-  data_store->dict = 0;
-  data_store->case_file = 0;
-  data_store->width_of_m = 10;
+  return psppire_dict_get_proto (store->dict);
 }
 
 }
 
-const PangoFontDescription *
-psppire_data_store_get_font_desc (const GSheetModel *model,
-                             gint row, gint column)
+static casenumber
+psppire_data_store_get_case_count_wrapper (const PsppireSheetModel *model)
 {
 {
-  PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
+  const PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
+  return psppire_data_store_get_case_count (store);
+}
 
 
-  return store->font_desc;
+static void
+psppire_data_store_init (PsppireDataStore *data_store)
+{
+  data_store->dict = 0;
+  data_store->datasheet = NULL;
+  data_store->dispose_has_run = FALSE;
 }
 
 static inline gchar *
 }
 
 static inline gchar *
-psppire_data_store_get_string_wrapper (const GSheetModel *model, gint row,
-                                      gint column)
+psppire_data_store_get_string_wrapper (const PsppireSheetModel *model, glong row,
+                                      glong column)
 {
   return psppire_data_store_get_string (PSPPIRE_DATA_STORE (model), row, column);
 }
 
 
 static inline gboolean
 {
   return psppire_data_store_get_string (PSPPIRE_DATA_STORE (model), row, column);
 }
 
 
 static inline gboolean
-psppire_data_store_set_string_wrapper (GSheetModel *model,
+psppire_data_store_set_string_wrapper (PsppireSheetModel *model,
                                       const gchar *text,
                                       const gchar *text,
-                                      gint row, gint column)
+                                      glong row, glong column)
 {
   return psppire_data_store_set_string (PSPPIRE_DATA_STORE (model), text,
                                        row, column);
 {
   return psppire_data_store_set_string (PSPPIRE_DATA_STORE (model), text,
                                        row, column);
@@ -213,156 +263,146 @@ psppire_data_store_set_string_wrapper (GSheetModel *model,
 
 
 
 
 
 
+static gchar * get_column_subtitle (const PsppireSheetModel *model, gint col);
+static gchar * get_column_button_label (const PsppireSheetModel *model, gint col);
+static gboolean get_column_sensitivity (const PsppireSheetModel *model, gint col);
+static GtkJustification get_column_justification (const PsppireSheetModel *model, gint col);
+
+static gchar * get_row_button_label (const PsppireSheetModel *model, gint row);
+static gboolean get_row_sensitivity (const PsppireSheetModel *model, gint row);
+static gboolean get_row_overstrike (const PsppireSheetModel *model, gint row);
+
 
 static void
 
 static void
-psppire_data_store_sheet_model_init (GSheetModelIface *iface)
+psppire_data_store_sheet_model_init (PsppireSheetModelIface *iface)
 {
   iface->free_strings = TRUE;
   iface->get_string = psppire_data_store_get_string_wrapper;
   iface->set_string = psppire_data_store_set_string_wrapper;
   iface->clear_datum = psppire_data_store_clear_datum;
   iface->is_editable = NULL;
 {
   iface->free_strings = TRUE;
   iface->get_string = psppire_data_store_get_string_wrapper;
   iface->set_string = psppire_data_store_set_string_wrapper;
   iface->clear_datum = psppire_data_store_clear_datum;
   iface->is_editable = NULL;
-  iface->is_visible = NULL;
   iface->get_foreground = NULL;
   iface->get_background = NULL;
   iface->get_foreground = NULL;
   iface->get_background = NULL;
-  iface->get_font_desc = psppire_data_store_get_font_desc;
-  iface->get_cell_border = NULL;
   iface->get_column_count = psppire_data_store_get_var_count;
   iface->get_column_count = psppire_data_store_get_var_count;
-  iface->get_row_count = psppire_data_store_get_case_count;
-}
-
-static
-gboolean always_true ()
-{
-  return TRUE;
-}
-
-
-static void
-delete_cases_callback (GtkWidget *w, gint first, gint n_cases, gpointer data)
-{
-  PsppireDataStore *store  ;
-
-  g_return_if_fail (data);
+  iface->get_row_count = psppire_data_store_get_case_count_wrapper;
 
 
-  store  = PSPPIRE_DATA_STORE (data);
-
-  g_assert (first >= 0);
+  iface->get_column_subtitle = get_column_subtitle;
+  iface->get_column_title = get_column_button_label;
+  iface->get_column_sensitivity = get_column_sensitivity;
+  iface->get_column_justification = get_column_justification;
 
 
-  g_sheet_model_rows_deleted (G_SHEET_MODEL (store), first, n_cases);
+  iface->get_row_title = get_row_button_label;
+  iface->get_row_sensitivity = get_row_sensitivity;
+  iface->get_row_overstrike = get_row_overstrike;
 }
 
 
 }
 
 
+/*
+   A callback which occurs after a variable has been deleted.
+ */
 static void
 static void
-insert_case_callback (GtkWidget *w, gint casenum, gpointer data)
+delete_variable_callback (GObject *obj, gint dict_index,
+                         gint case_index, gint width,
+                         gpointer data)
 {
 {
-  PsppireDataStore *store  ;
+  PsppireDataStore *store  = PSPPIRE_DATA_STORE (data);
 
 
-  g_return_if_fail (data);
 
 
-  store  = PSPPIRE_DATA_STORE (data);
+  psppire_sheet_model_columns_deleted (PSPPIRE_SHEET_MODEL (store), dict_index, 1);
+  datasheet_delete_columns (store->datasheet, case_index, 1);
+  datasheet_insert_column (store->datasheet, NULL, -1, case_index);
+#if AXIS_TRANSITION
 
 
-  g_sheet_model_range_changed (G_SHEET_MODEL (store),
-                              casenum, -1,
-                              psppire_case_file_get_case_count (store->case_file),
-                              -1);
 
 
-  g_sheet_model_rows_inserted (G_SHEET_MODEL (store), casenum, 1);
+  psppire_sheet_column_columns_changed (PSPPIRE_SHEET_COLUMN (store),
+                                  dict_index, -1);
+#endif
 }
 
 }
 
-
 static void
 static void
-changed_case_callback (GtkWidget *w, gint casenum, gpointer data)
+variable_changed_callback (GObject *obj, gint var_num, gpointer data)
 {
 {
-  PsppireDataStore *store  ;
-  g_return_if_fail (data);
+#if AXIS_TRANSITION
+  PsppireDataStore *store  = PSPPIRE_DATA_STORE (data);
 
 
-  store  = PSPPIRE_DATA_STORE (data);
+  psppire_sheet_column_columns_changed (PSPPIRE_SHEET_COLUMN (store),
+                                 var_num, 1);
 
 
-  g_sheet_model_range_changed (G_SHEET_MODEL (store),
-                                casenum, -1,
-                                casenum, -1);
+  psppire_sheet_model_range_changed (PSPPIRE_SHEET_MODEL (store),
+                              -1, var_num,
+                              -1, var_num);
+#endif
 }
 
 }
 
-
 static void
 static void
-delete_variables_callback (GObject *obj, gint var_num, gint n_vars, gpointer data)
+insert_variable_callback (GObject *obj, gint var_num, gpointer data)
 {
 {
-  PsppireDataStore *store ;
+  struct variable *variable;
+  PsppireDataStore *store;
+  gint posn;
 
   g_return_if_fail (data);
 
   store  = PSPPIRE_DATA_STORE (data);
 
 
   g_return_if_fail (data);
 
   store  = PSPPIRE_DATA_STORE (data);
 
-  g_sheet_model_columns_deleted (G_SHEET_MODEL (store), var_num, n_vars);
-
-  g_sheet_column_columns_changed (G_SHEET_COLUMN (store),
-                                  var_num, -1);
-}
+  variable = psppire_dict_get_variable (store->dict, var_num);
+  posn = var_get_case_index (variable);
+  psppire_data_store_insert_value (store, var_get_width (variable), posn);
 
 
+#if AXIS_TRANSITION
 
 
-static void
-variable_changed_callback (GObject *obj, gint var_num, gpointer data)
-{
-  PsppireDataStore *store;
-
-  g_return_if_fail (data);
-
-  store  = PSPPIRE_DATA_STORE (data);
-
-  g_sheet_column_columns_changed (G_SHEET_COLUMN (store),
+  psppire_sheet_column_columns_changed (PSPPIRE_SHEET_COLUMN (store),
                                  var_num, 1);
                                  var_num, 1);
+#endif
 
 
-
-  g_sheet_model_range_changed (G_SHEET_MODEL (store),
-                              -1, var_num,
-                              -1, var_num);
+  psppire_sheet_model_columns_inserted (PSPPIRE_SHEET_MODEL (store), var_num, 1);
 }
 
 }
 
-static void
-insert_variable_callback (GObject *obj, gint var_num, gpointer data)
-{
-  PsppireDataStore *store;
-  gint posn;
+struct resize_datum_aux
+  {
+    int old_width;
+    int new_width;
+  };
 
 
-  g_return_if_fail (data);
 
 
-  store  = PSPPIRE_DATA_STORE (data);
+void
+resize_datum (const union value *old, union value *new, void *aux_)
+{
+  struct resize_datum_aux *aux = aux_;
 
 
-  if ( var_num > 0 )
+  if (aux->new_width == 0)
     {
     {
-      struct variable *variable =
-       psppire_dict_get_variable (store->dict, var_num);
-
-      g_assert (variable != NULL);
-
-      posn = var_get_case_index (variable);
+      /* FIXME: try to parse string as number. */
+      new->f = SYSMIS;
     }
     }
-  else
+  else if (aux->old_width == 0)
     {
     {
-      posn = 0;
+      /* FIXME: format number as string. */
+      value_set_missing (new, aux->new_width);
     }
     }
-
-  psppire_case_file_insert_values (store->case_file, 1, posn);
-
-  g_sheet_column_columns_changed (G_SHEET_COLUMN (store),
-                                 var_num, 1);
-
-  g_sheet_model_columns_inserted (G_SHEET_MODEL (store), var_num, 1);
+  else
+    value_copy_rpad (new, aux->new_width, old, aux->old_width, ' ');
 }
 
 }
 
-
 static void
 dict_size_change_callback (GObject *obj,
 static void
 dict_size_change_callback (GObject *obj,
-                         gint posn, gint adjustment, gpointer data)
+                         gint var_num, gint old_width, gpointer data)
 {
 {
-  PsppireDataStore *store ;
+  PsppireDataStore *store  = PSPPIRE_DATA_STORE (data);
+  struct variable *variable;
+  int posn;
 
 
-  g_return_if_fail (data);
+  variable = psppire_dict_get_variable (store->dict, var_num);
+  posn = var_get_case_index (variable);
 
 
-  store  = PSPPIRE_DATA_STORE (data);
-
-  psppire_case_file_insert_values (store->case_file, adjustment, posn);
+  if (old_width != var_get_width (variable))
+    {
+      struct resize_datum_aux aux;
+      aux.old_width = old_width;
+      aux.new_width = var_get_width (variable);
+      datasheet_resize_column (store->datasheet, posn, aux.new_width,
+                               resize_datum, &aux);
+    }
 }
 
 
 }
 
 
@@ -386,34 +426,34 @@ psppire_data_store_new (PsppireDict *dict)
   return retval;
 }
 
   return retval;
 }
 
-
 void
 void
-psppire_data_store_set_case_file (PsppireDataStore *data_store,
-                                 PsppireCaseFile *cf)
+psppire_data_store_set_reader (PsppireDataStore *ds,
+                              struct casereader *reader)
 {
 {
-  if ( data_store->case_file)
-    {
-      g_object_unref (data_store->case_file);
-    }
+  gint i;
 
 
-  data_store->case_file = cf;
+  if ( ds->datasheet)
+    datasheet_destroy (ds->datasheet);
 
 
-  g_signal_connect (data_store->case_file, "cases-deleted",
-                  G_CALLBACK (delete_cases_callback),
-                  data_store);
+  ds->datasheet = datasheet_create (reader);
 
 
-  g_signal_connect (data_store->case_file, "case-inserted",
-                  G_CALLBACK (insert_case_callback),
-                  data_store);
+  psppire_sheet_model_range_changed (PSPPIRE_SHEET_MODEL (ds),
+                              -1, -1, -1, -1);
 
 
-
-  g_signal_connect (data_store->case_file, "case-changed",
-                  G_CALLBACK (changed_case_callback),
-                  data_store);
+  if ( ds->dict )
+    for (i = 0 ; i < n_dict_signals; ++i )
+      {
+       if ( ds->dict_handler_id [i] > 0)
+         {
+           g_signal_handler_unblock (ds->dict,
+                                     ds->dict_handler_id[i]);
+         }
+      }
+
+  g_signal_emit (ds, signals[BACKEND_CHANGED], 0);
 }
 
 
 }
 
 
-
 /**
  * psppire_data_store_replace_set_dictionary:
  * @data_store: The variable store
 /**
  * psppire_data_store_replace_set_dictionary:
  * @data_store: The variable store
@@ -425,29 +465,60 @@ psppire_data_store_set_case_file (PsppireDataStore *data_store,
 void
 psppire_data_store_set_dictionary (PsppireDataStore *data_store, PsppireDict *dict)
 {
 void
 psppire_data_store_set_dictionary (PsppireDataStore *data_store, PsppireDict *dict)
 {
-  data_store->dict = dict;
+  int i;
+
+  /* Disconnect any existing handlers */
+  if ( data_store->dict )
+    for (i = 0 ; i < n_dict_signals; ++i )
+      {
+       g_signal_handler_disconnect (data_store->dict,
+                                    data_store->dict_handler_id[i]);
+      }
 
 
-  g_signal_connect (dict, "variable-inserted",
-                  G_CALLBACK (insert_variable_callback),
-                  data_store);
+  data_store->dict = dict;
 
 
-  g_signal_connect (dict, "variables-deleted",
-                  G_CALLBACK (delete_variables_callback),
-                  data_store);
+  if ( dict != NULL)
+    {
 
 
-  g_signal_connect (dict, "variable-changed",
-                  G_CALLBACK (variable_changed_callback),
-                  data_store);
+      data_store->dict_handler_id [VARIABLE_INSERTED] =
+       g_signal_connect (dict, "variable-inserted",
+                         G_CALLBACK (insert_variable_callback),
+                         data_store);
+
+      data_store->dict_handler_id [VARIABLE_DELETED] =
+       g_signal_connect (dict, "variable-deleted",
+                         G_CALLBACK (delete_variable_callback),
+                         data_store);
+
+      data_store->dict_handler_id [VARIABLE_CHANGED] =
+       g_signal_connect (dict, "variable-changed",
+                         G_CALLBACK (variable_changed_callback),
+                         data_store);
+
+      data_store->dict_handler_id [SIZE_CHANGED] =
+       g_signal_connect (dict, "dict-size-changed",
+                         G_CALLBACK (dict_size_change_callback),
+                         data_store);
+    }
 
 
 
 
-  g_signal_connect (dict, "dict-size-changed",
-                   G_CALLBACK (dict_size_change_callback),
-                   data_store);
 
   /* The entire model has changed */
 
   /* The entire model has changed */
-  g_sheet_model_range_changed (G_SHEET_MODEL (data_store), -1, -1, -1, -1);
+  psppire_sheet_model_range_changed (PSPPIRE_SHEET_MODEL (data_store), -1, -1, -1, -1);
+
+#if AXIS_TRANSITION
+  psppire_sheet_column_columns_changed (PSPPIRE_SHEET_COLUMN (data_store), 0, -1);
+#endif
 
 
-  g_sheet_column_columns_changed (G_SHEET_COLUMN (data_store), 0, -1);
+  if ( data_store->dict )
+    for (i = 0 ; i < n_dict_signals; ++i )
+      {
+       if ( data_store->dict_handler_id [i] > 0)
+         {
+           g_signal_handler_block (data_store->dict,
+                                   data_store->dict_handler_id[i]);
+         }
+      }
 }
 
 static void
 }
 
 static void
@@ -459,58 +530,69 @@ psppire_data_store_finalize (GObject *object)
 }
 
 
 }
 
 
+static void
+psppire_data_store_dispose (GObject *object)
+{
+  PsppireDataStore *ds = PSPPIRE_DATA_STORE (object);
+
+  if (ds->dispose_has_run)
+    return;
+
+  if (ds->datasheet)
+    {
+      datasheet_destroy (ds->datasheet);
+      ds->datasheet = NULL;
+    }
+
+  /* must chain up */
+  (* parent_class->dispose) (object);
+
+  ds->dispose_has_run = TRUE;
+}
+
+
 
 /* Insert a blank case before POSN */
 gboolean
 
 /* Insert a blank case before POSN */
 gboolean
-psppire_data_store_insert_new_case (PsppireDataStore *ds, gint posn)
+psppire_data_store_insert_new_case (PsppireDataStore *ds, casenumber posn)
 {
   gboolean result;
 {
   gboolean result;
-  gint val_cnt, v;
-  struct ccase cc;
+  const struct caseproto *proto;
+  struct ccase *cc;
   g_return_val_if_fail (ds, FALSE);
 
   g_return_val_if_fail (ds, FALSE);
 
+  proto = datasheet_get_proto (ds->datasheet);
+  g_return_val_if_fail (caseproto_get_n_widths (proto) > 0, FALSE);
+  g_return_val_if_fail (posn <= psppire_data_store_get_case_count (ds), FALSE);
 
 
-  /* Opportunity for optimisation exists here when creating a blank case */
-  val_cnt = datasheet_get_column_cnt (ds->case_file->datasheet) ;
+  cc = case_create (proto);
+  case_set_missing (cc);
 
 
-  case_create (&cc, val_cnt);
+  result = psppire_data_store_insert_case (ds, cc, posn);
 
 
-  memset ( case_data_rw_idx (&cc, 0), 0, val_cnt * MAX_SHORT_STRING);
-
-  for (v = 0 ; v < psppire_dict_get_var_cnt (ds->dict) ; ++v)
-    {
-      const struct variable *pv = psppire_dict_get_variable (ds->dict, v);
-      if ( var_is_alpha (pv))
-       continue;
-
-      case_data_rw (&cc, pv)->f = SYSMIS;
-    }
-
-  result = psppire_case_file_insert_case (ds->case_file, &cc, posn);
-
-  case_destroy (&cc);
+  case_unref (cc);
 
   return result;
 }
 
 
 gchar *
 
   return result;
 }
 
 
 gchar *
-psppire_data_store_get_string (PsppireDataStore *store, gint row, gint column)
+psppire_data_store_get_string (PsppireDataStore *store, glong row, glong column)
 {
   gint idx;
   char *text;
   const struct fmt_spec *fp ;
   const struct variable *pv ;
 {
   gint idx;
   char *text;
   const struct fmt_spec *fp ;
   const struct variable *pv ;
-  union value *v ;
-  GString *s;
+  union value v;
+  int width;
 
   g_return_val_if_fail (store->dict, NULL);
 
   g_return_val_if_fail (store->dict, NULL);
-  g_return_val_if_fail (store->case_file, NULL);
+  g_return_val_if_fail (store->datasheet, NULL);
 
   if (column >= psppire_dict_get_var_cnt (store->dict))
     return NULL;
 
 
   if (column >= psppire_dict_get_var_cnt (store->dict))
     return NULL;
 
-  if ( row >= psppire_case_file_get_case_count (store->case_file))
+  if ( row >= psppire_data_store_get_case_count (store))
     return NULL;
 
   pv = psppire_dict_get_variable (store->dict, column);
     return NULL;
 
   pv = psppire_dict_get_variable (store->dict, column);
@@ -518,67 +600,58 @@ psppire_data_store_get_string (PsppireDataStore *store, gint row, gint column)
   g_assert (pv);
 
   idx = var_get_case_index (pv);
   g_assert (pv);
 
   idx = var_get_case_index (pv);
+  width = var_get_width (pv);
 
   g_assert (idx >= 0);
 
 
   g_assert (idx >= 0);
 
-  v = psppire_case_file_get_value (store->case_file, row, idx, NULL,
-                                   var_get_width (pv));
-
-  g_return_val_if_fail (v, NULL);
+  value_init (&v, width);
+  if (!psppire_data_store_get_value (store, row, idx, &v))
+    return NULL;
 
   if ( store->show_labels)
     {
 
   if ( store->show_labels)
     {
-      const gchar *label = var_lookup_value_label (pv, v);
+      const gchar *label = var_lookup_value_label (pv, &v);
       if (label)
         {
       if (label)
         {
-          free (v);
-         return pspp_locale_to_utf8 (label, -1, 0);
+          value_destroy (&v, width);
+         return recode_string (UTF8, psppire_dict_encoding (store->dict),
+                               label, -1);
         }
     }
 
   fp = var_get_write_format (pv);
 
         }
     }
 
   fp = var_get_write_format (pv);
 
-  s = g_string_sized_new (fp->w + 1);
-  g_string_set_size (s, fp->w);
-
-  memset (s->str, 0, fp->w);
-
-  g_assert (fp->w == s->len);
-
   /* Converts binary value V into printable form in the exactly
      FP->W character in buffer S according to format specification
      FP.  No null terminator is appended to the buffer.  */
   /* Converts binary value V into printable form in the exactly
      FP->W character in buffer S according to format specification
      FP.  No null terminator is appended to the buffer.  */
-  data_out (v, fp, s->str);
-
-  text = pspp_locale_to_utf8 (s->str, fp->w, 0);
-  g_string_free (s, TRUE);
+  text = data_out (&v, fp);
 
   g_strchomp (text);
 
 
   g_strchomp (text);
 
-  free (v);
+  value_destroy (&v, width);
   return text;
 }
 
 
 static gboolean
   return text;
 }
 
 
 static gboolean
-psppire_data_store_clear_datum (GSheetModel *model,
-                                         gint row, gint col)
-
+psppire_data_store_clear_datum (PsppireSheetModel *model,
+                                         glong row, glong col)
 {
   PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
 
   union value v;
   const struct variable *pv = psppire_dict_get_variable (store->dict, col);
 {
   PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
 
   union value v;
   const struct variable *pv = psppire_dict_get_variable (store->dict, col);
+  int width = var_get_width (pv);
 
   const gint index = var_get_case_index (pv) ;
 
 
   const gint index = var_get_case_index (pv) ;
 
-  if ( var_is_numeric (pv))
-    v.f = SYSMIS;
-  else
-    memcpy (v.s, "", MAX_SHORT_STRING);
+  value_init (&v, width);
+  value_set_missing (&v, width);
+  psppire_data_store_set_value (store, row, index, &v);
+  value_destroy (&v, width);
+
+  psppire_sheet_model_range_changed (model, row, col, row, col);
 
 
-  psppire_case_file_set_value (store->case_file, row, index, &v,
-                             var_get_width (pv));
 
   return TRUE;
 }
 
   return TRUE;
 }
@@ -590,178 +663,174 @@ psppire_data_store_clear_datum (GSheetModel *model,
 */
 gboolean
 psppire_data_store_set_string (PsppireDataStore *store,
 */
 gboolean
 psppire_data_store_set_string (PsppireDataStore *store,
-                              const gchar *text, gint row, gint col)
+                              const gchar *text, glong row, glong col)
 {
 {
+  gchar *s;
+  glong n_cases;
   const struct variable *pv = psppire_dict_get_variable (store->dict, col);
   const struct variable *pv = psppire_dict_get_variable (store->dict, col);
-  g_return_val_if_fail (pv, FALSE);
+  if ( NULL == pv)
+    return FALSE;
 
 
-#if 0
-  /* Allow the user to insert a lot of blank cases, simply by skipping rows */
-  for (r = psppire_case_file_get_case_count (store->case_file); r <= row ; ++r)
-    {
+  n_cases = psppire_data_store_get_case_count (store);
 
 
-      gint c;
+  if ( row > n_cases)
+    return FALSE;
 
 
-      psppire_case_array_insert_case (store->cases, r, 0, 0);
+  if (row == n_cases)
+    psppire_data_store_insert_new_case (store, row);
 
 
+  s = recode_string (psppire_dict_encoding (store->dict), UTF8, text, -1);
 
 
-      for (c = 0 ; c < psppire_dict_get_var_cnt (store->dict); ++c )
-       psppire_data_store_clear_datum (model, r, c);
-    }
-#endif
+  psppire_data_store_data_in (store, row,
+                             var_get_case_index (pv), ss_cstr (s),
+                             var_get_write_format (pv));
+  free (s);
 
 
-  psppire_case_file_data_in (store->case_file, row,
-                             var_get_case_index (pv), ss_cstr (text),
-                             var_get_write_format (pv));
+  psppire_sheet_model_range_changed (PSPPIRE_SHEET_MODEL (store), row, col, row, col);
 
   return TRUE;
 }
 
 
 
   return TRUE;
 }
 
 
+
 void
 void
-psppire_data_store_set_font (PsppireDataStore *store,
-                           const PangoFontDescription *fd)
+psppire_data_store_show_labels (PsppireDataStore *store, gboolean show_labels)
 {
   g_return_if_fail (store);
   g_return_if_fail (PSPPIRE_IS_DATA_STORE (store));
 
 {
   g_return_if_fail (store);
   g_return_if_fail (PSPPIRE_IS_DATA_STORE (store));
 
-  store->font_desc = fd;
-#if 0
-  store->width_of_m = calc_m_width (fd);
-#endif
-  g_signal_emit (store, signals [FONT_CHANGED], 0);
-
+  store->show_labels = show_labels;
 
 
-  g_sheet_model_range_changed (G_SHEET_MODEL (store),
+  psppire_sheet_model_range_changed (PSPPIRE_SHEET_MODEL (store),
                                 -1, -1, -1, -1);
 }
 
 
 void
                                 -1, -1, -1, -1);
 }
 
 
 void
-psppire_data_store_show_labels (PsppireDataStore *store, gboolean show_labels)
+psppire_data_store_clear (PsppireDataStore *ds)
 {
 {
-  g_return_if_fail (store);
-  g_return_if_fail (PSPPIRE_IS_DATA_STORE (store));
+  datasheet_destroy (ds->datasheet);
+  ds->datasheet = NULL;
 
 
-  store->show_labels = show_labels;
+  psppire_dict_clear (ds->dict);
 
 
-  g_sheet_model_range_changed (G_SHEET_MODEL (store),
-                                -1, -1, -1, -1);
+  g_signal_emit (ds, signals [CASES_DELETED], 0, 0, -1);
 }
 
 
 
 }
 
 
 
-/* FIXME: There's no reason to actually have this function.
-   It should be done by a procedure */
-void
-psppire_data_store_create_system_file (PsppireDataStore *store,
-                             struct file_handle *handle)
-{
-  gint i, var_cnt;
-  const struct sfm_write_options wo = {
-    true, /* writeable */
-    false, /* dont compress */
-    3 /* version */
-  };
+/* Return a casereader made from this datastore */
+struct casereader *
+psppire_data_store_get_reader (PsppireDataStore *ds)
+{
+  int i;
+  struct casereader *reader ;
 
 
-  struct casewriter *writer;
+  if ( ds->dict )
+    for (i = 0 ; i < n_dict_signals; ++i )
+      {
+       g_signal_handler_block (ds->dict,
+                               ds->dict_handler_id[i]);
+      }
 
 
-  g_assert (handle);
+  reader = datasheet_make_reader (ds->datasheet);
 
 
-  writer = sfm_open_writer (handle, store->dict->dict, wo);
+  /* We must not reference this again */
+  ds->datasheet = NULL;
 
 
-  if ( ! writer)
-    return;
+  return reader;
+}
 
 
 
 
-  var_cnt = psppire_data_store_get_var_count (G_SHEET_MODEL (store));
 
 
-  for (i = 0 ; i < psppire_case_file_get_case_count (store->case_file); ++i )
-    {
-      struct ccase c;
-      psppire_case_file_get_case (store->case_file, i, &c);
-      casewriter_write (writer, &c);
-    }
-  casewriter_destroy (writer);
-}
+/* Column related funcs */
 
 
 
 
+static const gchar null_var_name[]=N_("var");
 
 
-void
-psppire_data_store_clear (PsppireDataStore *data_store)
+\f
+
+/* Row related funcs */
+
+static gchar *
+get_row_button_label (const PsppireSheetModel *model, gint unit)
 {
 {
-  psppire_case_file_clear (data_store->case_file);
+  PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
+  gchar *s = g_strdup_printf (_("%d"), unit + FIRST_CASE_NUMBER);
 
 
-  psppire_dict_clear (data_store->dict);
-}
+  gchar *text =  recode_string (UTF8, psppire_dict_encoding (ds->dict),
+                               s, -1);
+
+  g_free (s);
 
 
+  return text;
+}
 
 
 
 
-/* Return a casereader made from this datastore */
-struct casereader *
-psppire_data_store_get_reader (PsppireDataStore *ds)
+static gboolean
+get_row_sensitivity (const PsppireSheetModel *model, gint unit)
 {
 {
-  struct casereader *reader ;
-
-  reader = psppire_case_file_make_reader (ds->case_file);
+  PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
 
 
-  return reader;
+  return (unit < psppire_data_store_get_case_count (ds));
 }
 
 
 }
 
 
+\f
 
 
-/* Column related funcs */
+/* Column related stuff */
 
 
-static gint
-geometry_get_column_count (const GSheetColumn *geom)
+static gchar *
+get_column_subtitle (const PsppireSheetModel *model, gint col)
 {
 {
-  PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
+  const struct variable *v ;
+  PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
 
 
-  return MAX (MIN_COLUMNS, psppire_dict_get_var_cnt (ds->dict));
-}
+  if ( col >= psppire_dict_get_var_cnt (ds->dict) )
+    return NULL;
 
 
+  v = psppire_dict_get_variable (ds->dict, col);
 
 
+  if ( ! var_has_label (v))
+    return NULL;
 
 
-static gint
-geometry_get_width (const GSheetColumn *geom, gint unit)
-{
-  const struct variable *pv ;
-  PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
+  return xstrdup (var_get_label (v));
+}
 
 
-  if ( unit >= psppire_dict_get_var_cnt (ds->dict) )
-    return ds->width_of_m * 8 ;
+static gchar *
+get_column_button_label (const PsppireSheetModel *model, gint col)
+{
+  struct variable *pv ;
+  PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
 
 
-  pv = psppire_dict_get_variable (ds->dict, unit);
+  if ( col >= psppire_dict_get_var_cnt (ds->dict) )
+    return g_locale_to_utf8 (null_var_name, -1, 0, 0, 0);
 
 
-  if ( pv == NULL )
-    return ds->width_of_m * 8 ;
+  pv = psppire_dict_get_variable (ds->dict, col);
 
 
-  return ds->width_of_m * var_get_display_width (pv);
+  return xstrdup (var_get_name (pv));
 }
 
 }
 
-static void
-geometry_set_width (GSheetColumn *geom, gint unit, gint width)
+static gboolean
+get_column_sensitivity (const PsppireSheetModel *model, gint col)
 {
 {
-  PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
+  PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
 
 
-  struct variable *pv = psppire_dict_get_variable (ds->dict, unit);
-
-  var_set_display_width (pv, width / ds->width_of_m );
+  return (col < psppire_dict_get_var_cnt (ds->dict));
 }
 
 
 
 static GtkJustification
 }
 
 
 
 static GtkJustification
-geometry_get_justification (const GSheetColumn *geom, gint unit)
+get_column_justification (const PsppireSheetModel *model, gint col)
 {
 {
-  PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
+  PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
   const struct variable *pv ;
 
   const struct variable *pv ;
 
-
-  if ( unit >= psppire_dict_get_var_cnt (ds->dict) )
+  if ( col >= psppire_dict_get_var_cnt (ds->dict) )
     return GTK_JUSTIFY_LEFT;
 
     return GTK_JUSTIFY_LEFT;
 
-  pv = psppire_dict_get_variable (ds->dict, unit);
+  pv = psppire_dict_get_variable (ds->dict, col);
 
   return (var_get_alignment (pv) == ALIGN_LEFT ? GTK_JUSTIFY_LEFT
           : var_get_alignment (pv) == ALIGN_RIGHT ? GTK_JUSTIFY_RIGHT
 
   return (var_get_alignment (pv) == ALIGN_LEFT ? GTK_JUSTIFY_LEFT
           : var_get_alignment (pv) == ALIGN_RIGHT ? GTK_JUSTIFY_RIGHT
@@ -769,109 +838,193 @@ geometry_get_justification (const GSheetColumn *geom, gint unit)
 }
 
 
 }
 
 
-static const gchar null_var_name[]=N_("var");
 
 
-static gchar *
-geometry_get_column_button_label (const GSheetColumn *geom, gint unit)
+\f
+
+
+/* Returns the CASENUMth case, or a null pointer on failure.
+ */
+struct ccase *
+psppire_data_store_get_case (const PsppireDataStore *ds,
+                            casenumber casenum)
 {
 {
-  gchar *text;
-  struct variable *pv ;
-  PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
+  g_return_val_if_fail (ds, FALSE);
+  g_return_val_if_fail (ds->datasheet, FALSE);
 
 
-  if ( unit >= psppire_dict_get_var_cnt (ds->dict) )
-    return g_locale_to_utf8 (null_var_name, -1, 0, 0, 0);
+  return datasheet_get_row (ds->datasheet, casenum);
+}
 
 
-  pv = psppire_dict_get_variable (ds->dict, unit);
 
 
-  text =  pspp_locale_to_utf8 (var_get_name (pv), -1, 0);
+gboolean
+psppire_data_store_delete_cases (PsppireDataStore *ds, casenumber first,
+                                casenumber n_cases)
+{
+  g_return_val_if_fail (ds, FALSE);
+  g_return_val_if_fail (ds->datasheet, FALSE);
 
 
-  return text;
+  g_return_val_if_fail (first + n_cases <=
+                       psppire_data_store_get_case_count (ds), FALSE);
+
+
+  datasheet_delete_rows (ds->datasheet, first, n_cases);
+
+  g_signal_emit (ds, signals [CASES_DELETED], 0, first, n_cases);
+  psppire_sheet_model_rows_deleted (PSPPIRE_SHEET_MODEL (ds), first, n_cases);
+
+  return TRUE;
 }
 
 
 }
 
 
+
+/* Insert case CC into the case file before POSN */
 static gboolean
 static gboolean
-geometry_get_sensitivity (const GSheetColumn *geom, gint unit)
+psppire_data_store_insert_case (PsppireDataStore *ds,
+                               struct ccase *cc,
+                               casenumber posn)
 {
 {
-  PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
+  bool result ;
+
+  g_return_val_if_fail (ds, FALSE);
+  g_return_val_if_fail (ds->datasheet, FALSE);
+
+  case_ref (cc);
+  result = datasheet_insert_rows (ds->datasheet, posn, &cc, 1);
+
+  if ( result )
+    {
+      g_signal_emit (ds, signals [CASE_INSERTED], 0, posn);
+      psppire_sheet_model_rows_inserted (PSPPIRE_SHEET_MODEL (ds), posn, 1);
+    }
+  else
+    g_warning ("Cannot insert case at position %ld\n", posn);
 
 
-  return (unit < psppire_dict_get_var_cnt (ds->dict));
+  return result;
 }
 
 
 }
 
 
-static void
-psppire_data_store_sheet_column_init (GSheetColumnIface *iface)
+/* Copies the IDXth value from case CASENUM into VALUE, which
+   must be of the correct width for IDX.
+   Returns true if successful, false on failure. */
+static bool
+psppire_data_store_get_value (const PsppireDataStore *ds,
+                             casenumber casenum, size_t idx,
+                             union value *value)
 {
 {
-  iface->get_column_count = geometry_get_column_count;
-  iface->get_width = geometry_get_width;
-  iface->set_width = geometry_set_width;
-  iface->get_visibility = always_true;
-  iface->get_sensitivity = geometry_get_sensitivity;
-  iface->get_justification = geometry_get_justification;
-  iface->get_button_label = geometry_get_column_button_label;
+  g_return_val_if_fail (ds, false);
+  g_return_val_if_fail (ds->datasheet, false);
+  g_return_val_if_fail (idx < datasheet_get_n_columns (ds->datasheet), false);
+
+  return datasheet_get_value (ds->datasheet, casenum, idx, value);
 }
 
 
 }
 
 
-/* Row related funcs */
 
 
-static gint
-geometry_get_row_count (const GSheetRow *geom, gpointer data)
+/* Set the IDXth value of case C to V.
+   V must be the correct width for IDX.
+   Returns true if successful, false on I/O error. */
+static gboolean
+psppire_data_store_set_value (PsppireDataStore *ds, casenumber casenum,
+                             gint idx, union value *v)
 {
 {
-  PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
+  bool ok;
 
 
-  return TRAILING_ROWS + psppire_case_file_get_case_count (ds->case_file);
-}
+  g_return_val_if_fail (ds, FALSE);
+  g_return_val_if_fail (ds->datasheet, FALSE);
 
 
+  g_return_val_if_fail (idx < datasheet_get_n_columns (ds->datasheet), FALSE);
 
 
-static gint
-geometry_get_height (const GSheetRow *geom, gint unit, gpointer data)
-{
-  return 25;
+  ok = datasheet_put_value (ds->datasheet, casenum, idx, v);
+  if (ok)
+    g_signal_emit (ds, signals [CASE_CHANGED], 0, casenum);
+
+  return ok;
 }
 
 
 }
 
 
+
+
+/* Set the IDXth value of case C using D_IN */
 static gboolean
 static gboolean
-geometry_get_row_sensitivity (const GSheetRow *geom, gint unit, gpointer data)
+psppire_data_store_data_in (PsppireDataStore *ds, casenumber casenum, gint idx,
+                           struct substring input, const struct fmt_spec *fmt)
 {
 {
-  PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
+  union value value;
+  int width;
+  bool ok;
 
 
+  g_return_val_if_fail (ds, FALSE);
+  g_return_val_if_fail (ds->datasheet, FALSE);
 
 
-  return (unit < psppire_case_file_get_case_count (ds->case_file));
-}
+  g_return_val_if_fail (idx < datasheet_get_n_columns (ds->datasheet), FALSE);
 
 
+  width = fmt_var_width (fmt);
+  g_return_val_if_fail (caseproto_get_width (
+                          datasheet_get_proto (ds->datasheet), idx) == width,
+                        FALSE);
+  value_init (&value, width);
+  ok = (datasheet_get_value (ds->datasheet, casenum, idx, &value)
+        && data_in (input, LEGACY_NATIVE, fmt->type, 0, 0, 0, &value, width)
+        && datasheet_put_value (ds->datasheet, casenum, idx, &value));
+  value_destroy (&value, width);
 
 
-static gchar *
-geometry_get_row_button_label (const GSheetRow *geom, gint unit, gpointer data)
+  if (ok)
+    g_signal_emit (ds, signals [CASE_CHANGED], 0, casenum);
+
+  return ok;
+}
+
+/* Resize the cases in the casefile, by inserting a value of the
+   given WIDTH into every one of them at the position immediately
+   preceding WHERE.
+*/
+static gboolean
+psppire_data_store_insert_value (PsppireDataStore *ds,
+                                 gint width, gint where)
 {
 {
-  gchar *text;
-  gchar *s;
-  PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
+  union value value;
 
 
-  if ( unit >
-       TRAILING_ROWS + psppire_case_file_get_case_count (ds->case_file))
-    return 0;
+  g_return_val_if_fail (ds, FALSE);
 
 
-  s = g_strdup_printf (_("%d"), unit);
+  g_assert (width >= 0);
 
 
-  text =  pspp_locale_to_utf8 (s, -1, 0);
+  if ( ! ds->datasheet )
+    ds->datasheet = datasheet_create (NULL);
 
 
-  g_free (s);
+  value_init (&value, width);
+  if (width == 0)
+    value.f = 0;
+  else
+    value_set_missing (&value, width);
 
 
-  return text;
-}
+  datasheet_insert_column (ds->datasheet, &value, width, where);
 
 
+  return TRUE;
+}
 
 
-static void
-psppire_data_store_sheet_row_init (GSheetRowIface *iface)
+static gboolean
+get_row_overstrike (const PsppireSheetModel *model, gint row)
 {
 {
-  iface->get_row_count = geometry_get_row_count;
+  union value val;
+  PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
 
 
-  iface->get_height = geometry_get_height;
-  iface->set_height = 0;
-  iface->get_visibility = always_true;
-  iface->get_sensitivity = geometry_get_row_sensitivity;
+  const struct dictionary *dict = ds->dict->dict;
 
 
-  iface->get_button_label = geometry_get_row_button_label;
-}
+  const struct variable *filter = dict_get_filter (dict);
 
 
+  if ( row < 0 || row >= datasheet_get_n_rows (ds->datasheet))
+    return FALSE;
 
 
+  if ( ! filter)
+    return FALSE;
 
 
+  g_assert (var_is_numeric (filter));
+
+  value_init (&val, 0);
+  if ( ! datasheet_get_value (ds->datasheet, row,
+                             var_get_case_index (filter),
+                             &val) )
+    return FALSE;
+
+
+  return (val.f == 0.0);
+}