Add range_changed callbacks as appropriate
[pspp] / src / ui / gui / psppire-data-store.c
index 7dca919c019ed11610943029231eedb13c8323cd..ce7b2518fbcd9ac3f6d5a4d6ad453f738c3236a7 100644 (file)
@@ -1,11 +1,9 @@
-/* psppire-data-store.c
+/* PSPPIRE - a graphical user interface for PSPP.
+   Copyright (C) 2006, 2008  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>
 #include <data/data-out.h>
 #include <data/variable.h>
 
 #include <data/data-out.h>
 #include <data/variable.h>
 
-#include <gtksheet/gtksheet.h>
 #include <gtksheet/gsheetmodel.h>
 #include <gtksheet/gsheetmodel.h>
-#include <gtksheet/gsheet-column-iface.h>
+#include <gtksheet/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 "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 "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_sheet_model_init (GSheetModelIface *iface);
 
 
 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_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 (GSheetModel *model,
 
 static gboolean psppire_data_store_clear_datum (GSheetModel *model,
-                                         gint row, gint column);
+                                         glong row, glong column);
+
 
 
+static gboolean psppire_data_store_insert_case (PsppireDataStore *ds,
+                                               struct ccase *cc,
+                                               casenumber posn);
+
+
+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;
@@ -97,35 +111,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,
                                   G_TYPE_SHEET_MODEL,
                                   &sheet_model_info);
 
                                                &data_store_info, 0);
 
       g_type_add_interface_static (data_store_type,
                                   G_TYPE_SHEET_MODEL,
                                   &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;
@@ -141,9 +135,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,
@@ -151,11 +146,63 @@ 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
+static gboolean
+psppire_data_store_insert_values (PsppireDataStore *ds,
+                                 gint n_values, gint where);
+
+static union value *
+psppire_data_store_get_value (const PsppireDataStore *ds,
+                             casenumber casenum, size_t idx,
+                             union value *value, int width);
+
+
+static gboolean
+psppire_data_store_set_value (PsppireDataStore *ds, casenumber casenum,
+                             gint idx, union value *v, gint width);
+
+
+
+
+static glong
 psppire_data_store_get_var_count (const GSheetModel *model)
 {
   const PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
 psppire_data_store_get_var_count (const GSheetModel *model)
 {
   const PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
@@ -163,35 +210,36 @@ psppire_data_store_get_var_count (const GSheetModel *model)
   return psppire_dict_get_var_cnt (store->dict);
 }
 
   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 datasheet_get_row_cnt (store->datasheet);
+}
 
 
-  return psppire_case_file_get_case_count (store->case_file);
+size_t
+psppire_data_store_get_value_count (const PsppireDataStore *store)
+{
+  return psppire_dict_get_value_cnt (store->dict);
 }
 
 }
 
+static casenumber
+psppire_data_store_get_case_count_wrapper (const GSheetModel *model)
+{
+  const PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
+  return psppire_data_store_get_case_count (store);
+}
 
 static void
 psppire_data_store_init (PsppireDataStore *data_store)
 {
   data_store->dict = 0;
 
 static void
 psppire_data_store_init (PsppireDataStore *data_store)
 {
   data_store->dict = 0;
-  data_store->case_file = 0;
-  data_store->width_of_m = 10;
-}
-
-const PangoFontDescription *
-psppire_data_store_get_font_desc (const GSheetModel *model,
-                             gint row, gint column)
-{
-  PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
-
-  return store->font_desc;
+  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 GSheetModel *model, glong row,
+                                      glong column)
 {
   return psppire_data_store_get_string (PSPPIRE_DATA_STORE (model), row, column);
 }
 {
   return psppire_data_store_get_string (PSPPIRE_DATA_STORE (model), row, column);
 }
@@ -200,7 +248,7 @@ psppire_data_store_get_string_wrapper (const GSheetModel *model, gint row,
 static inline gboolean
 psppire_data_store_set_string_wrapper (GSheetModel *model,
                                       const gchar *text,
 static inline gboolean
 psppire_data_store_set_string_wrapper (GSheetModel *model,
                                       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);
@@ -208,6 +256,14 @@ psppire_data_store_set_string_wrapper (GSheetModel *model,
 
 
 
 
 
 
+static gchar * get_column_subtitle (const GSheetModel *model, gint col);
+static gchar * get_column_button_label (const GSheetModel *model, gint col);
+static gboolean get_column_sensitivity (const GSheetModel *model, gint col);
+static GtkJustification get_column_justification (const GSheetModel *model, gint col);
+
+static gchar * get_row_button_label (const GSheetModel *model, gint row);
+static gboolean get_row_sensitivity (const GSheetModel *model, gint row);
+
 
 static void
 psppire_data_store_sheet_model_init (GSheetModelIface *iface)
 
 static void
 psppire_data_store_sheet_model_init (GSheetModelIface *iface)
@@ -217,94 +273,50 @@ psppire_data_store_sheet_model_init (GSheetModelIface *iface)
   iface->set_string = psppire_data_store_set_string_wrapper;
   iface->clear_datum = psppire_data_store_clear_datum;
   iface->is_editable = NULL;
   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_cell_border = NULL;
   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;
-}
+  iface->get_row_count = psppire_data_store_get_case_count_wrapper;
 
 
+  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;
 
 
-static void
-delete_cases_callback (GtkWidget *w, gint first, gint n_cases, gpointer data)
-{
-  PsppireDataStore *store  ;
-
-  g_return_if_fail (data);
-
-  store  = PSPPIRE_DATA_STORE (data);
-
-  g_assert (first >= 0);
-
-  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;
 }
 
 
 }
 
 
+/*
+   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 val_cnt,
+                         gpointer data)
 {
 {
-  PsppireDataStore *store  ;
+  PsppireDataStore *store  = PSPPIRE_DATA_STORE (data);
 
 
-  g_return_if_fail (data);
 
 
-  store  = PSPPIRE_DATA_STORE (data);
+  g_sheet_model_columns_deleted (G_SHEET_MODEL (store), dict_index, 1);
+#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);
-}
-
-
-static void
-changed_case_callback (GtkWidget *w, gint casenum, gpointer data)
-{
-  PsppireDataStore *store  ;
-  g_return_if_fail (data);
-
-  store  = PSPPIRE_DATA_STORE (data);
-
-  g_sheet_model_range_changed (G_SHEET_MODEL (store),
-                                casenum, -1,
-                                casenum, -1);
-}
-
-
-static void
-delete_variables_callback (GObject *obj, gint var_num, gint n_vars, gpointer data)
-{
-  PsppireDataStore *store ;
-
-  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),
 
   g_sheet_column_columns_changed (G_SHEET_COLUMN (store),
-                                  var_num, -1);
+                                  dict_index, -1);
+#endif
 }
 
 
 }
 
 
+
 static void
 variable_changed_callback (GObject *obj, gint var_num, gpointer data)
 {
 static void
 variable_changed_callback (GObject *obj, gint var_num, gpointer data)
 {
-  PsppireDataStore *store;
-
-  g_return_if_fail (data);
-
-  store  = PSPPIRE_DATA_STORE (data);
+  PsppireDataStore *store  = PSPPIRE_DATA_STORE (data);
 
 
+#if AXIS_TRANSITION
   g_sheet_column_columns_changed (G_SHEET_COLUMN (store),
                                  var_num, 1);
 
   g_sheet_column_columns_changed (G_SHEET_COLUMN (store),
                                  var_num, 1);
 
@@ -312,6 +324,7 @@ variable_changed_callback (GObject *obj, gint var_num, gpointer data)
   g_sheet_model_range_changed (G_SHEET_MODEL (store),
                               -1, var_num,
                               -1, var_num);
   g_sheet_model_range_changed (G_SHEET_MODEL (store),
                               -1, var_num,
                               -1, var_num);
+#endif
 }
 
 static void
 }
 
 static void
@@ -338,10 +351,13 @@ insert_variable_callback (GObject *obj, gint var_num, gpointer data)
       posn = 0;
     }
 
       posn = 0;
     }
 
-  psppire_case_file_insert_values (store->case_file, 1, posn);
+  psppire_data_store_insert_values (store, 1, posn);
+
+#if AXIS_TRANSITION
 
   g_sheet_column_columns_changed (G_SHEET_COLUMN (store),
                                  var_num, 1);
 
   g_sheet_column_columns_changed (G_SHEET_COLUMN (store),
                                  var_num, 1);
+#endif
 
   g_sheet_model_columns_inserted (G_SHEET_MODEL (store), var_num, 1);
 }
 
   g_sheet_model_columns_inserted (G_SHEET_MODEL (store), var_num, 1);
 }
@@ -351,13 +367,16 @@ static void
 dict_size_change_callback (GObject *obj,
                          gint posn, gint adjustment, gpointer data)
 {
 dict_size_change_callback (GObject *obj,
                          gint posn, gint adjustment, gpointer data)
 {
-  PsppireDataStore *store ;
+  PsppireDataStore *store  = PSPPIRE_DATA_STORE (data);
 
 
-  g_return_if_fail (data);
+  const struct variable *v = psppire_dict_get_variable (store->dict, posn);
 
 
-  store  = PSPPIRE_DATA_STORE (data);
+  const gint new_val_width = value_cnt_from_width (var_get_width (v));
 
 
-  psppire_case_file_insert_values (store->case_file, adjustment, posn);
+  if ( adjustment > 0 )
+    psppire_data_store_insert_values (store, adjustment,
+                                    new_val_width - adjustment +
+                                    var_get_case_index(v));
 }
 
 
 }
 
 
@@ -381,34 +400,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);
+  g_sheet_model_range_changed (G_SHEET_MODEL (ds),
+                              -1, -1, -1, -1);
 
 
+  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_connect (data_store->case_file, "case-changed",
-                  G_CALLBACK (changed_case_callback),
-                  data_store);
+  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
@@ -420,29 +439,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;
 
 
-  g_signal_connect (dict, "variable-inserted",
-                  G_CALLBACK (insert_variable_callback),
-                  data_store);
+  /* 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, "variables-deleted",
-                  G_CALLBACK (delete_variables_callback),
-                  data_store);
+  data_store->dict = dict;
+
+  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 */
   g_sheet_model_range_changed (G_SHEET_MODEL (data_store), -1, -1, -1, -1);
 
 
   /* The entire model has changed */
   g_sheet_model_range_changed (G_SHEET_MODEL (data_store), -1, -1, -1, -1);
 
