Change license from GPLv2+ to GPLv3+.
[pspp-builds.git] / src / ui / gui / psppire-data-store.c
index c0bebeccb03f3b7126b90a94e43d8e4d9f7d3c0c..976c533da5a45a54961ff931c1bc1244577032bd 100644 (file)
@@ -1,11 +1,9 @@
-/* psppire-data-store.c
-   PSPPIRE --- A Graphical User Interface for PSPP
+/* 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
-   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,
    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 <stdlib.h>
 #include <gettext.h>
-#define _(msgid) gettext(msgid)
+#define _(msgid) gettext (msgid)
 #define N_(msgid) msgid
 
-#include <data/casefile.h>
-#include <data/case.h>
+#include <data/datasheet.h>
 #include <data/data-out.h>
 #include <data/variable.h>
 
 #include <data/value-labels.h>
 #include <data/data-in.h>
 
-#include <data/file-handle-def.h>
-#include <data/sys-file-writer.h>
-
-
 
 static void psppire_data_store_init            (PsppireDataStore      *data_store);
 static void psppire_data_store_class_init      (PsppireDataStoreClass *class);
@@ -58,12 +49,7 @@ static void psppire_data_store_sheet_row_init (GSheetRowIface *iface);
 
 static void psppire_data_store_finalize        (GObject           *object);
 
-static gchar *psppire_data_store_get_string(const GSheetModel *sheet_model, gint row, gint column);
-
-static gboolean psppire_data_store_set_string(GSheetModel *model, 
-                                         const gchar *text, 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 column);
 
 
@@ -77,7 +63,7 @@ static GObjectClass *parent_class = NULL;
 enum  {FONT_CHANGED,
        n_SIGNALS};
 
-static guint signal[n_SIGNALS];
+static guint signal[n_SIGNALS];
 
 
 inline GType
@@ -152,14 +138,14 @@ psppire_data_store_class_init (PsppireDataStoreClass *class)
 
   object_class->finalize = psppire_data_store_finalize;
 
-  signal[FONT_CHANGED] =
+  signal[FONT_CHANGED] =
     g_signal_new ("font_changed",
-                 G_TYPE_FROM_CLASS(class),
+                 G_TYPE_FROM_CLASS (class),
                  G_SIGNAL_RUN_FIRST,
                  0,
                  NULL, NULL,
                  g_cclosure_marshal_VOID__VOID,
-                 G_TYPE_NONE, 
+                 G_TYPE_NONE,
                  0);
 }
 
@@ -168,17 +154,17 @@ psppire_data_store_class_init (PsppireDataStoreClass *class)
 static gint
 psppire_data_store_get_var_count (const GSheetModel *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)
 {
-  const PsppireDataStore *store = PSPPIRE_DATA_STORE(model);
+  const PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
 
-  return psppire_case_file_get_case_count(store->case_file);
+  return psppire_case_file_get_case_count (store->case_file);
 }
 
 
@@ -191,21 +177,40 @@ psppire_data_store_init (PsppireDataStore *data_store)
 }
 
 const PangoFontDescription *
-psppire_data_store_get_font_desc(const GSheetModel *model,
+psppire_data_store_get_font_desc (const GSheetModel *model,
                              gint row, gint column)
 {
-  PsppireDataStore *store = PSPPIRE_DATA_STORE(model);
-  
+  PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
+
   return store->font_desc;
 }
 
+static inline gchar *
+psppire_data_store_get_string_wrapper (const GSheetModel *model, gint row,
+                                      gint column)
+{
+  return psppire_data_store_get_string (PSPPIRE_DATA_STORE (model), row, column);
+}
+
+
+static inline gboolean
+psppire_data_store_set_string_wrapper (GSheetModel *model,
+                                      const gchar *text,
+                                      gint row, gint column)
+{
+  return psppire_data_store_set_string (PSPPIRE_DATA_STORE (model), text,
+                                       row, column);
+}
+
+
+
 
 static void
 psppire_data_store_sheet_model_init (GSheetModelIface *iface)
 {
   iface->free_strings = TRUE;
-  iface->get_string = psppire_data_store_get_string;
-  iface->set_string = psppire_data_store_set_string;
+  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;
@@ -218,88 +223,109 @@ psppire_data_store_sheet_model_init (GSheetModelIface *iface)
 }
 
 static
-gboolean always_true()
+gboolean always_true ()
 {
   return TRUE;
 }
 
 
 static void
-delete_cases_callback(GtkWidget *w, gint first, gint n_cases, gpointer data)
+delete_cases_callback (GtkWidget *w, gint first, gint n_cases, gpointer data)
 {
   PsppireDataStore *store  ;
 
   g_return_if_fail (data);
 
-  store  = PSPPIRE_DATA_STORE(data);
+  store  = PSPPIRE_DATA_STORE (data);
 
-  g_assert(first >= 0);
+  g_assert (first >= 0);
 
-  g_sheet_model_rows_deleted (G_SHEET_MODEL(store), first, n_cases);
+  g_sheet_model_rows_deleted (G_SHEET_MODEL (store), first, n_cases);
 }
 
 
 static void
-insert_case_callback(GtkWidget *w, gint casenum, gpointer data)
+insert_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),
+  store  = PSPPIRE_DATA_STORE (data);
+
+  g_sheet_model_range_changed (G_SHEET_MODEL (store),
                               casenum, -1,
-                              psppire_case_file_get_case_count(store->case_file),
+                              psppire_case_file_get_case_count (store->case_file),
                               -1);
 
-  g_sheet_model_rows_inserted (G_SHEET_MODEL(store), casenum, 1);
+  g_sheet_model_rows_inserted (G_SHEET_MODEL (store), casenum, 1);
 }
 
 
 static void
-changed_case_callback(GtkWidget *w, gint casenum, gpointer data)
+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),
+  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)
+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);
+  store  = PSPPIRE_DATA_STORE (data);
 
-  g_sheet_model_columns_deleted (G_SHEET_MODEL(store), var_num, n_vars);
+  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);
 }
 
+
+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),
+                                 var_num, 1);
+
+
+  g_sheet_model_range_changed (G_SHEET_MODEL (store),
+                              -1, var_num,
+                              -1, var_num);
+}
+
 static void
-insert_variable_callback(GObject *obj, gint var_num, gpointer data)
+insert_variable_callback (GObject *obj, gint var_num, gpointer data)
 {
   PsppireDataStore *store;
   gint posn;
 
   g_return_if_fail (data);
 
-  store  = PSPPIRE_DATA_STORE(data);
-  
-  if ( var_num > 0 ) 
+  store  = PSPPIRE_DATA_STORE (data);
+
+  if ( var_num > 0 )
     {
-      struct variable *variable;
-      variable = psppire_dict_get_variable(store->dict, var_num);
+      struct variable *variable =
+       psppire_dict_get_variable (store->dict, var_num);
+
+      g_assert (variable != NULL);
 
       posn = var_get_case_index (variable);
     }
@@ -308,24 +334,24 @@ insert_variable_callback(GObject *obj, gint var_num, gpointer data)
       posn = 0;
     }
 
-  psppire_case_file_insert_values(store->case_file, 1, posn);
+  psppire_case_file_insert_values (store->case_file, 1, posn);
 
-  g_sheet_column_columns_changed(G_SHEET_COLUMN(store),
+  g_sheet_column_columns_changed (G_SHEET_COLUMN (store),
                                  var_num, 1);
 
-  g_sheet_model_columns_inserted (G_SHEET_MODEL(store), var_num, 1);
+  g_sheet_model_columns_inserted (G_SHEET_MODEL (store), var_num, 1);
 }
 
 
 static void
-dict_size_change_callback(GObject *obj, 
+dict_size_change_callback (GObject *obj,
                          gint posn, gint adjustment, gpointer data)
 {
   PsppireDataStore *store ;
 
   g_return_if_fail (data);
 
-  store  = PSPPIRE_DATA_STORE(data);
+  store  = PSPPIRE_DATA_STORE (data);
 
   psppire_case_file_insert_values (store->case_file, adjustment, posn);
 }
@@ -346,13 +372,38 @@ psppire_data_store_new (PsppireDict *dict)
 
   retval = g_object_new (GTK_TYPE_DATA_STORE, NULL);
 
-  psppire_data_store_set_dictionary(retval, dict);
-
+  psppire_data_store_set_dictionary (retval, dict);
 
   return retval;
 }
 
 
+void
+psppire_data_store_set_case_file (PsppireDataStore *data_store,
+                                 PsppireCaseFile *cf)
+{
+  if ( data_store->case_file)
+    {
+      g_object_unref (data_store->case_file);
+    }
+
+  data_store->case_file = cf;
+
+  g_signal_connect (data_store->case_file, "cases-deleted",
+                  G_CALLBACK (delete_cases_callback),
+                  data_store);
+
+  g_signal_connect (data_store->case_file, "case-inserted",
+                  G_CALLBACK (insert_case_callback),
+                  data_store);
+
+
+  g_signal_connect (data_store->case_file, "case-changed",
+                  G_CALLBACK (changed_case_callback),
+                  data_store);
+}
+
+
 
 /**
  * psppire_data_store_replace_set_dictionary:
@@ -363,49 +414,31 @@ psppire_data_store_new (PsppireDict *dict)
  * destroyed.
  **/
 void
