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