+#if AXIS_TRANSITION
   g_sheet_column_columns_changed (G_SHEET_COLUMN (data_store), 0, -1);
   g_sheet_column_columns_changed (G_SHEET_COLUMN (data_store), 0, -1);
+#endif
+
+  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
@@ -454,19 +504,42 @@ 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;
   gint val_cnt, v;
   struct ccase cc;
   g_return_val_if_fail (ds, FALSE);
 
 {
   gboolean result;
   gint val_cnt, v;
   struct ccase cc;
   g_return_val_if_fail (ds, FALSE);
 
+  val_cnt = datasheet_get_column_cnt (ds->datasheet) ;
 
 
-  /* Opportunity for optimisation exists here when creating a blank case */
-  val_cnt = datasheet_get_column_cnt (ds->case_file->datasheet) ;
+  g_return_val_if_fail (val_cnt > 0, FALSE);
+
+  g_return_val_if_fail (posn <= psppire_data_store_get_case_count (ds), FALSE);
 
   case_create (&cc, val_cnt);
 
 
   case_create (&cc, val_cnt);
 
@@ -481,7 +554,7 @@ psppire_data_store_insert_new_case (PsppireDataStore *ds, gint posn)
       case_data_rw (&cc, pv)->f = SYSMIS;
     }
 
       case_data_rw (&cc, pv)->f = SYSMIS;
     }
 