-psppire_data_store_set_dictionary(PsppireDataStore *data_store, PsppireDict *dict)
+psppire_data_store_set_dictionary (PsppireDataStore *data_store, PsppireDict *dict)
 {
-  gint var_cnt = psppire_dict_get_next_value_idx(dict);
-
   data_store->dict = dict;
 
-  if ( data_store->case_file)
-    {
-      g_object_unref(data_store->case_file);
-      data_store->case_file = 0;
-    }
-
-  data_store->case_file = psppire_case_file_new(var_cnt);
-
-  g_signal_connect(data_store->case_file, "cases-deleted", 
-                  G_CALLBACK(delete_cases_callback), 
+  g_signal_connect (dict, "variable-inserted",
+                  G_CALLBACK (insert_variable_callback),
                   data_store);
 
-  g_signal_connect(data_store->case_file, "case-inserted", 
-                  G_CALLBACK(insert_case_callback), 
+  g_signal_connect (dict, "variables-deleted",
+                  G_CALLBACK (delete_variables_callback),
                   data_store);
 
-
-  g_signal_connect(data_store->case_file, "case-changed", 
-                  G_CALLBACK(changed_case_callback), 
+  g_signal_connect (dict, "variable-changed",
+                  G_CALLBACK (variable_changed_callback),
                   data_store);
 
-  g_signal_connect(dict, "variable-inserted", 
-                  G_CALLBACK(insert_variable_callback), 
-                  data_store);
 
-  g_signal_connect(dict, "variables-deleted", 
-                  G_CALLBACK(delete_variables_callback), 
-                  data_store);
-
-  g_signal_connect (dict, "dict-size-changed", 
-                   G_CALLBACK(dict_size_change_callback),
+  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);
-  
-  g_sheet_column_columns_changed(G_SHEET_COLUMN(data_store), 0, -1);
+  g_sheet_model_range_changed (G_SHEET_MODEL (data_store), -1, -1, -1, -1);
+
+  g_sheet_column_columns_changed (G_SHEET_COLUMN (data_store), 0, -1);
 }
 
 static void
@@ -420,22 +453,22 @@ psppire_data_store_finalize (GObject *object)
 
 /* 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, gint posn)
 {
   gboolean result;
-  gint val_cnt, v; 
+  gint val_cnt, v;
   struct ccase cc;
   g_return_val_if_fail (ds, FALSE);
 
 
   /* Opportunity for optimisation exists here when creating a blank case */
-  val_cnt = casefile_get_value_cnt(ds->case_file->flexifile) ;
-  
+  val_cnt = datasheet_get_column_cnt (ds->case_file->datasheet) ;
+
   case_create (&cc, val_cnt);
 
   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) 
+  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))
@@ -452,16 +485,15 @@ psppire_data_store_insert_new_case(PsppireDataStore *ds, gint posn)
 }
 
 
