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