-  result = psppire_case_file_insert_case (ds->case_file, &cc, posn);
+  result = psppire_data_store_insert_case (ds, &cc, posn);
 
   case_destroy (&cc);
 
 
   case_destroy (&cc);
 
@@ -490,7 +563,7 @@ psppire_data_store_insert_new_case (PsppireDataStore *ds, gint posn)
 
 
 gchar *
 
 
 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;
 {
   gint idx;
   char *text;
@@ -500,12 +573,12 @@ psppire_data_store_get_string (PsppireDataStore *store, gint row, gint column)
   GString *s;
 
   g_return_val_if_fail (store->dict, NULL);
   GString *s;
 
   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);
@@ -516,7 +589,7 @@ psppire_data_store_get_string (PsppireDataStore *store, gint row, gint column)
 
   g_assert (idx >= 0);
 
 
   g_assert (idx >= 0);
 
-  v = psppire_case_file_get_value (store->case_file, row, idx, NULL,
+  v = psppire_data_store_get_value (store, row, idx, NULL,
                                    var_get_width (pv));
 
   g_return_val_if_fail (v, NULL);
                                    var_get_width (pv));
 
   g_return_val_if_fail (v, NULL);
@@ -557,8 +630,7 @@ psppire_data_store_get_string (PsppireDataStore *store, gint row, gint column)
 
 static gboolean
 psppire_data_store_clear_datum (GSheetModel *model,
 
 static gboolean
 psppire_data_store_clear_datum (GSheetModel *model,
-                                         gint row, gint col)
-
+                                         glong row, glong col)
 {
   PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
 
 {
   PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
 
@@ -572,8 +644,10 @@ psppire_data_store_clear_datum (GSheetModel *model,
   else
     memcpy (v.s, "", MAX_SHORT_STRING);
 
   else
     memcpy (v.s, "", MAX_SHORT_STRING);
 
-  psppire_case_file_set_value (store->case_file, row, index, &v,
-                             var_get_width (pv));
+  psppire_data_store_set_value (store, row, index, &v,
+                               var_get_width (pv));
+
+  g_sheet_model_range_changed (model, row, col, row, col);
 
   return TRUE;
 }
 
   return TRUE;
 }
@@ -585,52 +659,30 @@ 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)
 {
 {
+  glong n_cases;
   const struct variable *pv = psppire_dict_get_variable (store->dict, col);
   g_return_val_if_fail (pv, FALSE);
 
   const struct variable *pv = psppire_dict_get_variable (store->dict, col);
   g_return_val_if_fail (pv, 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);
 
 
+  psppire_data_store_data_in (store, row,
+                             var_get_case_index (pv), ss_cstr (text),
+                             var_get_write_format (pv));
 
 
-      for (c = 0 ; c < psppire_dict_get_var_cnt (store->dict); ++c )
-       psppire_data_store_clear_datum (model, r, c);
-    }
-#endif
-
-  psppire_case_file_data_in (store->case_file, row,
-                             var_get_case_index (pv), ss_cstr (text),
-                             var_get_write_format (pv));
+  g_sheet_model_range_changed (G_SHEET_MODEL (store), row, col, row, col);
 
   return TRUE;
 }
 
 
 
   return TRUE;
 }
 
 
