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