-static gchar *
-psppire_data_store_get_string (const GSheetModel *model, gint row, gint column)
+gchar *
+psppire_data_store_get_string (PsppireDataStore *store, gint row, gint column)
 {
   gint idx;
   char *text;
   const struct fmt_spec *fp ;
   const struct variable *pv ;
-  const union value *v ;
+  union value *v ;
   GString *s;
-  PsppireDataStore *store = PSPPIRE_DATA_STORE(model);
 
   g_return_val_if_fail (store->dict, NULL);
   g_return_val_if_fail (store->case_file, NULL);
@@ -474,32 +506,36 @@ psppire_data_store_get_string (const GSheetModel *model, gint row, gint column)
 
   pv = psppire_dict_get_variable (store->dict, column);
 
+  g_assert (pv);
+
   idx = var_get_case_index (pv);
 
-  v = psppire_case_file_get_value (store->case_file, row, idx);
+  g_assert (idx >= 0);
 
-  g_return_val_if_fail(v, NULL);
+  v = psppire_case_file_get_value (store->case_file, row, idx, NULL,
+                                   var_get_width (pv));
 
-  if ( store->show_labels) 
-    {
-      const struct val_labs * vl = var_get_value_labels (pv);
+  g_return_val_if_fail (v, NULL);
 
-      const gchar *label;
-      if ( (label = val_labs_find(vl, *v)) )
-       {
-         return pspp_locale_to_utf8(label, -1, 0);
-       }
+  if ( store->show_labels)
+    {
+      const gchar *label = var_lookup_value_label (pv, v);
+      if (label)
+        {
+          free (v);
+         return pspp_locale_to_utf8 (label, -1, 0);
+        }
     }
 
   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.  */
@@ -510,16 +546,17 @@ psppire_data_store_get_string (const GSheetModel *model, gint row, gint column)
 
   g_strchomp (text);
 
+  free (v);
   return text;
 }
 
 
-static gboolean 
-psppire_data_store_clear_datum (GSheetModel *model, 
+static gboolean
+psppire_data_store_clear_datum (GSheetModel *model,
                                          gint row, gint col)
 
 {
-  PsppireDataStore *store = PSPPIRE_DATA_STORE(model);
+  PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
 
   union value v;
   const struct variable *pv = psppire_dict_get_variable (store->dict, col);
@@ -529,53 +566,51 @@ psppire_data_store_clear_datum (GSheetModel *model,
   if ( var_is_numeric (pv))
     v.f = SYSMIS;
   else
-    memcpy(v.s, "", MAX_SHORT_STRING);
+    memcpy (v.s, "", MAX_SHORT_STRING);
 
-  psppire_case_file_set_value(store->case_file, row, index, &v, 
+  psppire_case_file_set_value (store->case_file, row, index, &v,
                              var_get_width (pv));
 
   return TRUE;
 }
 
 
-/* Attempts to update that part of the variable store which corresponds 
+/* Attempts to update that part of the variable store which corresponds
    to ROW, COL with  the value TEXT.
    Returns true if anything was updated, false otherwise.
 */
-static gboolean 
-psppire_data_store_set_string(GSheetModel *model, 
-                         const gchar *text, gint row, gint col)
+gboolean
+psppire_data_store_set_string (PsppireDataStore *store,
+                              const gchar *text, gint row, gint col)
 {
-  PsppireDataStore *store = PSPPIRE_DATA_STORE(model);
-
-  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) 
+  for (r = psppire_case_file_get_case_count (store->case_file); r <= row ; ++r)
     {
 
       gint c;
 
-      psppire_case_array_insert_case(store->cases, r, 0, 0);
+      psppire_case_array_insert_case (store->cases, r, 0, 0);
 
 
-      for (c = 0 ; c < psppire_dict_get_var_cnt(store->dict); ++c ) 
-       psppire_data_store_clear_datum(model, r, c);
+      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));
-  
+
   return TRUE;
 }
 
 
 void
-psppire_data_store_set_font(PsppireDataStore *store, 
+psppire_data_store_set_font (PsppireDataStore *store,
                            const PangoFontDescription *fd)
 {
   g_return_if_fail (store);
@@ -583,115 +618,85 @@ psppire_data_store_set_font(PsppireDataStore *store,
 
   store->font_desc = fd;
 #if 0
-  store->width_of_m = calc_m_width(fd);
+  store->width_of_m = calc_m_width (fd);
 #endif
-  g_signal_emit(store, signal[FONT_CHANGED], 0);  
+  g_signal_emit (store, signals [FONT_CHANGED], 0);
 
 
-  g_sheet_model_range_changed (G_SHEET_MODEL(store),
+  g_sheet_model_range_changed (G_SHEET_MODEL (store),
                                 -1, -1, -1, -1);
 }
 
 
 void
-psppire_data_store_show_labels(PsppireDataStore *store, gboolean show_labels)
+psppire_data_store_show_labels (PsppireDataStore *store, gboolean show_labels)
 {
   g_return_if_fail (store);
   g_return_if_fail (PSPPIRE_IS_DATA_STORE (store));
 
   store->show_labels = show_labels;
 
-  g_sheet_model_range_changed (G_SHEET_MODEL(store),
+  g_sheet_model_range_changed (G_SHEET_MODEL (store),
                                 -1, -1, -1, -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)
+psppire_data_store_clear (PsppireDataStore *data_store)
 {
-  gint i, var_cnt;
-  const struct sfm_write_options wo = {
-    true, /* writeable */
-    false, /* dont compress */
-    3 /* version */
-  }; 
-
-  struct sfm_writer *writer ;
-
-  g_assert(handle);
-
-  writer = sfm_open_writer(handle, store->dict->dict, wo);
-
-  if ( ! writer) 
-    return;
-
-
-  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;
-      
-      case_create (&c, var_cnt);
-      psppire_case_file_get_case (store->case_file, i, &c);
-      sfm_write_case (writer, &c);
+  psppire_case_file_clear (data_store->case_file);
 
-      case_destroy (&c);
-    }
-
-  sfm_close_writer(writer);
+  psppire_dict_clear (data_store->dict);
 }
 
 
 
-void 
-psppire_data_store_clear(PsppireDataStore *data_store)
+/* Return a casereader made from this datastore */
+struct casereader *
+psppire_data_store_get_reader (PsppireDataStore *ds)
 {
-  psppire_case_file_clear(data_store->case_file);
+  struct casereader *reader ;
 
-  psppire_dict_clear(data_store->dict);
-}
+  reader = psppire_case_file_make_reader (ds->case_file);
 
+  return reader;
+}
 
 
 
 /* Column related funcs */
 
 static gint
-geometry_get_column_count(const GSheetColumn *geom)
+geometry_get_column_count (const GSheetColumn *geom)
 {
-  PsppireDataStore *ds = PSPPIRE_DATA_STORE(geom);
+  PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
 
-  return MAX(MIN_COLUMNS, psppire_dict_get_var_cnt(ds->dict));
+  return MAX (MIN_COLUMNS, psppire_dict_get_var_cnt (ds->dict));
 }
 
 
 
 static gint
-geometry_get_width(const GSheetColumn *geom, gint unit)
+geometry_get_width (const GSheetColumn *geom, gint unit)
 {
   const struct variable *pv ;
-  PsppireDataStore *ds = PSPPIRE_DATA_STORE(geom);
+  PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
 
-  if ( unit >= psppire_dict_get_var_cnt(ds->dict) )
+  if ( unit >= psppire_dict_get_var_cnt (ds->dict) )
     return ds->width_of_m * 8 ;
 
   pv = psppire_dict_get_variable (ds->dict, unit);
 
-  if ( pv == NULL ) 
+  if ( pv == NULL )
     return ds->width_of_m * 8 ;
 
   return ds->width_of_m * var_get_display_width (pv);
 }
 
 static void
-geometry_set_width(GSheetColumn *geom, gint unit, gint width)
+geometry_set_width (GSheetColumn *geom, gint unit, gint width)
 {
-  PsppireDataStore *ds = PSPPIRE_DATA_STORE(geom);
+  PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
 
   struct variable *pv = psppire_dict_get_variable (ds->dict, unit);
 
@@ -701,16 +706,16 @@ geometry_set_width(GSheetColumn *geom, gint unit, gint width)
 
 
 static GtkJustification
-geometry_get_justification(const GSheetColumn *geom, gint unit)
+geometry_get_justification (const GSheetColumn *geom, gint unit)
 {
-  PsppireDataStore *ds = PSPPIRE_DATA_STORE(geom);
+  PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
   const struct variable *pv ;
 
 
-  if ( unit >= psppire_dict_get_var_cnt(ds->dict) )
+  if ( unit >= psppire_dict_get_var_cnt (ds->dict) )
     return GTK_JUSTIFY_LEFT;
 
-  pv = psppire_dict_get_variable(ds->dict, unit);
+  pv = psppire_dict_get_variable (ds->dict, unit);
 
   return (var_get_alignment (pv) == ALIGN_LEFT ? GTK_JUSTIFY_LEFT
           : var_get_alignment (pv) == ALIGN_RIGHT ? GTK_JUSTIFY_RIGHT
@@ -719,16 +724,16 @@ 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)
+geometry_get_column_button_label (const GSheetColumn *geom, gint unit)
 {
   gchar *text;
   struct variable *pv ;
-  PsppireDataStore *ds = PSPPIRE_DATA_STORE(geom);
+  PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
 
-  if ( unit >= psppire_dict_get_var_cnt(ds->dict) )
-    return g_locale_to_utf8(null_var_name, -1, 0, 0, 0);
+  if ( unit >= psppire_dict_get_var_cnt (ds->dict) )
+    return g_locale_to_utf8 (null_var_name, -1, 0, 0, 0);
 
   pv = psppire_dict_get_variable (ds->dict, unit);
 
@@ -738,12 +743,33 @@ geometry_get_column_button_label(const GSheetColumn *geom, gint unit)
 }
 
 
+static gchar *
+geometry_get_column_subtitle (const GSheetColumn *geom, gint unit)
+{
+  gchar *text;
+  const struct variable *v ;
+  PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
+
+  if ( unit >= psppire_dict_get_var_cnt (ds->dict) )
+    return NULL;
+
+  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);
+
+  return text;
+}
+
+
 static gboolean
-geometry_get_sensitivity(const GSheetColumn *geom, gint unit)
+geometry_get_sensitivity (const GSheetColumn *geom, gint unit)
 {
-  PsppireDataStore *ds = PSPPIRE_DATA_STORE(geom);
+  PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
 
-  return (unit < psppire_dict_get_var_cnt(ds->dict));
+  return (unit < psppire_dict_get_var_cnt (ds->dict));
 }
 
 
@@ -757,54 +783,55 @@ psppire_data_store_sheet_column_init (GSheetColumnIface *iface)
   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;
 }
 
 
 /* Row related funcs */
 
 static gint
-geometry_get_row_count(const GSheetRow *geom, gpointer data)
+geometry_get_row_count (const GSheetRow *geom, gpointer data)
 {
-  PsppireDataStore *ds = PSPPIRE_DATA_STORE(geom);
+  PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
 
-  return TRAILING_ROWS + psppire_case_file_get_case_count(ds->case_file);
+  return TRAILING_ROWS + psppire_case_file_get_case_count (ds->case_file);
 }
 
 
 static gint
-geometry_get_height(const GSheetRow *geom, gint unit, gpointer data)
+geometry_get_height (const GSheetRow *geom, gint unit, gpointer data)
 {
   return 25;
 }
 
 
 static gboolean
-geometry_get_row_sensitivity(const GSheetRow *geom, gint unit, gpointer data)
+geometry_get_row_sensitivity (const GSheetRow *geom, gint unit, gpointer data)
 {
-  PsppireDataStore *ds = PSPPIRE_DATA_STORE(geom);
+  PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
 
-  
-  return (unit < psppire_case_file_get_case_count(ds->case_file));
+
+  return (unit < psppire_case_file_get_case_count (ds->case_file));
 }
 
 
 static gchar *
-geometry_get_row_button_label(const GSheetRow *geom, gint unit, gpointer data)
+geometry_get_row_button_label (const GSheetRow *geom, gint unit, gpointer data)
 {
   gchar *text;
   gchar *s;
-  PsppireDataStore *ds = PSPPIRE_DATA_STORE(geom);
+  PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
 
-  if ( unit > 
-       TRAILING_ROWS + psppire_case_file_get_case_count(ds->case_file))
+  if ( unit >
+       TRAILING_ROWS + psppire_case_file_get_case_count (ds->case_file))
     return 0;
 
-  s = g_strdup_printf(_("%d"), unit);
+  s = g_strdup_printf (_("%d"), unit + FIRST_CASE_NUMBER);
+
+  text =  pspp_locale_to_utf8 (s, -1, 0);
+
+  g_free (s);
 
-  text =  pspp_locale_to_utf8(s, -1, 0);
-  
-  g_free(s);
-  
   return text;
 }
 
@@ -821,3 +848,6 @@ psppire_data_store_sheet_row_init (GSheetRowIface *iface)
 
   iface->get_button_label = geometry_get_row_button_label;
 }
+
+
+