-void
-psppire_data_store_set_font (PsppireDataStore *store,
-                           const PangoFontDescription *fd)
-{
-  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);
-
-
-  g_sheet_model_range_changed (G_SHEET_MODEL (store),
-                                -1, -1, -1, -1);
-}
-
 
 void
 psppire_data_store_show_labels (PsppireDataStore *store, gboolean show_labels)
 
 void
 psppire_data_store_show_labels (PsppireDataStore *store, gboolean show_labels)
@@ -646,11 +698,14 @@ psppire_data_store_show_labels (PsppireDataStore *store, gboolean show_labels)
 
 
 void
 
 
 void
-psppire_data_store_clear (PsppireDataStore *data_store)
+psppire_data_store_clear (PsppireDataStore *ds)
 {
 {
-  psppire_case_file_clear (data_store->case_file);
+  datasheet_destroy (ds->datasheet);
+  ds->datasheet = NULL;
+
+  psppire_dict_clear (ds->dict);
 
 
-  psppire_dict_clear (data_store->dict);
+  g_signal_emit (ds, signals [CASES_DELETED], 0, 0, -1);
 }
 
 
 }
 
 
@@ -659,9 +714,20 @@ psppire_data_store_clear (PsppireDataStore *data_store)
 struct casereader *
 psppire_data_store_get_reader (PsppireDataStore *ds)
 {
 struct casereader *
 psppire_data_store_get_reader (PsppireDataStore *ds)
 {
+  int i;
   struct casereader *reader ;
 
   struct casereader *reader ;
 
-  reader = psppire_case_file_make_reader (ds->case_file);
+  if ( ds->dict )
+    for (i = 0 ; i < n_dict_signals; ++i )
+      {
+       g_signal_handler_block (ds->dict,
+                               ds->dict_handler_id[i]);
+      }
+
+  reader = datasheet_make_reader (ds->datasheet);
+
+  /* We must not reference this again */
+  ds->datasheet = NULL;
 
   return reader;
 }
 
   return reader;
 }
