Categoricals.c Create entries for all groups.
[pspp] / src / ui / gui / psppire-var-store.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2006, 2009, 2010  Free Software Foundation
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18 #include <string.h>
19 #include <stdlib.h>
20 #include <gettext.h>
21 #define _(msgid) gettext (msgid)
22 #define N_(msgid) msgid
23
24 #include <libpspp/i18n.h>
25
26 #include <gobject/gvaluecollector.h>
27
28 #include <ui/gui/sheet/psppire-sheetmodel.h>
29
30 #include "psppire-var-store.h"
31 #include "helper.h"
32
33 #include <data/dictionary.h>
34 #include <data/variable.h>
35 #include <data/format.h>
36 #include <data/missing-values.h>
37
38 #include "val-labs-dialog.h"
39 #include "missing-val-dialog.h"
40 #include <data/value-labels.h>
41
42 #include "var-display.h"
43
44 static void
45 var_change_callback (GtkWidget *w, gint n, gpointer data)
46 {
47   PsppireSheetModel *model = PSPPIRE_SHEET_MODEL (data);
48
49   psppire_sheet_model_range_changed (model,
50                                  n, 0, n, PSPPIRE_VAR_STORE_n_COLS);
51 }
52
53
54 static void
55 var_delete_callback (GtkWidget *w, gint dict_idx, gint case_idx, gint val_cnt, gpointer data)
56 {
57   PsppireSheetModel *model = PSPPIRE_SHEET_MODEL (data);
58
59   psppire_sheet_model_rows_deleted (model, dict_idx, 1);
60 }
61
62
63
64 static void
65 var_insert_callback (GtkWidget *w, glong row, gpointer data)
66 {
67   PsppireSheetModel *model = PSPPIRE_SHEET_MODEL (data);
68
69   psppire_sheet_model_rows_inserted (model, row, 1);
70 }
71
72 static void
73 refresh (PsppireDict  *d, gpointer data)
74 {
75   PsppireVarStore *vs = data;
76
77   psppire_sheet_model_range_changed (PSPPIRE_SHEET_MODEL (vs), -1, -1, -1, -1);
78 }
79
80 enum
81   {
82     PROP_0,
83     PSPPIRE_VAR_STORE_FORMAT_TYPE,
84     PSPPIRE_VAR_STORE_DICT
85   };
86
87 static void         psppire_var_store_init            (PsppireVarStore      *var_store);
88 static void         psppire_var_store_class_init      (PsppireVarStoreClass *class);
89 static void         psppire_var_store_sheet_model_init (PsppireSheetModelIface *iface);
90 static void         psppire_var_store_finalize        (GObject           *object);
91 static void         psppire_var_store_dispose        (GObject           *object);
92
93
94 static gchar *psppire_var_store_get_string (const PsppireSheetModel *sheet_model, glong row, glong column);
95
96 static gboolean  psppire_var_store_clear (PsppireSheetModel *model,  glong row, glong col);
97
98
99 static gboolean psppire_var_store_set_string (PsppireSheetModel *model,
100                                           const gchar *text, glong row, glong column);
101
102 static glong psppire_var_store_get_row_count (const PsppireSheetModel * model);
103 static glong psppire_var_store_get_column_count (const PsppireSheetModel * model);
104
105 static gchar *text_for_column (PsppireVarStore *vs, const struct variable *pv,
106                                gint c, GError **err);
107
108
109 static GObjectClass *parent_class = NULL;
110
111 GType
112 psppire_var_store_format_type_get_type (void)
113 {
114   static GType etype = 0;
115   if (etype == 0)
116     {
117       static const GEnumValue values[] =
118         {
119           { PSPPIRE_VAR_STORE_INPUT_FORMATS,
120             "PSPPIRE_VAR_STORE_INPUT_FORMATS",
121             "input" },
122           { PSPPIRE_VAR_STORE_OUTPUT_FORMATS,
123             "PSPPIRE_VAR_STORE_OUTPUT_FORMATS",
124             "output" },
125           { 0, NULL, NULL }
126         };
127
128       etype = g_enum_register_static
129         (g_intern_static_string ("PsppireVarStoreFormatType"), values);
130
131     }
132   return etype;
133 }
134
135 GType
136 psppire_var_store_get_type (void)
137 {
138   static GType var_store_type = 0;
139
140   if (!var_store_type)
141     {
142       static const GTypeInfo var_store_info =
143       {
144         sizeof (PsppireVarStoreClass),
145         NULL,           /* base_init */
146         NULL,           /* base_finalize */
147         (GClassInitFunc) psppire_var_store_class_init,
148         NULL,           /* class_finalize */
149         NULL,           /* class_data */
150         sizeof (PsppireVarStore),
151         0,
152         (GInstanceInitFunc) psppire_var_store_init,
153       };
154
155       static const GInterfaceInfo sheet_model_info =
156       {
157         (GInterfaceInitFunc) psppire_var_store_sheet_model_init,
158         NULL,
159         NULL
160       };
161
162       var_store_type = g_type_register_static (G_TYPE_OBJECT, "PsppireVarStore", &var_store_info, 0);
163
164       g_type_add_interface_static (var_store_type,
165                                    PSPPIRE_TYPE_SHEET_MODEL,
166                                    &sheet_model_info);
167     }
168
169   return var_store_type;
170 }
171
172 static void
173 psppire_var_store_set_property (GObject      *object,
174                                 guint         property_id,
175                                 const GValue *value,
176                                 GParamSpec   *pspec)
177 {
178   PsppireVarStore *self = (PsppireVarStore *) object;
179
180   switch (property_id)
181     {
182     case PSPPIRE_VAR_STORE_FORMAT_TYPE:
183       self->format_type = g_value_get_enum (value);
184       break;
185
186     case PSPPIRE_VAR_STORE_DICT:
187       if ( self->dictionary)
188         g_object_unref (self->dictionary);
189       self->dictionary = g_value_dup_object (value);
190       g_signal_connect (self->dictionary, "variable-changed", G_CALLBACK (var_change_callback),
191                         self);
192
193       g_signal_connect (self->dictionary, "variable-deleted", G_CALLBACK (var_delete_callback),
194                         self);
195
196       g_signal_connect (self->dictionary, "variable-inserted",
197                         G_CALLBACK (var_insert_callback), self);
198
199       g_signal_connect (self->dictionary, "backend-changed", G_CALLBACK (refresh),
200                         self);
201
202       /* The entire model has changed */
203       psppire_sheet_model_range_changed (PSPPIRE_SHEET_MODEL (self), -1, -1, -1, -1);
204
205       break;
206
207     default:
208       G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
209       break;
210     }
211 }
212
213 static void
214 psppire_var_store_get_property (GObject      *object,
215                         guint         property_id,
216                         GValue       *value,
217                         GParamSpec   *pspec)
218 {
219   PsppireVarStore *self = (PsppireVarStore *) object;
220
221   switch (property_id)
222     {
223     case PSPPIRE_VAR_STORE_FORMAT_TYPE:
224       g_value_set_enum (value, self->format_type);
225       break;
226
227     case PSPPIRE_VAR_STORE_DICT:
228       g_value_take_object (value, self->dictionary);
229       break;
230
231     default:
232       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
233       break;
234     }
235 }
236
237
238 static void
239 psppire_var_store_class_init (PsppireVarStoreClass *class)
240 {
241   GObjectClass *object_class;
242   GParamSpec *format_pspec;
243   GParamSpec *dict_pspec;
244
245   parent_class = g_type_class_peek_parent (class);
246   object_class = (GObjectClass*) class;
247
248   object_class->finalize = psppire_var_store_finalize;
249   object_class->dispose = psppire_var_store_dispose;
250   object_class->set_property = psppire_var_store_set_property;
251   object_class->get_property = psppire_var_store_get_property;
252
253   format_pspec = g_param_spec_enum ("format-type",
254                              "Variable format type",
255                              ("Whether variables have input or output "
256                               "formats"),
257                              PSPPIRE_TYPE_VAR_STORE_FORMAT_TYPE,
258                              PSPPIRE_VAR_STORE_OUTPUT_FORMATS,
259                              G_PARAM_READWRITE);
260
261   g_object_class_install_property (object_class,
262                                    PSPPIRE_VAR_STORE_FORMAT_TYPE,
263                                    format_pspec);
264
265   dict_pspec = g_param_spec_object ("dictionary",
266                                     "Dictionary",
267                                     "The PsppireDict represented by this var store",
268                                     PSPPIRE_TYPE_DICT,
269                                     G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
270                                     
271   g_object_class_install_property (object_class,
272                                    PSPPIRE_VAR_STORE_DICT,
273                                    dict_pspec);
274 }
275
276 #define DISABLED_COLOR "gray"
277
278 static void
279 psppire_var_store_init (PsppireVarStore *var_store)
280 {
281   if ( ! gdk_color_parse (DISABLED_COLOR, &var_store->disabled))
282         g_critical ("Could not parse color \"%s\"", DISABLED_COLOR);
283
284   var_store->dictionary = NULL;
285   var_store->format_type = PSPPIRE_VAR_STORE_OUTPUT_FORMATS;
286 }
287
288 static gboolean
289 psppire_var_store_item_editable (PsppireVarStore *var_store, glong row, glong column)
290 {
291   const struct fmt_spec *write_spec ;
292
293   struct variable *pv = psppire_var_store_get_var (var_store, row);
294
295   if ( !pv )
296     return TRUE;
297
298   if ( var_is_alpha (pv) && column == PSPPIRE_VAR_STORE_COL_DECIMALS )
299     return FALSE;
300
301   write_spec = var_get_print_format (pv);
302
303   switch ( write_spec->type )
304     {
305     case FMT_DATE:
306     case FMT_EDATE:
307     case FMT_SDATE:
308     case FMT_ADATE:
309     case FMT_JDATE:
310     case FMT_QYR:
311     case FMT_MOYR:
312     case FMT_WKYR:
313     case FMT_DATETIME:
314     case FMT_TIME:
315     case FMT_DTIME:
316     case FMT_WKDAY:
317     case FMT_MONTH:
318       if ( column == PSPPIRE_VAR_STORE_COL_DECIMALS || column == PSPPIRE_VAR_STORE_COL_WIDTH)
319         return FALSE;
320       break;
321     default:
322       break;
323     }
324
325   return TRUE;
326 }
327
328
329 struct variable *
330 psppire_var_store_get_var (PsppireVarStore *store, glong row)
331 {
332   return psppire_dict_get_variable (store->dictionary, row);
333 }
334
335 static gboolean
336 psppire_var_store_is_editable (const PsppireSheetModel *model, glong row, glong column)
337 {
338   PsppireVarStore *store = PSPPIRE_VAR_STORE (model);
339   return psppire_var_store_item_editable (store, row, column);
340 }
341
342
343 static GdkColor *
344 psppire_var_store_get_foreground (const PsppireSheetModel *model, glong row, glong column)
345 {
346   PsppireVarStore *store = PSPPIRE_VAR_STORE (model);
347
348   if ( ! psppire_var_store_item_editable (store, row, column) )
349     return &store->disabled;
350
351   return NULL;
352 }
353
354
355 static gchar *get_column_title (const PsppireSheetModel *model, gint col);
356 static gchar *get_row_title (const PsppireSheetModel *model, gint row);
357 static gboolean get_row_sensitivity (const PsppireSheetModel *model, gint row);
358
359 static void
360 psppire_var_store_sheet_model_init (PsppireSheetModelIface *iface)
361 {
362   iface->get_row_count = psppire_var_store_get_row_count;
363   iface->get_column_count = psppire_var_store_get_column_count;
364   iface->free_strings = TRUE;
365   iface->get_string = psppire_var_store_get_string;
366   iface->set_string = psppire_var_store_set_string;
367   iface->clear_datum = psppire_var_store_clear;
368   iface->is_editable = psppire_var_store_is_editable;
369   iface->get_foreground = psppire_var_store_get_foreground;
370   iface->get_background = NULL;
371   iface->get_justification = NULL;
372
373   iface->get_column_title = get_column_title;
374
375   iface->get_row_title = get_row_title;
376   iface->get_row_sensitivity = get_row_sensitivity;
377
378   iface->get_row_overstrike = NULL;
379 }
380
381 /**
382  * psppire_var_store_new:
383  * @dict: The dictionary for this var_store.
384  *
385  *
386  * Return value: a new #PsppireVarStore
387  **/
388 PsppireVarStore *
389 psppire_var_store_new (PsppireDict *dict)
390 {
391   PsppireVarStore *retval;
392
393   retval = g_object_new (GTK_TYPE_VAR_STORE, "dictionary", dict, NULL);
394
395   //  psppire_var_store_set_dictionary (retval, dict);
396
397   return retval;
398 }
399
400 #if 0
401 /**
402  * psppire_var_store_replace_set_dictionary:
403  * @var_store: The variable store
404  * @dict: The dictionary to set
405  *
406  * If a dictionary is already associated with the var-store, then it will be
407  * destroyed.
408  **/
409 void
410 psppire_var_store_set_dictionary (PsppireVarStore *var_store, PsppireDict *dict)
411 {
412   if ( var_store->dict ) g_object_unref (var_store->dict);
413
414   var_store->dict = dict;
415
416   g_signal_connect (dict, "variable-changed", G_CALLBACK (var_change_callback),
417                    var_store);
418
419   g_signal_connect (dict, "variable-deleted", G_CALLBACK (var_delete_callback),
420                    var_store);
421
422   g_signal_connect (dict, "variable-inserted",
423                     G_CALLBACK (var_insert_callback), var_store);
424
425   g_signal_connect (dict, "backend-changed", G_CALLBACK (refresh),
426                     var_store);
427
428   /* The entire model has changed */
429   psppire_sheet_model_range_changed (PSPPIRE_SHEET_MODEL (var_store), -1, -1, -1, -1);
430 }
431 #endif
432
433 static void
434 psppire_var_store_finalize (GObject *object)
435 {
436   /* must chain up */
437   (* parent_class->finalize) (object);
438 }
439
440 static void
441 psppire_var_store_dispose (GObject *object)
442 {
443   PsppireVarStore *self = PSPPIRE_VAR_STORE (object);
444
445   if (self->dictionary)
446     g_object_unref (self->dictionary);
447
448   /* must chain up */
449   (* parent_class->finalize) (object);
450 }
451
452
453 static gchar *
454 psppire_var_store_get_string (const PsppireSheetModel *model,
455                               glong row, glong column)
456 {
457   PsppireVarStore *store = PSPPIRE_VAR_STORE (model);
458
459   struct variable *pv;
460
461   if ( row >= psppire_dict_get_var_cnt (store->dictionary))
462     return 0;
463
464   pv = psppire_dict_get_variable (store->dictionary, row);
465
466   return text_for_column (store, pv, column, 0);
467 }
468
469
470 /* Clears that part of the variable store, if possible, which corresponds
471    to ROW, COL.
472    Returns true if anything was updated, false otherwise.
473 */
474 static gboolean
475 psppire_var_store_clear (PsppireSheetModel *model,  glong row, glong col)
476 {
477   struct variable *pv ;
478
479   PsppireVarStore *var_store = PSPPIRE_VAR_STORE (model);
480
481   if ( row >= psppire_dict_get_var_cnt (var_store->dictionary))
482       return FALSE;
483
484   pv = psppire_var_store_get_var (var_store, row);
485
486   if ( !pv )
487     return FALSE;
488
489   switch (col)
490     {
491     case PSPPIRE_VAR_STORE_COL_LABEL:
492       var_set_label (pv, NULL);
493       return TRUE;
494       break;
495     }
496
497   return FALSE;
498 }
499
500 /* Attempts to update that part of the variable store which corresponds
501    to ROW, COL with  the value TEXT.
502    Returns true if anything was updated, false otherwise.
503 */
504 static gboolean
505 psppire_var_store_set_string (PsppireSheetModel *model,
506                           const gchar *text, glong row, glong col)
507 {
508   struct variable *pv ;
509
510   PsppireVarStore *var_store = PSPPIRE_VAR_STORE (model);
511
512   if ( row >= psppire_dict_get_var_cnt (var_store->dictionary))
513       return FALSE;
514
515   pv = psppire_var_store_get_var (var_store, row);
516
517   if ( !pv )
518     return FALSE;
519
520   switch (col)
521     {
522     case PSPPIRE_VAR_STORE_COL_NAME:
523       {
524         gboolean ok;
525         ok =  psppire_dict_rename_var (var_store->dictionary, pv, text);
526         return ok;
527       }
528     case PSPPIRE_VAR_STORE_COL_COLUMNS:
529       if ( ! text) return FALSE;
530       var_set_display_width (pv, atoi (text));
531       return TRUE;
532       break;
533     case PSPPIRE_VAR_STORE_COL_WIDTH:
534       {
535         const int width = atoi (text);
536         if ( ! text)
537           return FALSE;
538
539         if (width < 0)
540           return FALSE;
541
542         if ( var_is_alpha (pv))
543           {
544             if ( width > MAX_STRING )
545               return FALSE;
546             var_set_width (pv, width);
547           }
548         else
549           {
550             bool for_input
551               = var_store->format_type == PSPPIRE_VAR_STORE_INPUT_FORMATS;
552             struct fmt_spec fmt ;
553             fmt = *var_get_write_format (pv);
554             if ( width < fmt_min_width (fmt.type, for_input)
555                  ||
556                  width > fmt_max_width (fmt.type, for_input))
557               return FALSE;
558
559             fmt.w = width;
560             fmt.d = MIN (fmt_max_decimals (fmt.type, width, for_input), fmt.d);
561
562             var_set_both_formats (pv, &fmt);
563           }
564
565         return TRUE;
566       }
567       break;
568     case PSPPIRE_VAR_STORE_COL_DECIMALS:
569       {
570         bool for_input
571           = var_store->format_type == PSPPIRE_VAR_STORE_INPUT_FORMATS;
572         int decimals;
573         struct fmt_spec fmt;
574         if ( ! text) return FALSE;
575         decimals = atoi (text);
576         fmt = *var_get_write_format (pv);
577         if ( decimals >
578              fmt_max_decimals (fmt.type,
579                                fmt.w,
580                                for_input
581                                ))
582           return FALSE;
583
584         fmt.d = decimals;
585         var_set_both_formats (pv, &fmt);
586         return TRUE;
587       }
588       break;
589     case PSPPIRE_VAR_STORE_COL_LABEL:
590       {
591         var_set_label (pv, text);
592         return TRUE;
593       }
594       break;
595     case PSPPIRE_VAR_STORE_COL_TYPE:
596     case PSPPIRE_VAR_STORE_COL_VALUES:
597     case PSPPIRE_VAR_STORE_COL_MISSING:
598     case PSPPIRE_VAR_STORE_COL_ALIGN:
599     case PSPPIRE_VAR_STORE_COL_MEASURE:
600       /* These can be modified only by their respective dialog boxes */
601       return FALSE;
602       break;
603     default:
604       g_assert_not_reached ();
605       return FALSE;
606     }
607
608   return TRUE;
609 }
610
611
612 static const gchar none[] = N_("None");
613
614 static  gchar *
615 text_for_column (PsppireVarStore *vs,
616                  const struct variable *pv, gint c, GError **err)
617 {
618   PsppireDict *dict = vs->dictionary;
619   static const gchar *const type_label[] =
620     {
621       N_("Numeric"),
622       N_("Comma"),
623       N_("Dot"),
624       N_("Scientific"),
625       N_("Date"),
626       N_("Dollar"),
627       N_("Custom"),
628       N_("String")
629     };
630
631   enum {VT_NUMERIC, VT_COMMA, VT_DOT, VT_SCIENTIFIC, VT_DATE, VT_DOLLAR,
632         VT_CUSTOM, VT_STRING};
633
634   const struct fmt_spec *write_spec = var_get_write_format (pv);
635
636   switch (c)
637     {
638     case PSPPIRE_VAR_STORE_COL_NAME:
639       return xstrdup (var_get_name (pv));
640       break;
641     case PSPPIRE_VAR_STORE_COL_TYPE:
642       {
643         switch ( write_spec->type )
644           {
645           case FMT_F:
646             return xstrdup (gettext (type_label[VT_NUMERIC]));
647             break;
648           case FMT_COMMA:
649             return xstrdup (gettext (type_label[VT_COMMA]));
650             break;
651           case FMT_DOT:
652             return xstrdup (gettext (type_label[VT_DOT]));
653             break;
654           case FMT_E:
655             return xstrdup (gettext (type_label[VT_SCIENTIFIC]));
656             break;
657           case FMT_DATE:
658           case FMT_EDATE:
659           case FMT_SDATE:
660           case FMT_ADATE:
661           case FMT_JDATE:
662           case FMT_QYR:
663           case FMT_MOYR:
664           case FMT_WKYR:
665           case FMT_DATETIME:
666           case FMT_TIME:
667           case FMT_DTIME:
668           case FMT_WKDAY:
669           case FMT_MONTH:
670             return xstrdup (gettext (type_label[VT_DATE]));
671             break;
672           case FMT_DOLLAR:
673             return xstrdup (gettext (type_label[VT_DOLLAR]));
674             break;
675           case FMT_CCA:
676           case FMT_CCB:
677           case FMT_CCC:
678           case FMT_CCD:
679           case FMT_CCE:
680             return xstrdup (gettext (type_label[VT_CUSTOM]));
681             break;
682           case FMT_A:
683             return xstrdup (gettext (type_label[VT_STRING]));
684             break;
685           default:
686             {
687               char str[FMT_STRING_LEN_MAX + 1];
688               g_warning ("Unknown format: \"%s\"\n",
689                         fmt_to_string (write_spec, str));
690             }
691             break;
692           }
693       }
694       break;
695     case PSPPIRE_VAR_STORE_COL_WIDTH:
696       {
697         gchar *s;
698         GString *gstr = g_string_sized_new (10);
699         g_string_printf (gstr, _("%d"), write_spec->w);
700         s = g_locale_to_utf8 (gstr->str, gstr->len, 0, 0, err);
701         g_string_free (gstr, TRUE);
702         return s;
703       }
704       break;
705     case PSPPIRE_VAR_STORE_COL_DECIMALS:
706       {
707         gchar *s;
708         GString *gstr = g_string_sized_new (10);
709         g_string_printf (gstr, _("%d"), write_spec->d);
710         s = g_locale_to_utf8 (gstr->str, gstr->len, 0, 0, err);
711         g_string_free (gstr, TRUE);
712         return s;
713       }
714       break;
715     case PSPPIRE_VAR_STORE_COL_COLUMNS:
716       {
717         gchar *s;
718         GString *gstr = g_string_sized_new (10);
719         g_string_printf (gstr, _("%d"), var_get_display_width (pv));
720         s = g_locale_to_utf8 (gstr->str, gstr->len, 0, 0, err);
721         g_string_free (gstr, TRUE);
722         return s;
723       }
724       break;
725     case PSPPIRE_VAR_STORE_COL_LABEL:
726       {
727         const char *label = var_get_label (pv);
728         if (label)
729           return xstrdup (label);
730         return NULL;
731       }
732       break;
733
734     case PSPPIRE_VAR_STORE_COL_MISSING:
735       {
736         return missing_values_to_string (dict, pv, err);
737       }
738       break;
739     case PSPPIRE_VAR_STORE_COL_VALUES:
740       {
741         if ( ! var_has_value_labels (pv))
742           return xstrdup (gettext (none));
743         else
744           {
745             const struct val_labs *vls = var_get_value_labels (pv);
746             const struct val_lab **labels = val_labs_sorted (vls);
747             const struct val_lab *vl = labels[0];
748             free (labels);
749
750             g_assert (vl);
751
752             {
753               gchar *const vstr = value_to_text (vl->value, dict, *write_spec);
754
755               return g_strdup_printf ( "{%s,\"%s\"}_", vstr, val_lab_get_label (vl));
756             }
757           }
758       }
759       break;
760     case PSPPIRE_VAR_STORE_COL_ALIGN:
761       {
762         const gint align = var_get_alignment (pv);
763
764         g_assert (align < n_ALIGNMENTS);
765         return xstrdup (gettext (alignments[align]));
766       }
767       break;
768     case PSPPIRE_VAR_STORE_COL_MEASURE:
769       {
770         return xstrdup (measure_to_string (pv, err));
771       }
772       break;
773     }
774   return 0;
775 }
776
777
778
779 /* Return the number of variables */
780 gint
781 psppire_var_store_get_var_cnt (PsppireVarStore  *store)
782 {
783   return psppire_dict_get_var_cnt (store->dictionary);
784 }
785
786
787 static glong
788 psppire_var_store_get_row_count (const PsppireSheetModel * model)
789 {
790   gint rows = 0;
791   PsppireVarStore *vs = PSPPIRE_VAR_STORE (model);
792
793   if (vs->dictionary)
794     rows =  psppire_dict_get_var_cnt (vs->dictionary);
795
796   return rows ;
797 }
798
799 static glong
800 psppire_var_store_get_column_count (const PsppireSheetModel * model)
801 {
802   return PSPPIRE_VAR_STORE_n_COLS ;
803 }
804
805 \f
806
807 /* Row related funcs */
808
809
810 static gboolean
811 get_row_sensitivity (const PsppireSheetModel *model, gint row)
812 {
813   PsppireVarStore *vs = PSPPIRE_VAR_STORE (model);
814
815   if ( ! vs->dictionary)
816     return FALSE;
817
818   return  row < psppire_dict_get_var_cnt (vs->dictionary);
819 }
820
821
822 static gchar *
823 get_row_title (const PsppireSheetModel *model, gint unit)
824 {
825   return g_strdup_printf (_("%d"), unit + 1);
826 }
827
828
829 \f
830
831 static const gchar *column_titles[] = {
832   N_("Name"),
833   N_("Type"),
834   N_("Width"),
835   N_("Decimals"),
836   N_("Label"),
837   N_("Values"),
838   N_("Missing"),
839   N_("Columns"),
840   N_("Align"),
841   N_("Measure"),
842 };
843
844
845 static gchar *
846 get_column_title (const PsppireSheetModel *model, gint col)
847 {
848   if ( col >= 10)
849     return NULL;
850   return g_strdup (gettext (column_titles[col]));
851 }