1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2006, 2008, 2009 Free Software Foundation
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.
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.
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/>. */
21 #define _(msgid) gettext (msgid)
22 #define N_(msgid) msgid
24 #include <data/datasheet.h>
25 #include <data/data-out.h>
26 #include <data/variable.h>
28 #include <ui/gui/sheet/psppire-sheetmodel.h>
29 #include <ui/gui/psppire-marshal.h>
31 #include <pango/pango-context.h>
33 #include "psppire-data-store.h"
36 #include <data/dictionary.h>
37 #include <data/missing-values.h>
38 #include <data/value-labels.h>
39 #include <data/data-in.h>
40 #include <data/format.h>
42 #include <math/sort.h>
49 static void psppire_data_store_init (PsppireDataStore *data_store);
50 static void psppire_data_store_class_init (PsppireDataStoreClass *class);
51 static void psppire_data_store_sheet_model_init (PsppireSheetModelIface *iface);
53 static void psppire_data_store_finalize (GObject *object);
54 static void psppire_data_store_dispose (GObject *object);
56 static gboolean psppire_data_store_clear_datum (PsppireSheetModel *model,
57 glong row, glong column);
60 static gboolean psppire_data_store_insert_case (PsppireDataStore *ds,
65 static gboolean psppire_data_store_data_in (PsppireDataStore *ds,
66 casenumber casenum, gint idx,
67 struct substring input,
68 const struct fmt_spec *fmt);
72 static GObjectClass *parent_class = NULL;
84 static guint signals [n_SIGNALS];
88 psppire_data_store_get_type (void)
90 static GType data_store_type = 0;
94 static const GTypeInfo data_store_info =
96 sizeof (PsppireDataStoreClass),
98 NULL, /* base_finalize */
99 (GClassInitFunc) psppire_data_store_class_init,
100 NULL, /* class_finalize */
101 NULL, /* class_data */
102 sizeof (PsppireDataStore),
104 (GInstanceInitFunc) psppire_data_store_init,
107 static const GInterfaceInfo sheet_model_info =
109 (GInterfaceInitFunc) psppire_data_store_sheet_model_init,
115 data_store_type = g_type_register_static (G_TYPE_OBJECT,
117 &data_store_info, 0);
119 g_type_add_interface_static (data_store_type,
120 PSPPIRE_TYPE_SHEET_MODEL,
125 return data_store_type;
130 psppire_data_store_class_init (PsppireDataStoreClass *class)
132 GObjectClass *object_class;
134 parent_class = g_type_class_peek_parent (class);
135 object_class = (GObjectClass*) class;
137 object_class->finalize = psppire_data_store_finalize;
138 object_class->dispose = psppire_data_store_dispose;
140 signals [BACKEND_CHANGED] =
141 g_signal_new ("backend-changed",
142 G_TYPE_FROM_CLASS (class),
146 g_cclosure_marshal_VOID__VOID,
150 signals [CASE_INSERTED] =
151 g_signal_new ("case-inserted",
152 G_TYPE_FROM_CLASS (class),
156 g_cclosure_marshal_VOID__INT,
162 signals [CASE_CHANGED] =
163 g_signal_new ("case-changed",
164 G_TYPE_FROM_CLASS (class),
168 g_cclosure_marshal_VOID__INT,
173 signals [CASES_DELETED] =
174 g_signal_new ("cases-deleted",
175 G_TYPE_FROM_CLASS (class),
179 psppire_marshal_VOID__INT_INT,
189 psppire_data_store_insert_values (PsppireDataStore *ds,
190 gint n_values, gint where);
193 psppire_data_store_get_value (const PsppireDataStore *ds,
194 casenumber casenum, size_t idx,
195 union value *value, int width);
199 psppire_data_store_set_value (PsppireDataStore *ds, casenumber casenum,
200 gint idx, union value *v, gint width);
206 psppire_data_store_get_var_count (const PsppireSheetModel *model)
208 const PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
210 return psppire_dict_get_var_cnt (store->dict);
214 psppire_data_store_get_case_count (const PsppireDataStore *store)
216 return datasheet_get_row_cnt (store->datasheet);
220 psppire_data_store_get_value_count (const PsppireDataStore *store)
222 return psppire_dict_get_value_cnt (store->dict);
226 psppire_data_store_get_case_count_wrapper (const PsppireSheetModel *model)
228 const PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
229 return psppire_data_store_get_case_count (store);
233 psppire_data_store_init (PsppireDataStore *data_store)
235 data_store->dict = 0;
236 data_store->datasheet = NULL;
237 data_store->dispose_has_run = FALSE;
240 static inline gchar *
241 psppire_data_store_get_string_wrapper (const PsppireSheetModel *model, glong row,
244 return psppire_data_store_get_string (PSPPIRE_DATA_STORE (model), row, column);
248 static inline gboolean
249 psppire_data_store_set_string_wrapper (PsppireSheetModel *model,
251 glong row, glong column)
253 return psppire_data_store_set_string (PSPPIRE_DATA_STORE (model), text,
259 static gchar * get_column_subtitle (const PsppireSheetModel *model, gint col);
260 static gchar * get_column_button_label (const PsppireSheetModel *model, gint col);
261 static gboolean get_column_sensitivity (const PsppireSheetModel *model, gint col);
262 static GtkJustification get_column_justification (const PsppireSheetModel *model, gint col);
264 static gchar * get_row_button_label (const PsppireSheetModel *model, gint row);
265 static gboolean get_row_sensitivity (const PsppireSheetModel *model, gint row);
269 psppire_data_store_sheet_model_init (PsppireSheetModelIface *iface)
271 iface->free_strings = TRUE;
272 iface->get_string = psppire_data_store_get_string_wrapper;
273 iface->set_string = psppire_data_store_set_string_wrapper;
274 iface->clear_datum = psppire_data_store_clear_datum;
275 iface->is_editable = NULL;
276 iface->get_foreground = NULL;
277 iface->get_background = NULL;
278 iface->get_column_count = psppire_data_store_get_var_count;
279 iface->get_row_count = psppire_data_store_get_case_count_wrapper;
281 iface->get_column_subtitle = get_column_subtitle;
282 iface->get_column_title = get_column_button_label;
283 iface->get_column_sensitivity = get_column_sensitivity;
284 iface->get_column_justification = get_column_justification;
286 iface->get_row_title = get_row_button_label;
287 iface->get_row_sensitivity = get_row_sensitivity;
292 A callback which occurs after a variable has been deleted.
295 delete_variable_callback (GObject *obj, gint dict_index,
296 gint case_index, gint val_cnt,
299 PsppireDataStore *store = PSPPIRE_DATA_STORE (data);
302 psppire_sheet_model_columns_deleted (PSPPIRE_SHEET_MODEL (store), dict_index, 1);
306 psppire_sheet_column_columns_changed (PSPPIRE_SHEET_COLUMN (store),
312 variable_changed_callback (GObject *obj, gint var_num, gpointer data)
315 PsppireDataStore *store = PSPPIRE_DATA_STORE (data);
317 psppire_sheet_column_columns_changed (PSPPIRE_SHEET_COLUMN (store),
320 psppire_sheet_model_range_changed (PSPPIRE_SHEET_MODEL (store),
327 insert_variable_callback (GObject *obj, gint var_num, gpointer data)
329 PsppireDataStore *store;
332 g_return_if_fail (data);
334 store = PSPPIRE_DATA_STORE (data);
338 struct variable *variable =
339 psppire_dict_get_variable (store->dict, var_num);
341 g_assert (variable != NULL);
343 posn = var_get_case_index (variable);
350 psppire_data_store_insert_values (store, 1, posn);
354 psppire_sheet_column_columns_changed (PSPPIRE_SHEET_COLUMN (store),
358 psppire_sheet_model_columns_inserted (PSPPIRE_SHEET_MODEL (store), var_num, 1);
363 dict_size_change_callback (GObject *obj,
364 gint posn, gint adjustment, gpointer data)
366 PsppireDataStore *store = PSPPIRE_DATA_STORE (data);
368 const struct variable *v = psppire_dict_get_variable (store->dict, posn);
370 const gint new_val_width = value_cnt_from_width (var_get_width (v));
372 if ( adjustment > 0 )
373 psppire_data_store_insert_values (store, adjustment,
374 new_val_width - adjustment +
375 var_get_case_index(v));
381 * psppire_data_store_new:
382 * @dict: The dictionary for this data_store.
385 * Return value: a new #PsppireDataStore
388 psppire_data_store_new (PsppireDict *dict)
390 PsppireDataStore *retval;
392 retval = g_object_new (GTK_TYPE_DATA_STORE, NULL);
394 psppire_data_store_set_dictionary (retval, dict);
400 psppire_data_store_set_reader (PsppireDataStore *ds,
401 struct casereader *reader)
406 datasheet_destroy (ds->datasheet);
408 ds->datasheet = datasheet_create (reader);
410 psppire_sheet_model_range_changed (PSPPIRE_SHEET_MODEL (ds),
414 for (i = 0 ; i < n_dict_signals; ++i )
416 if ( ds->dict_handler_id [i] > 0)
418 g_signal_handler_unblock (ds->dict,
419 ds->dict_handler_id[i]);
423 g_signal_emit (ds, signals[BACKEND_CHANGED], 0);
428 * psppire_data_store_replace_set_dictionary:
429 * @data_store: The variable store
430 * @dict: The dictionary to set
432 * If a dictionary is already associated with the data-store, then it will be
436 psppire_data_store_set_dictionary (PsppireDataStore *data_store, PsppireDict *dict)
440 /* Disconnect any existing handlers */
441 if ( data_store->dict )
442 for (i = 0 ; i < n_dict_signals; ++i )
444 g_signal_handler_disconnect (data_store->dict,
445 data_store->dict_handler_id[i]);
448 data_store->dict = dict;
453 data_store->dict_handler_id [VARIABLE_INSERTED] =
454 g_signal_connect (dict, "variable-inserted",
455 G_CALLBACK (insert_variable_callback),
458 data_store->dict_handler_id [VARIABLE_DELETED] =
459 g_signal_connect (dict, "variable-deleted",
460 G_CALLBACK (delete_variable_callback),
463 data_store->dict_handler_id [VARIABLE_CHANGED] =
464 g_signal_connect (dict, "variable-changed",
465 G_CALLBACK (variable_changed_callback),
468 data_store->dict_handler_id [SIZE_CHANGED] =
469 g_signal_connect (dict, "dict-size-changed",
470 G_CALLBACK (dict_size_change_callback),
476 /* The entire model has changed */
477 psppire_sheet_model_range_changed (PSPPIRE_SHEET_MODEL (data_store), -1, -1, -1, -1);
480 psppire_sheet_column_columns_changed (PSPPIRE_SHEET_COLUMN (data_store), 0, -1);
483 if ( data_store->dict )
484 for (i = 0 ; i < n_dict_signals; ++i )
486 if ( data_store->dict_handler_id [i] > 0)
488 g_signal_handler_block (data_store->dict,
489 data_store->dict_handler_id[i]);
495 psppire_data_store_finalize (GObject *object)
499 (* parent_class->finalize) (object);
504 psppire_data_store_dispose (GObject *object)
506 PsppireDataStore *ds = PSPPIRE_DATA_STORE (object);
508 if (ds->dispose_has_run)
513 datasheet_destroy (ds->datasheet);
514 ds->datasheet = NULL;
518 (* parent_class->dispose) (object);
520 ds->dispose_has_run = TRUE;
525 /* Insert a blank case before POSN */
527 psppire_data_store_insert_new_case (PsppireDataStore *ds, casenumber posn)
532 g_return_val_if_fail (ds, FALSE);
534 val_cnt = datasheet_get_column_cnt (ds->datasheet) ;
536 g_return_val_if_fail (val_cnt > 0, FALSE);
538 g_return_val_if_fail (posn <= psppire_data_store_get_case_count (ds), FALSE);
540 cc = case_create (val_cnt);
542 memset ( case_data_rw_idx (cc, 0), 0, val_cnt * MAX_SHORT_STRING);
544 for (v = 0 ; v < psppire_dict_get_var_cnt (ds->dict) ; ++v)
546 const struct variable *pv = psppire_dict_get_variable (ds->dict, v);
547 if ( var_is_alpha (pv))
550 case_data_rw (cc, pv)->f = SYSMIS;
553 result = psppire_data_store_insert_case (ds, cc, posn);
562 psppire_data_store_get_string (PsppireDataStore *store, glong row, glong column)
566 const struct fmt_spec *fp ;
567 const struct variable *pv ;
571 g_return_val_if_fail (store->dict, NULL);
572 g_return_val_if_fail (store->datasheet, NULL);
574 if (column >= psppire_dict_get_var_cnt (store->dict))
577 if ( row >= psppire_data_store_get_case_count (store))
580 pv = psppire_dict_get_variable (store->dict, column);
584 idx = var_get_case_index (pv);
588 v = psppire_data_store_get_value (store, row, idx, NULL,
591 g_return_val_if_fail (v, NULL);
593 if ( store->show_labels)
595 const gchar *label = var_lookup_value_label (pv, v);
599 return pspp_locale_to_utf8 (label, -1, 0);
603 fp = var_get_write_format (pv);
605 s = g_string_sized_new (fp->w + 1);
606 g_string_set_size (s, fp->w);
608 memset (s->str, 0, fp->w);
610 g_assert (fp->w == s->len);
612 /* Converts binary value V into printable form in the exactly
613 FP->W character in buffer S according to format specification
614 FP. No null terminator is appended to the buffer. */
615 data_out (v, fp, s->str);
617 text = pspp_locale_to_utf8 (s->str, fp->w, 0);
618 g_string_free (s, TRUE);
628 psppire_data_store_clear_datum (PsppireSheetModel *model,
629 glong row, glong col)
631 PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
634 const struct variable *pv = psppire_dict_get_variable (store->dict, col);
636 const gint index = var_get_case_index (pv) ;
638 if ( var_is_numeric (pv))
641 memcpy (v.s, "", MAX_SHORT_STRING);
643 psppire_data_store_set_value (store, row, index, &v,
646 psppire_sheet_model_range_changed (model, row, col, row, col);
652 /* Attempts to update that part of the variable store which corresponds
653 to ROW, COL with the value TEXT.
654 Returns true if anything was updated, false otherwise.
657 psppire_data_store_set_string (PsppireDataStore *store,
658 const gchar *text, glong row, glong col)
661 const struct variable *pv = psppire_dict_get_variable (store->dict, col);
665 n_cases = psppire_data_store_get_case_count (store);
671 psppire_data_store_insert_new_case (store, row);
673 psppire_data_store_data_in (store, row,
674 var_get_case_index (pv), ss_cstr (text),
675 var_get_write_format (pv));
677 psppire_sheet_model_range_changed (PSPPIRE_SHEET_MODEL (store), row, col, row, col);
685 psppire_data_store_show_labels (PsppireDataStore *store, gboolean show_labels)
687 g_return_if_fail (store);
688 g_return_if_fail (PSPPIRE_IS_DATA_STORE (store));
690 store->show_labels = show_labels;
692 psppire_sheet_model_range_changed (PSPPIRE_SHEET_MODEL (store),
698 psppire_data_store_clear (PsppireDataStore *ds)
700 datasheet_destroy (ds->datasheet);
701 ds->datasheet = NULL;
703 psppire_dict_clear (ds->dict);
705 g_signal_emit (ds, signals [CASES_DELETED], 0, 0, -1);
710 /* Return a casereader made from this datastore */
712 psppire_data_store_get_reader (PsppireDataStore *ds)
715 struct casereader *reader ;
718 for (i = 0 ; i < n_dict_signals; ++i )
720 g_signal_handler_block (ds->dict,
721 ds->dict_handler_id[i]);
724 reader = datasheet_make_reader (ds->datasheet);
726 /* We must not reference this again */
727 ds->datasheet = NULL;
734 /* Column related funcs */
737 static const gchar null_var_name[]=N_("var");
741 /* Row related funcs */
744 get_row_button_label (const PsppireSheetModel *model, gint unit)
746 gchar *s = g_strdup_printf (_("%d"), unit + FIRST_CASE_NUMBER);
748 gchar *text = pspp_locale_to_utf8 (s, -1, 0);
757 get_row_sensitivity (const PsppireSheetModel *model, gint unit)
759 PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
761 return (unit < psppire_data_store_get_case_count (ds));
767 /* Column related stuff */
770 get_column_subtitle (const PsppireSheetModel *model, gint col)
773 const struct variable *v ;
774 PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
776 if ( col >= psppire_dict_get_var_cnt (ds->dict) )
779 v = psppire_dict_get_variable (ds->dict, col);
781 if ( ! var_has_label (v))
784 text = pspp_locale_to_utf8 (var_get_label (v), -1, 0);
790 get_column_button_label (const PsppireSheetModel *model, gint col)
793 struct variable *pv ;
794 PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
796 if ( col >= psppire_dict_get_var_cnt (ds->dict) )
797 return g_locale_to_utf8 (null_var_name, -1, 0, 0, 0);
799 pv = psppire_dict_get_variable (ds->dict, col);
801 text = pspp_locale_to_utf8 (var_get_name (pv), -1, 0);
807 get_column_sensitivity (const PsppireSheetModel *model, gint col)
809 PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
811 return (col < psppire_dict_get_var_cnt (ds->dict));
816 static GtkJustification
817 get_column_justification (const PsppireSheetModel *model, gint col)
819 PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
820 const struct variable *pv ;
822 if ( col >= psppire_dict_get_var_cnt (ds->dict) )
823 return GTK_JUSTIFY_LEFT;
825 pv = psppire_dict_get_variable (ds->dict, col);
827 return (var_get_alignment (pv) == ALIGN_LEFT ? GTK_JUSTIFY_LEFT
828 : var_get_alignment (pv) == ALIGN_RIGHT ? GTK_JUSTIFY_RIGHT
829 : GTK_JUSTIFY_CENTER);
837 /* Returns the CASENUMth case, or a null pointer on failure.
840 psppire_data_store_get_case (const PsppireDataStore *ds,
843 g_return_val_if_fail (ds, FALSE);
844 g_return_val_if_fail (ds->datasheet, FALSE);
846 return datasheet_get_row (ds->datasheet, casenum);
851 psppire_data_store_delete_cases (PsppireDataStore *ds, casenumber first,
854 g_return_val_if_fail (ds, FALSE);
855 g_return_val_if_fail (ds->datasheet, FALSE);
857 g_return_val_if_fail (first + n_cases <=
858 psppire_data_store_get_case_count (ds), FALSE);
861 datasheet_delete_rows (ds->datasheet, first, n_cases);
863 g_signal_emit (ds, signals [CASES_DELETED], 0, first, n_cases);
864 psppire_sheet_model_rows_deleted (PSPPIRE_SHEET_MODEL (ds), first, n_cases);
871 /* Insert case CC into the case file before POSN */
873 psppire_data_store_insert_case (PsppireDataStore *ds,
879 g_return_val_if_fail (ds, FALSE);
880 g_return_val_if_fail (ds->datasheet, FALSE);
883 result = datasheet_insert_rows (ds->datasheet, posn, &cc, 1);
887 g_signal_emit (ds, signals [CASE_INSERTED], 0, posn);
888 psppire_sheet_model_rows_inserted (PSPPIRE_SHEET_MODEL (ds), posn, 1);
891 g_warning ("Cannot insert case at position %ld\n", posn);
897 /* Copies the IDXth value from case CASENUM into VALUE.
898 If VALUE is null, then memory is allocated is allocated with
899 malloc. Returns the value if successful, NULL on failure. */
901 psppire_data_store_get_value (const PsppireDataStore *ds,
902 casenumber casenum, size_t idx,
903 union value *value, int width)
907 g_return_val_if_fail (ds, false);
908 g_return_val_if_fail (ds->datasheet, false);
910 g_return_val_if_fail (idx < datasheet_get_column_cnt (ds->datasheet), false);
914 value = xnmalloc (value_cnt_from_width (width), sizeof *value);
919 if (!datasheet_get_value (ds->datasheet, casenum, idx, value, width))
930 /* Set the IDXth value of case C to V.
931 Returns true if successful, false on I/O error. */
933 psppire_data_store_set_value (PsppireDataStore *ds, casenumber casenum,
934 gint idx, union value *v, gint width)
938 g_return_val_if_fail (ds, FALSE);
939 g_return_val_if_fail (ds->datasheet, FALSE);
941 g_return_val_if_fail (idx < datasheet_get_column_cnt (ds->datasheet), FALSE);
943 ok = datasheet_put_value (ds->datasheet, casenum, idx, v, width);
945 g_signal_emit (ds, signals [CASE_CHANGED], 0, casenum);
953 /* Set the IDXth value of case C using D_IN */
955 psppire_data_store_data_in (PsppireDataStore *ds, casenumber casenum, gint idx,
956 struct substring input, const struct fmt_spec *fmt)
958 union value *value = NULL;
962 g_return_val_if_fail (ds, FALSE);
963 g_return_val_if_fail (ds->datasheet, FALSE);
965 g_return_val_if_fail (idx < datasheet_get_column_cnt (ds->datasheet), FALSE);
967 width = fmt_var_width (fmt);
968 value = xmalloca (value_cnt_from_width (width) * sizeof *value);
969 ok = (datasheet_get_value (ds->datasheet, casenum, idx, value, width)
970 && data_in (input, LEGACY_NATIVE, fmt->type, 0, 0, 0, value, width)
971 && datasheet_put_value (ds->datasheet, casenum, idx, value, width));
976 g_signal_emit (ds, signals [CASE_CHANGED], 0, casenum);
981 /* Resize the cases in the casefile, by inserting N_VALUES into every
982 one of them at the position immediately preceeding WHERE.
985 psppire_data_store_insert_values (PsppireDataStore *ds,
986 gint n_values, gint where)
988 g_return_val_if_fail (ds, FALSE);
993 g_assert (n_values > 0);
995 if ( ! ds->datasheet )
996 ds->datasheet = datasheet_create (NULL);
999 union value *values = xcalloc (n_values, sizeof *values);
1000 datasheet_insert_columns (ds->datasheet, values, n_values, where);