(Re)enabled cell reference entry and datum entry widgets.
[pspp-builds.git] / src / ui / gui / psppire-data-store.c
1 /* psppire-data-store.c
2
3    PSPPIRE --- A Graphical User Interface for PSPP
4    Copyright (C) 2006  Free Software Foundation
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19    02110-1301, USA. */
20
21 #include <config.h>
22 #include <string.h>
23 #include <stdlib.h>
24 #include <gettext.h>
25 #define _(msgid) gettext (msgid)
26 #define N_(msgid) msgid
27
28 #include <data/casewriter.h>
29 #include <data/datasheet.h>
30 #include <data/data-out.h>
31 #include <data/variable.h>
32
33 #include <gtksheet/gtksheet.h>
34 #include <gtksheet/gsheetmodel.h>
35 #include <gtksheet/gsheet-column-iface.h>
36
37 #include <pango/pango-context.h>
38
39 #include "psppire-data-store.h"
40 #include "psppire-case-file.h"
41 #include "helper.h"
42
43 #include <data/dictionary.h>
44 #include <data/missing-values.h>
45 #include <data/value-labels.h>
46 #include <data/data-in.h>
47
48 #include <data/file-handle-def.h>
49 #include <data/sys-file-writer.h>
50
51
52
53 static void psppire_data_store_init            (PsppireDataStore      *data_store);
54 static void psppire_data_store_class_init      (PsppireDataStoreClass *class);
55 static void psppire_data_store_sheet_model_init (GSheetModelIface *iface);
56 static void psppire_data_store_sheet_column_init (GSheetColumnIface *iface);
57 static void psppire_data_store_sheet_row_init (GSheetRowIface *iface);
58
59 static void psppire_data_store_finalize        (GObject           *object);
60
61 static gboolean psppire_data_store_clear_datum (GSheetModel *model,
62                                           gint row, gint column);
63
64
65 #define MIN_COLUMNS 10
66
67 #define TRAILING_ROWS 10
68
69 static GObjectClass *parent_class = NULL;
70
71
72 enum  {FONT_CHANGED,
73        n_SIGNALS};
74
75 static guint signals [n_SIGNALS];
76
77
78 inline GType
79 psppire_data_store_get_type (void)
80 {
81   static GType data_store_type = 0;
82
83   if (!data_store_type)
84     {
85       static const GTypeInfo data_store_info =
86       {
87         sizeof (PsppireDataStoreClass),
88         NULL,           /* base_init */
89         NULL,           /* base_finalize */
90         (GClassInitFunc) psppire_data_store_class_init,
91         NULL,           /* class_finalize */
92         NULL,           /* class_data */
93         sizeof (PsppireDataStore),
94         0,
95         (GInstanceInitFunc) psppire_data_store_init,
96       };
97
98       static const GInterfaceInfo sheet_model_info =
99       {
100         (GInterfaceInitFunc) psppire_data_store_sheet_model_init,
101         NULL,
102         NULL
103       };
104
105       static const GInterfaceInfo sheet_column_info =
106       {
107         (GInterfaceInitFunc) psppire_data_store_sheet_column_init,
108         NULL,
109         NULL
110       };
111
112       static const GInterfaceInfo sheet_row_info =
113       {
114         (GInterfaceInitFunc) psppire_data_store_sheet_row_init,
115         NULL,
116         NULL
117       };
118
119
120       data_store_type = g_type_register_static (G_TYPE_OBJECT, "PsppireDataStore",
121                                                 &data_store_info, 0);
122
123       g_type_add_interface_static (data_store_type,
124                                    G_TYPE_SHEET_MODEL,
125                                    &sheet_model_info);
126
127       g_type_add_interface_static (data_store_type,
128                                    G_TYPE_SHEET_COLUMN,
129                                    &sheet_column_info);
130
131       g_type_add_interface_static (data_store_type,
132                                    G_TYPE_SHEET_ROW,
133                                    &sheet_row_info);
134     }
135
136   return data_store_type;
137 }
138
139
140 static void
141 psppire_data_store_class_init (PsppireDataStoreClass *class)
142 {
143   GObjectClass *object_class;
144
145   parent_class = g_type_class_peek_parent (class);
146   object_class = (GObjectClass*) class;
147
148   object_class->finalize = psppire_data_store_finalize;
149
150   signals [FONT_CHANGED] =
151     g_signal_new ("font_changed",
152                   G_TYPE_FROM_CLASS (class),
153                   G_SIGNAL_RUN_FIRST,
154                   0,
155                   NULL, NULL,
156                   g_cclosure_marshal_VOID__VOID,
157                   G_TYPE_NONE,
158                   0);
159 }
160
161
162
163 static gint
164 psppire_data_store_get_var_count (const GSheetModel *model)
165 {
166   const PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
167
168   return psppire_dict_get_var_cnt (store->dict);
169 }
170
171 static gint
172 psppire_data_store_get_case_count (const GSheetModel *model)
173 {
174   const PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
175
176   return psppire_case_file_get_case_count (store->case_file);
177 }
178
179
180 static void
181 psppire_data_store_init (PsppireDataStore *data_store)
182 {
183   data_store->dict = 0;
184   data_store->case_file = 0;
185   data_store->width_of_m = 10;
186 }
187
188 const PangoFontDescription *
189 psppire_data_store_get_font_desc (const GSheetModel *model,
190                               gint row, gint column)
191 {
192   PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
193
194   return store->font_desc;
195 }
196
197 static inline gchar *
198 psppire_data_store_get_string_wrapper (const GSheetModel *model, gint row,
199                                        gint column)
200 {
201   return psppire_data_store_get_string (PSPPIRE_DATA_STORE (model), row, column);
202 }
203
204
205 static inline gboolean
206 psppire_data_store_set_string_wrapper (GSheetModel *model,
207                                        const gchar *text,
208                                        gint row, gint column)
209 {
210   return psppire_data_store_set_string (PSPPIRE_DATA_STORE (model), text,
211                                         row, column);
212 }
213
214
215
216
217 static void
218 psppire_data_store_sheet_model_init (GSheetModelIface *iface)
219 {
220   iface->free_strings = TRUE;
221   iface->get_string = psppire_data_store_get_string_wrapper;
222   iface->set_string = psppire_data_store_set_string_wrapper;
223   iface->clear_datum = psppire_data_store_clear_datum;
224   iface->is_editable = NULL;
225   iface->is_visible = NULL;
226   iface->get_foreground = NULL;
227   iface->get_background = NULL;
228   iface->get_font_desc = psppire_data_store_get_font_desc;
229   iface->get_cell_border = NULL;
230   iface->get_column_count = psppire_data_store_get_var_count;
231   iface->get_row_count = psppire_data_store_get_case_count;
232 }
233
234 static
235 gboolean always_true ()
236 {
237   return TRUE;
238 }
239
240
241 static void
242 delete_cases_callback (GtkWidget *w, gint first, gint n_cases, gpointer data)
243 {
244   PsppireDataStore *store  ;
245
246   g_return_if_fail (data);
247
248   store  = PSPPIRE_DATA_STORE (data);
249
250   g_assert (first >= 0);
251
252   g_sheet_model_rows_deleted (G_SHEET_MODEL (store), first, n_cases);
253 }
254
255
256 static void
257 insert_case_callback (GtkWidget *w, gint casenum, gpointer data)
258 {
259   PsppireDataStore *store  ;
260
261   g_return_if_fail (data);
262
263   store  = PSPPIRE_DATA_STORE (data);
264
265   g_sheet_model_range_changed (G_SHEET_MODEL (store),
266                                casenum, -1,
267                                psppire_case_file_get_case_count (store->case_file),
268                                -1);
269
270   g_sheet_model_rows_inserted (G_SHEET_MODEL (store), casenum, 1);
271 }
272
273
274 static void
275 changed_case_callback (GtkWidget *w, gint casenum, gpointer data)
276 {
277   PsppireDataStore *store  ;
278   g_return_if_fail (data);
279
280   store  = PSPPIRE_DATA_STORE (data);
281
282   g_sheet_model_range_changed (G_SHEET_MODEL (store),
283                                  casenum, -1,
284                                  casenum, -1);
285 }
286
287
288 static void
289 delete_variables_callback (GObject *obj, gint var_num, gint n_vars, gpointer data)
290 {
291   PsppireDataStore *store ;
292
293   g_return_if_fail (data);
294
295   store  = PSPPIRE_DATA_STORE (data);
296
297   g_sheet_model_columns_deleted (G_SHEET_MODEL (store), var_num, n_vars);
298
299   g_sheet_column_columns_changed (G_SHEET_COLUMN (store),
300                                    var_num, -1);
301 }
302
303
304 static void
305 variable_changed_callback (GObject *obj, gint var_num, gpointer data)
306 {
307   PsppireDataStore *store;
308
309   g_return_if_fail (data);
310
311   store  = PSPPIRE_DATA_STORE (data);
312
313   g_sheet_column_columns_changed (G_SHEET_COLUMN (store),
314                                   var_num, 1);
315
316
317   g_sheet_model_range_changed (G_SHEET_MODEL (store),
318                                -1, var_num,
319                                -1, var_num);
320 }
321
322 static void
323 insert_variable_callback (GObject *obj, gint var_num, gpointer data)
324 {
325   PsppireDataStore *store;
326   gint posn;
327
328   g_return_if_fail (data);
329
330   store  = PSPPIRE_DATA_STORE (data);
331
332   if ( var_num > 0 )
333     {
334       struct variable *variable =
335         psppire_dict_get_variable (store->dict, var_num);
336
337       g_assert (variable != NULL);
338
339       posn = var_get_case_index (variable);
340     }
341   else
342     {
343       posn = 0;
344     }
345
346   psppire_case_file_insert_values (store->case_file, 1, posn);
347
348   g_sheet_column_columns_changed (G_SHEET_COLUMN (store),
349                                   var_num, 1);
350
351   g_sheet_model_columns_inserted (G_SHEET_MODEL (store), var_num, 1);
352 }
353
354
355 static void
356 dict_size_change_callback (GObject *obj,
357                           gint posn, gint adjustment, gpointer data)
358 {
359   PsppireDataStore *store ;
360
361   g_return_if_fail (data);
362
363   store  = PSPPIRE_DATA_STORE (data);
364
365   psppire_case_file_insert_values (store->case_file, adjustment, posn);
366 }
367
368
369
370 /**
371  * psppire_data_store_new:
372  * @dict: The dictionary for this data_store.
373  *
374  *
375  * Return value: a new #PsppireDataStore
376  **/
377 PsppireDataStore *
378 psppire_data_store_new (PsppireDict *dict)
379 {
380   PsppireDataStore *retval;
381
382   retval = g_object_new (GTK_TYPE_DATA_STORE, NULL);
383
384   psppire_data_store_set_dictionary (retval, dict);
385
386   return retval;
387 }
388
389
390 void
391 psppire_data_store_set_case_file (PsppireDataStore *data_store,
392                                   PsppireCaseFile *cf)
393 {
394   if ( data_store->case_file)
395     {
396       g_object_unref (data_store->case_file);
397     }
398
399   data_store->case_file = cf;
400
401   g_signal_connect (data_store->case_file, "cases-deleted",
402                    G_CALLBACK (delete_cases_callback),
403                    data_store);
404
405   g_signal_connect (data_store->case_file, "case-inserted",
406                    G_CALLBACK (insert_case_callback),
407                    data_store);
408
409
410   g_signal_connect (data_store->case_file, "case-changed",
411                    G_CALLBACK (changed_case_callback),
412                    data_store);
413 }
414
415
416
417 /**
418  * psppire_data_store_replace_set_dictionary:
419  * @data_store: The variable store
420  * @dict: The dictionary to set
421  *
422  * If a dictionary is already associated with the data-store, then it will be
423  * destroyed.
424  **/
425 void
426 psppire_data_store_set_dictionary (PsppireDataStore *data_store, PsppireDict *dict)
427 {
428   data_store->dict = dict;
429
430   g_signal_connect (dict, "variable-inserted",
431                    G_CALLBACK (insert_variable_callback),
432                    data_store);
433
434   g_signal_connect (dict, "variables-deleted",
435                    G_CALLBACK (delete_variables_callback),
436                    data_store);
437
438   g_signal_connect (dict, "variable-changed",
439                    G_CALLBACK (variable_changed_callback),
440                    data_store);
441
442
443   g_signal_connect (dict, "dict-size-changed",
444                     G_CALLBACK (dict_size_change_callback),
445                     data_store);
446
447   /* The entire model has changed */
448   g_sheet_model_range_changed (G_SHEET_MODEL (data_store), -1, -1, -1, -1);
449
450   g_sheet_column_columns_changed (G_SHEET_COLUMN (data_store), 0, -1);
451 }
452
453 static void
454 psppire_data_store_finalize (GObject *object)
455 {
456
457   /* must chain up */
458   (* parent_class->finalize) (object);
459 }
460
461
462
463 /* Insert a blank case before POSN */
464 gboolean
465 psppire_data_store_insert_new_case (PsppireDataStore *ds, gint posn)
466 {
467   gboolean result;
468   gint val_cnt, v;
469   struct ccase cc;
470   g_return_val_if_fail (ds, FALSE);
471
472
473   /* Opportunity for optimisation exists here when creating a blank case */
474   val_cnt = datasheet_get_column_cnt (ds->case_file->datasheet) ;
475
476   case_create (&cc, val_cnt);
477
478   memset ( case_data_rw_idx (&cc, 0), 0, val_cnt * MAX_SHORT_STRING);
479
480   for (v = 0 ; v < psppire_dict_get_var_cnt (ds->dict) ; ++v)
481     {
482       const struct variable *pv = psppire_dict_get_variable (ds->dict, v);
483       if ( var_is_alpha (pv))
484         continue;
485
486       case_data_rw (&cc, pv)->f = SYSMIS;
487     }
488
489   result = psppire_case_file_insert_case (ds->case_file, &cc, posn);
490
491   case_destroy (&cc);
492
493   return result;
494 }
495
496
497 gchar *
498 psppire_data_store_get_string (PsppireDataStore *store, gint row, gint column)
499 {
500   gint idx;
501   char *text;
502   const struct fmt_spec *fp ;
503   const struct variable *pv ;
504   union value *v ;
505   GString *s;
506
507   g_return_val_if_fail (store->dict, NULL);
508   g_return_val_if_fail (store->case_file, NULL);
509
510   if (column >= psppire_dict_get_var_cnt (store->dict))
511     return NULL;
512
513   if ( row >= psppire_case_file_get_case_count (store->case_file))
514     return NULL;
515
516   pv = psppire_dict_get_variable (store->dict, column);
517
518   g_assert (pv);
519
520   idx = var_get_case_index (pv);
521
522   g_assert (idx >= 0);
523
524   v = psppire_case_file_get_value (store->case_file, row, idx, NULL,
525                                    var_get_width (pv));
526
527   g_return_val_if_fail (v, NULL);
528
529   if ( store->show_labels)
530     {
531       const gchar *label = var_lookup_value_label (pv, v);
532       if (label)
533         {
534           free (v);
535           return pspp_locale_to_utf8 (label, -1, 0);
536         }
537     }
538
539   fp = var_get_write_format (pv);
540
541   s = g_string_sized_new (fp->w + 1);
542   g_string_set_size (s, fp->w);
543
544   memset (s->str, 0, fp->w);
545
546   g_assert (fp->w == s->len);
547
548   /* Converts binary value V into printable form in the exactly
549      FP->W character in buffer S according to format specification
550      FP.  No null terminator is appended to the buffer.  */
551   data_out (v, fp, s->str);
552
553   text = pspp_locale_to_utf8 (s->str, fp->w, 0);
554   g_string_free (s, TRUE);
555
556   g_strchomp (text);
557
558   free (v);
559   return text;
560 }
561
562
563 static gboolean
564 psppire_data_store_clear_datum (GSheetModel *model,
565                                           gint row, gint col)
566
567 {
568   PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
569
570   union value v;
571   const struct variable *pv = psppire_dict_get_variable (store->dict, col);
572
573   const gint index = var_get_case_index (pv) ;
574
575   if ( var_is_numeric (pv))
576     v.f = SYSMIS;
577   else
578     memcpy (v.s, "", MAX_SHORT_STRING);
579
580   psppire_case_file_set_value (store->case_file, row, index, &v,
581                               var_get_width (pv));
582
583   return TRUE;
584 }
585
586
587 /* Attempts to update that part of the variable store which corresponds
588    to ROW, COL with  the value TEXT.
589    Returns true if anything was updated, false otherwise.
590 */
591 gboolean
592 psppire_data_store_set_string (PsppireDataStore *store,
593                                const gchar *text, gint row, gint col)
594 {
595   const struct variable *pv = psppire_dict_get_variable (store->dict, col);
596   g_return_val_if_fail (pv, FALSE);
597
598 #if 0
599   /* Allow the user to insert a lot of blank cases, simply by skipping rows */
600   for (r = psppire_case_file_get_case_count (store->case_file); r <= row ; ++r)
601     {
602
603       gint c;
604
605       psppire_case_array_insert_case (store->cases, r, 0, 0);
606
607
608       for (c = 0 ; c < psppire_dict_get_var_cnt (store->dict); ++c )
609         psppire_data_store_clear_datum (model, r, c);
610     }
611 #endif
612
613   psppire_case_file_data_in (store->case_file, row,
614                              var_get_case_index (pv), ss_cstr (text),
615                              var_get_write_format (pv));
616
617   return TRUE;
618 }
619
620
621 void
622 psppire_data_store_set_font (PsppireDataStore *store,
623                             const PangoFontDescription *fd)
624 {
625   g_return_if_fail (store);
626   g_return_if_fail (PSPPIRE_IS_DATA_STORE (store));
627
628   store->font_desc = fd;
629 #if 0
630   store->width_of_m = calc_m_width (fd);
631 #endif
632   g_signal_emit (store, signals [FONT_CHANGED], 0);
633
634
635   g_sheet_model_range_changed (G_SHEET_MODEL (store),
636                                  -1, -1, -1, -1);
637 }
638
639
640 void
641 psppire_data_store_show_labels (PsppireDataStore *store, gboolean show_labels)
642 {
643   g_return_if_fail (store);
644   g_return_if_fail (PSPPIRE_IS_DATA_STORE (store));
645
646   store->show_labels = show_labels;
647
648   g_sheet_model_range_changed (G_SHEET_MODEL (store),
649                                  -1, -1, -1, -1);
650 }
651
652
653
654 /* FIXME: There's no reason to actually have this function.
655    It should be done by a procedure */
656 void
657 psppire_data_store_create_system_file (PsppireDataStore *store,
658                               struct file_handle *handle)
659 {
660   gint i, var_cnt;
661   const struct sfm_write_options wo = {
662     true, /* writeable */
663     false, /* dont compress */
664     3 /* version */
665   };
666
667   struct casewriter *writer;
668
669   g_assert (handle);
670
671   writer = sfm_open_writer (handle, store->dict->dict, wo);
672
673   if ( ! writer)
674     return;
675
676
677   var_cnt = psppire_data_store_get_var_count (G_SHEET_MODEL (store));
678
679   for (i = 0 ; i < psppire_case_file_get_case_count (store->case_file); ++i )
680     {
681       struct ccase c;
682       psppire_case_file_get_case (store->case_file, i, &c);
683       casewriter_write (writer, &c);
684     }
685   casewriter_destroy (writer);
686 }
687
688
689
690 void
691 psppire_data_store_clear (PsppireDataStore *data_store)
692 {
693   psppire_case_file_clear (data_store->case_file);
694
695   psppire_dict_clear (data_store->dict);
696 }
697
698
699
700 /* Return a casereader made from this datastore */
701 struct casereader *
702 psppire_data_store_get_reader (PsppireDataStore *ds)
703 {
704   struct casereader *reader ;
705
706   reader = psppire_case_file_make_reader (ds->case_file);
707
708   return reader;
709 }
710
711
712
713 /* Column related funcs */
714
715 static gint
716 geometry_get_column_count (const GSheetColumn *geom)
717 {
718   PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
719
720   return MAX (MIN_COLUMNS, psppire_dict_get_var_cnt (ds->dict));
721 }
722
723
724
725 static gint
726 geometry_get_width (const GSheetColumn *geom, gint unit)
727 {
728   const struct variable *pv ;
729   PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
730
731   if ( unit >= psppire_dict_get_var_cnt (ds->dict) )
732     return ds->width_of_m * 8 ;
733
734   pv = psppire_dict_get_variable (ds->dict, unit);
735
736   if ( pv == NULL )
737     return ds->width_of_m * 8 ;
738
739   return ds->width_of_m * var_get_display_width (pv);
740 }
741
742 static void
743 geometry_set_width (GSheetColumn *geom, gint unit, gint width)
744 {
745   PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
746
747   struct variable *pv = psppire_dict_get_variable (ds->dict, unit);
748
749   var_set_display_width (pv, width / ds->width_of_m );
750 }
751
752
753
754 static GtkJustification
755 geometry_get_justification (const GSheetColumn *geom, gint unit)
756 {
757   PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
758   const struct variable *pv ;
759
760
761   if ( unit >= psppire_dict_get_var_cnt (ds->dict) )
762     return GTK_JUSTIFY_LEFT;
763
764   pv = psppire_dict_get_variable (ds->dict, unit);
765
766   return (var_get_alignment (pv) == ALIGN_LEFT ? GTK_JUSTIFY_LEFT
767           : var_get_alignment (pv) == ALIGN_RIGHT ? GTK_JUSTIFY_RIGHT
768           : GTK_JUSTIFY_CENTER);
769 }
770
771
772 static const gchar null_var_name[]=N_("var");
773
774 static gchar *
775 geometry_get_column_button_label (const GSheetColumn *geom, gint unit)
776 {
777   gchar *text;
778   struct variable *pv ;
779   PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
780
781   if ( unit >= psppire_dict_get_var_cnt (ds->dict) )
782     return g_locale_to_utf8 (null_var_name, -1, 0, 0, 0);
783
784   pv = psppire_dict_get_variable (ds->dict, unit);
785
786   text =  pspp_locale_to_utf8 (var_get_name (pv), -1, 0);
787
788   return text;
789 }
790
791
792 static gboolean
793 geometry_get_sensitivity (const GSheetColumn *geom, gint unit)
794 {
795   PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
796
797   return (unit < psppire_dict_get_var_cnt (ds->dict));
798 }
799
800
801 static void
802 psppire_data_store_sheet_column_init (GSheetColumnIface *iface)
803 {
804   iface->get_column_count = geometry_get_column_count;
805   iface->get_width = geometry_get_width;
806   iface->set_width = geometry_set_width;
807   iface->get_visibility = always_true;
808   iface->get_sensitivity = geometry_get_sensitivity;
809   iface->get_justification = geometry_get_justification;
810   iface->get_button_label = geometry_get_column_button_label;
811 }
812
813
814 /* Row related funcs */
815
816 static gint
817 geometry_get_row_count (const GSheetRow *geom, gpointer data)
818 {
819   PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
820
821   return TRAILING_ROWS + psppire_case_file_get_case_count (ds->case_file);
822 }
823
824
825 static gint
826 geometry_get_height (const GSheetRow *geom, gint unit, gpointer data)
827 {
828   return 25;
829 }
830
831
832 static gboolean
833 geometry_get_row_sensitivity (const GSheetRow *geom, gint unit, gpointer data)
834 {
835   PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
836
837
838   return (unit < psppire_case_file_get_case_count (ds->case_file));
839 }
840
841
842 static gchar *
843 geometry_get_row_button_label (const GSheetRow *geom, gint unit, gpointer data)
844 {
845   gchar *text;
846   gchar *s;
847   PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
848
849   if ( unit >
850        TRAILING_ROWS + psppire_case_file_get_case_count (ds->case_file))
851     return 0;
852
853   s = g_strdup_printf (_("%d"), unit);
854
855   text =  pspp_locale_to_utf8 (s, -1, 0);
856
857   g_free (s);
858
859   return text;
860 }
861
862
863 static void
864 psppire_data_store_sheet_row_init (GSheetRowIface *iface)
865 {
866   iface->get_row_count = geometry_get_row_count;
867
868   iface->get_height = geometry_get_height;
869   iface->set_height = 0;
870   iface->get_visibility = always_true;
871   iface->get_sensitivity = geometry_get_row_sensitivity;
872
873   iface->get_button_label = geometry_get_row_button_label;
874 }
875
876
877