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