@@ -670,188 +736,276 @@ psppire_data_store_get_reader (PsppireDataStore *ds)
 
 /* Column related funcs */
 
 
 /* Column related funcs */
 
-static gint
-geometry_get_column_count (const GSheetColumn *geom)
-{
-  PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
 
 
-  return MAX (MIN_COLUMNS, psppire_dict_get_var_cnt (ds->dict));
-}
+static const gchar null_var_name[]=N_("var");
 
 
+\f
 
 
+/* Row related funcs */
 
 
-static gint
-geometry_get_width (const GSheetColumn *geom, gint unit)
+static gchar *
+get_row_button_label (const GSheetModel *model, gint unit)
 {
 {
-  const struct variable *pv ;
-  PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
-
-  if ( unit >= psppire_dict_get_var_cnt (ds->dict) )
-    return ds->width_of_m * 8 ;
+  gchar *s = g_strdup_printf (_("%d"), unit + FIRST_CASE_NUMBER);
 
 
-  pv = psppire_dict_get_variable (ds->dict, unit);
+  gchar *text =  pspp_locale_to_utf8 (s, -1, 0);
 
 
-  if ( pv == NULL )
-    return ds->width_of_m * 8 ;
+  g_free (s);
 
 
-  return ds->width_of_m * var_get_display_width (pv);
+  return text;
 }
 
 }
 
-static void
-geometry_set_width (GSheetColumn *geom, gint unit, gint width)
-{
-  PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
 
 
-  struct variable *pv = psppire_dict_get_variable (ds->dict, unit);
+static gboolean
+get_row_sensitivity (const GSheetModel *model, gint unit)
+{
+  PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
 
 
-  var_set_display_width (pv, width / ds->width_of_m );
+  return (unit < psppire_data_store_get_case_count (ds));
 }
 
 
 }
 
 
+\f
 
 
-static GtkJustification
-geometry_get_justification (const GSheetColumn *geom, gint unit)
-{
-  PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
-  const struct variable *pv ;
+/* Column related stuff */
 
 
+static gchar *
+get_column_subtitle (const GSheetModel *model, gint col)
+{
+  gchar *text;
+  const struct variable *v ;
+  PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
 
 
-  if ( unit >= psppire_dict_get_var_cnt (ds->dict) )
-    return GTK_JUSTIFY_LEFT;
+  if ( col >= psppire_dict_get_var_cnt (ds->dict) )
+    return NULL;
 
 
-  pv = psppire_dict_get_variable (ds->dict, unit);
+  v = 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
-          : GTK_JUSTIFY_CENTER);
-}
+  if ( ! var_has_label (v))
+    return NULL;
 
 
+  text =  pspp_locale_to_utf8 (var_get_label (v), -1, 0);
 
 
-static const gchar null_var_name[]=N_("var");
+  return text;
+}
 
 static gchar *
 
 static gchar *
-geometry_get_column_button_label (const GSheetColumn *geom, gint unit)
+get_column_button_label (const GSheetModel *model, gint col)
 {
   gchar *text;
   struct variable *pv ;
 {
   gchar *text;
   struct variable *pv ;
-  PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
+  PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
 
 
-  if ( unit >= psppire_dict_get_var_cnt (ds->dict) )
+  if ( col >= psppire_dict_get_var_cnt (ds->dict) )
     return g_locale_to_utf8 (null_var_name, -1, 0, 0, 0);
 
     return g_locale_to_utf8 (null_var_name, -1, 0, 0, 0);
 
-  pv = psppire_dict_get_variable (ds->dict, unit);
+  pv = psppire_dict_get_variable (ds->dict, col);
 
   text =  pspp_locale_to_utf8 (var_get_name (pv), -1, 0);
 
   return text;
 }
 
 
   text =  pspp_locale_to_utf8 (var_get_name (pv), -1, 0);
 
   return text;
 }
 
-
-static gchar *
-geometry_get_column_subtitle (const GSheetColumn *geom, gint unit)
+static gboolean
+get_column_sensitivity (const GSheetModel *model, gint col)
 {
 {
-  gchar *text;
-  const struct variable *v ;
-  PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
+  PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
 
 
-  if ( unit >= psppire_dict_get_var_cnt (ds->dict) )
-    return NULL;
+  return (col < psppire_dict_get_var_cnt (ds->dict));
+}
 
 
-  v = psppire_dict_get_variable (ds->dict, unit);
 
 
-  if ( ! var_has_label (v))
-    return NULL;
 
 
-  text =  pspp_locale_to_utf8 (var_get_label (v), -1, 0);
+static GtkJustification
+get_column_justification (const GSheetModel *model, gint col)
+{
+  PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
+  const struct variable *pv ;
 
 
-  return text;
+  if ( col >= psppire_dict_get_var_cnt (ds->dict) )
+    return GTK_JUSTIFY_LEFT;
+
+  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
+          : GTK_JUSTIFY_CENTER);
 }
 
 
 }
 
 
-static gboolean
-geometry_get_sensitivity (const GSheetColumn *geom, gint unit)
+
+\f
+
+
+/* Fills C with the CASENUMth case.
+   Returns true on success, false otherwise.
+ */
+gboolean
+psppire_data_store_get_case (const PsppireDataStore *ds,
+                            casenumber casenum,
+                            struct ccase *c)
 {
 {
-  PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
+  g_return_val_if_fail (ds, FALSE);
+  g_return_val_if_fail (ds->datasheet, FALSE);
 
 
-  return (unit < psppire_dict_get_var_cnt (ds->dict));
+  return datasheet_get_row (ds->datasheet, casenum, c);
 }
 
 
 }
 
 
-static void
-psppire_data_store_sheet_column_init (GSheetColumnIface *iface)
+gboolean
+psppire_data_store_delete_cases (PsppireDataStore *ds, casenumber first,
+                                casenumber n_cases)
 {
 {
-  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;
-  iface->get_subtitle = geometry_get_column_subtitle;
+  g_return_val_if_fail (ds, FALSE);
+  g_return_val_if_fail (ds->datasheet, FALSE);
+
+  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);
+  g_sheet_model_rows_deleted (G_SHEET_MODEL (ds), first, n_cases);
+
+  return TRUE;
 }
 
 
 }
 
 
-/* Row related funcs */
 
 
-static gint
-geometry_get_row_count (const GSheetRow *geom, gpointer data)
+/* Insert case CC into the case file before POSN */
+static gboolean
+psppire_data_store_insert_case (PsppireDataStore *ds,
+                               struct ccase *cc,
+                               casenumber posn)
 {
 {
-  PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
+  struct ccase tmp;
+  bool result ;
+
+  g_return_val_if_fail (ds, FALSE);
+  g_return_val_if_fail (ds->datasheet, FALSE);
+
+  case_clone (&tmp, cc);
+  result = datasheet_insert_rows (ds->datasheet, posn, &tmp, 1);
+
+  if ( result )
+    {
+      g_signal_emit (ds, signals [CASE_INSERTED], 0, posn);
+      g_sheet_model_rows_inserted (G_SHEET_MODEL (ds), posn, 1);
+    }
+  else
+    g_warning ("Cannot insert case at position %ld\n", posn);
 
 
-  return TRAILING_ROWS + psppire_case_file_get_case_count (ds->case_file);
+  return result;
 }
 
 
 }
 
 
-static gint
-geometry_get_height (const GSheetRow *geom, gint unit, gpointer data)
+/* Copies the IDXth value from case CASENUM into VALUE.
+   If VALUE is null, then memory is allocated is allocated with
+   malloc.  Returns the value if successful, NULL on failure. */
+static union value *
+psppire_data_store_get_value (const PsppireDataStore *ds,
+                             casenumber casenum, size_t idx,
+                             union value *value, int width)
 {
 {
-  return 25;
+  bool allocated;
+
+  g_return_val_if_fail (ds, false);
+  g_return_val_if_fail (ds->datasheet, false);
+
+  g_return_val_if_fail (idx < datasheet_get_column_cnt (ds->datasheet), false);
+
+  if (value == NULL)
+    {
+      value = xnmalloc (value_cnt_from_width (width), sizeof *value);
+      allocated = true;
+    }
+  else
+    allocated = false;
+  if (!datasheet_get_value (ds->datasheet, casenum, idx, value, width))
+    {
+      if (allocated)
+        free (value);
+      value = NULL;
+    }
+  return value;
 }
 
 
 }
 
 
+
+/* Set the IDXth value of case C to V.
+   Returns true if successful, false on I/O error. */
 static gboolean
 static gboolean
-geometry_get_row_sensitivity (const GSheetRow *geom, gint unit, gpointer data)
+psppire_data_store_set_value (PsppireDataStore *ds, casenumber casenum,
+                             gint idx, union value *v, gint width)
 {
 {
-  PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
+  bool ok;
 
 
+  g_return_val_if_fail (ds, FALSE);
+  g_return_val_if_fail (ds->datasheet, FALSE);
+
+  g_return_val_if_fail (idx < datasheet_get_column_cnt (ds->datasheet), FALSE);
 
 
-  return (unit < psppire_case_file_get_case_count (ds->case_file));
+  ok = datasheet_put_value (ds->datasheet, casenum, idx, v, width);
+  if (ok)
+    g_signal_emit (ds, signals [CASE_CHANGED], 0, casenum);
+
+  return ok;
 }
 
 
 }
 
 
-static gchar *
-geometry_get_row_button_label (const GSheetRow *geom, gint unit, gpointer data)
+
+
+/* Set the IDXth value of case C using D_IN */
+static gboolean
+psppire_data_store_data_in (PsppireDataStore *ds, casenumber casenum, gint idx,
+                           struct substring input, const struct fmt_spec *fmt)
 {
 {
-  gchar *text;
-  gchar *s;
-  PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
+  union value *value = NULL;
+  int width;
+  bool ok;
 
 
-  if ( unit >
-       TRAILING_ROWS + psppire_case_file_get_case_count (ds->case_file))
-    return 0;
+  g_return_val_if_fail (ds, FALSE);
+  g_return_val_if_fail (ds->datasheet, FALSE);
 
 
-  s = g_strdup_printf (_("%d"), unit);
+  g_return_val_if_fail (idx < datasheet_get_column_cnt (ds->datasheet), FALSE);
 
 
-  text =  pspp_locale_to_utf8 (s, -1, 0);
+  width = fmt_var_width (fmt);
+  value = xmalloca (value_cnt_from_width (width) * sizeof *value);
+  ok = (datasheet_get_value (ds->datasheet, casenum, idx, value, width)
+        && data_in (input, LEGACY_NATIVE, fmt->type, 0, 0, 0, value, width)
+        && datasheet_put_value (ds->datasheet, casenum, idx, value, width));
 
 
-  g_free (s);
+  freea (value);
 
 
-  return text;
-}
+  if (ok)
+    g_signal_emit (ds, signals [CASE_CHANGED], 0, casenum);
 
 
+  return ok;
+}
 
 
-static void
-psppire_data_store_sheet_row_init (GSheetRowIface *iface)
+/* Resize the cases in the casefile, by inserting N_VALUES into every
+   one of them at the position immediately preceeding WHERE.
+*/
+static gboolean
+psppire_data_store_insert_values (PsppireDataStore *ds,
+                                 gint n_values, gint where)
 {
 {
-  iface->get_row_count = geometry_get_row_count;
+  g_return_val_if_fail (ds, FALSE);
 
 
-  iface->get_height = geometry_get_height;
-  iface->set_height = 0;
-  iface->get_visibility = always_true;
-  iface->get_sensitivity = geometry_get_row_sensitivity;
+  if ( n_values == 0 )
+    return FALSE;
 
 
-  iface->get_button_label = geometry_get_row_button_label;
-}
+  g_assert (n_values > 0);
 
 
+  if ( ! ds->datasheet )
+    ds->datasheet = datasheet_create (NULL);
 
 
+  {
+    union value *values = xcalloc (n_values, sizeof *values);
+    datasheet_insert_columns (ds->datasheet, values, n_values, where);
+    free (values);
+  }
 
 
+  return TRUE;
+}