1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2006, 2008 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),
314 variable_changed_callback (GObject *obj, gint var_num, gpointer data)
316 PsppireDataStore *store = PSPPIRE_DATA_STORE (data);
319 psppire_sheet_column_columns_changed (PSPPIRE_SHEET_COLUMN (store),
323 psppire_sheet_model_range_changed (PSPPIRE_SHEET_MODEL (store),
330 insert_variable_callback (GObject *obj, gint var_num, gpointer data)
332 PsppireDataStore *store;
335 g_return_if_fail (data);
337 store = PSPPIRE_DATA_STORE (data);
341 struct variable *variable =
342 psppire_dict_get_variable (store->dict, var_num);
344 g_assert (variable != NULL);
346 posn = var_get_case_index (variable);
353 psppire_data_store_insert_values (store, 1, posn);
357 psppire_sheet_column_columns_changed (PSPPIRE_SHEET_COLUMN (store),
361 psppire_sheet_model_columns_inserted (PSPPIRE_SHEET_MODEL (store), var_num, 1);
366 dict_size_change_callback (GObject *obj,
367 gint posn, gint adjustment, gpointer data)
369 PsppireDataStore *store = PSPPIRE_DATA_STORE (data);
371 const struct variable *v = psppire_dict_get_variable (store->dict, posn);
373 const gint new_val_width = value_cnt_from_width (var_get_width (v));
375 if ( adjustment > 0 )
376 psppire_data_store_insert_values (store, adjustment,
377 new_val_width - adjustment +
378 var_get_case_index(v));
384 * psppire_data_store_new:
385 * @dict: The dictionary for this data_store.
388 * Return value: a new #PsppireDataStore
391 psppire_data_store_new (PsppireDict *dict)
393 PsppireDataStore *retval;
395 retval = g_object_new (GTK_TYPE_DATA_STORE, NULL);
397 psppire_data_store_set_dictionary (retval, dict);
403 psppire_data_store_set_reader (PsppireDataStore *ds,
404 struct casereader *reader)
409 datasheet_destroy (ds->datasheet);
411 ds->datasheet = datasheet_create (reader);
413 psppire_sheet_model_range_changed (PSPPIRE_SHEET_MODEL (ds),
417 for (i = 0 ; i < n_dict_signals; ++i )
419 if ( ds->dict_handler_id [i] > 0)
421 g_signal_handler_unblock (ds->dict,
422 ds->dict_handler_id[i]);
426 g_signal_emit (ds, signals[BACKEND_CHANGED], 0);
431 * psppire_data_store_replace_set_dictionary:
432 * @data_store: The variable store
433 * @dict: The dictionary to set
435 * If a dictionary is already associated with the data-store, then it will be
439 psppire_data_store_set_dictionary (PsppireDataStore *data_store, PsppireDict *dict)
443 /* Disconnect any existing handlers */
444 if ( data_store->dict )
445 for (i = 0 ; i < n_dict_signals; ++i )
447 g_signal_handler_disconnect (data_store->dict,
448 data_store->dict_handler_id[i]);
451 data_store->dict = dict;
456 data_store->dict_handler_id [VARIABLE_INSERTED] =
457 g_signal_connect (dict, "variable-inserted",
458 G_CALLBACK (insert_variable_callback),
461 data_store->dict_handler_id [VARIABLE_DELETED] =
462 g_signal_connect (dict, "variable-deleted",
463 G_CALLBACK (delete_variable_callback),
466 data_store->dict_handler_id [VARIABLE_CHANGED] =
467 g_signal_connect (dict, "variable-changed",
468 G_CALLBACK (variable_changed_callback),
471 data_store->dict_handler_id [SIZE_CHANGED] =
472 g_signal_connect (dict, "dict-size-changed",
473 G_CALLBACK (dict_size_change_callback),
479 /* The entire model has changed */
480 psppire_sheet_model_range_changed (PSPPIRE_SHEET_MODEL (data_store), -1, -1, -1, -1);
483 psppire_sheet_column_columns_changed (PSPPIRE_SHEET_COLUMN (data_store), 0, -1);
486 if ( data_store->dict )
487 for (i = 0 ; i < n_dict_signals; ++i )
489 if ( data_store->dict_handler_id [i] > 0)
491 g_signal_handler_block (data_store->dict,
492 data_store->dict_handler_id[i]);
498 psppire_data_store_finalize (GObject *object)
502 (* parent_class->finalize) (object);
507 psppire_data_store_dispose (GObject *object)
509 PsppireDataStore *ds = PSPPIRE_DATA_STORE (object);
511 if (ds->dispose_has_run)
516 datasheet_destroy (ds->datasheet);
517 ds->datasheet = NULL;
521 (* parent_class->dispose) (object);
523 ds->dispose_has_run = TRUE;
528 /* Insert a blank case before POSN */
530 psppire_data_store_insert_new_case (PsppireDataStore *ds, casenumber posn)
535 g_return_val_if_fail (ds, FALSE);
537 val_cnt = datasheet_get_column_cnt (ds->datasheet) ;
539 g_return_val_if_fail (val_cnt > 0, FALSE);
541 g_return_val_if_fail (posn <= psppire_data_store_get_case_count (ds), FALSE);
543 case_create (&cc, val_cnt);
545 memset ( case_data_rw_idx (&cc, 0), 0, val_cnt * MAX_SHORT_STRING);
547 for (v = 0 ; v < psppire_dict_get_var_cnt (ds->dict) ; ++v)
549 const struct variable *pv = psppire_dict_get_variable (ds->dict, v);
550 if ( var_is_alpha (pv))
553 case_data_rw (&cc, pv)->f = SYSMIS;
556 result = psppire_data_store_insert_case (ds, &cc, posn);
565 psppire_data_store_get_string (PsppireDataStore *store, glong row, glong column)
569 const struct fmt_spec *fp ;
570 const struct variable *pv ;
574 g_return_val_if_fail (store->dict, NULL);
575 g_return_val_if_fail (store->datasheet, NULL);
577 if (column >= psppire_dict_get_var_cnt (store->dict))
580 if ( row >= psppire_data_store_get_case_count (store))
583 pv = psppire_dict_get_variable (store->dict, column);
587 idx = var_get_case_index (pv);
591 v = psppire_data_store_get_value (store, row, idx, NULL,
594 g_return_val_if_fail (v, NULL);
596 if ( store->show_labels)
598 const gchar *label = var_lookup_value_label (pv, v);
602 return pspp_locale_to_utf8 (label, -1, 0);
606 fp = var_get_write_format (pv);
608 s = g_string_sized_new (fp->w + 1);
609 g_string_set_size (s, fp->w);
611 memset (s->str, 0, fp->w);
613 g_assert (fp->w == s->len);
615 /* Converts binary value V into printable form in the exactly
616 FP->W character in buffer S according to format specification
617 FP. No null terminator is appended to the buffer. */
618 data_out (v, fp, s->str);
620 text = pspp_locale_to_utf8 (s->str, fp->w, 0);
621 g_string_free (s, TRUE);
631 psppire_data_store_clear_datum (PsppireSheetModel *model,
632 glong row, glong col)
634 PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
637 const struct variable *pv = psppire_dict_get_variable (store->dict, col);
639 const gint index = var_get_case_index (pv) ;
641 if ( var_is_numeric (pv))
644 memcpy (v.s, "", MAX_SHORT_STRING);
646 psppire_data_store_set_value (store, row, index, &v,
649 psppire_sheet_model_range_changed (model, row, col, row, col);
655 /* Attempts to update that part of the variable store which corresponds
656 to ROW, COL with the value TEXT.
657 Returns true if anything was updated, false otherwise.
660 psppire_data_store_set_string (PsppireDataStore *store,
661 const gchar *text, glong row, glong col)
664 const struct variable *pv = psppire_dict_get_variable (store->dict, col);
668 n_cases = psppire_data_store_get_case_count (store);
674 psppire_data_store_insert_new_case (store, row);
676 psppire_data_store_data_in (store, row,
677 var_get_case_index (pv), ss_cstr (text),
678 var_get_write_format (pv));
680 psppire_sheet_model_range_changed (PSPPIRE_SHEET_MODEL (store), row, col, row, col);
688 psppire_data_store_show_labels (PsppireDataStore *store, gboolean show_labels)
690 g_return_if_fail (store);
691 g_return_if_fail (PSPPIRE_IS_DATA_STORE (store));
693 store->show_labels = show_labels;
695 psppire_sheet_model_range_changed (PSPPIRE_SHEET_MODEL (store),
701 psppire_data_store_clear (PsppireDataStore *ds)
703 datasheet_destroy (ds->datasheet);
704 ds->datasheet = NULL;
706 psppire_dict_clear (ds->dict);
708 g_signal_emit (ds, signals [CASES_DELETED], 0, 0, -1);
713 /* Return a casereader made from this datastore */
715 psppire_data_store_get_reader (PsppireDataStore *ds)
718 struct casereader *reader ;
721 for (i = 0 ; i < n_dict_signals; ++i )
723 g_signal_handler_block (ds->dict,
724 ds->dict_handler_id[i]);
727 reader = datasheet_make_reader (ds->datasheet);
729 /* We must not reference this again */
730 ds->datasheet = NULL;
737 /* Column related funcs */
740 static const gchar null_var_name[]=N_("var");
744 /* Row related funcs */
747 get_row_button_label (const PsppireSheetModel *model, gint unit)
749 gchar *s = g_strdup_printf (_("%d"), unit + FIRST_CASE_NUMBER);
751 gchar *text = pspp_locale_to_utf8 (s, -1, 0);
760 get_row_sensitivity (const PsppireSheetModel *model, gint unit)
762 PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
764 return (unit < psppire_data_store_get_case_count (ds));
770 /* Column related stuff */
773 get_column_subtitle (const PsppireSheetModel *model, gint col)
776 const struct variable *v ;
777 PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
779 if ( col >= psppire_dict_get_var_cnt (ds->dict) )
782 v = psppire_dict_get_variable (ds->dict, col);
784 if ( ! var_has_label (v))
787 text = pspp_locale_to_utf8 (var_get_label (v), -1, 0);
793 get_column_button_label (const PsppireSheetModel *model, gint col)
796 struct variable *pv ;
797 PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
799 if ( col >= psppire_dict_get_var_cnt (ds->dict) )
800 return g_locale_to_utf8 (null_var_name, -1, 0, 0, 0);
802 pv = psppire_dict_get_variable (ds->dict, col);
804 text = pspp_locale_to_utf8 (var_get_name (pv), -1, 0);
810 get_column_sensitivity (const PsppireSheetModel *model, gint col)
812 PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
814 return (col < psppire_dict_get_var_cnt (ds->dict));
819 static GtkJustification
820 get_column_justification (const PsppireSheetModel *model, gint col)
822 PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
823 const struct variable *pv ;
825 if ( col >= psppire_dict_get_var_cnt (ds->dict) )
826 return GTK_JUSTIFY_LEFT;
828 pv = psppire_dict_get_variable (ds->dict, col);
830 return (var_get_alignment (pv) == ALIGN_LEFT ? GTK_JUSTIFY_LEFT
831 : var_get_alignment (pv) == ALIGN_RIGHT ? GTK_JUSTIFY_RIGHT
832 : GTK_JUSTIFY_CENTER);
840 /* Fills C with the CASENUMth case.
841 Returns true on success, false otherwise.
844 psppire_data_store_get_case (const PsppireDataStore *ds,
848 g_return_val_if_fail (ds, FALSE);
849 g_return_val_if_fail (ds->datasheet, FALSE);
851 return datasheet_get_row (ds->datasheet, casenum, c);
856 psppire_data_store_delete_cases (PsppireDataStore *ds, casenumber first,
859 g_return_val_if_fail (ds, FALSE);
860 g_return_val_if_fail (ds->datasheet, FALSE);
862 g_return_val_if_fail (first + n_cases <=
863 psppire_data_store_get_case_count (ds), FALSE);
866 datasheet_delete_rows (ds->datasheet, first, n_cases);
868 g_signal_emit (ds, signals [CASES_DELETED], 0, first, n_cases);
869 psppire_sheet_model_rows_deleted (PSPPIRE_SHEET_MODEL (ds), first, n_cases);
876 /* Insert case CC into the case file before POSN */
878 psppire_data_store_insert_case (PsppireDataStore *ds,
885 g_return_val_if_fail (ds, FALSE);
886 g_return_val_if_fail (ds->datasheet, FALSE);
888 case_clone (&tmp, cc);
889 result = datasheet_insert_rows (ds->datasheet, posn, &tmp, 1);
893 g_signal_emit (ds, signals [CASE_INSERTED], 0, posn);
894 psppire_sheet_model_rows_inserted (PSPPIRE_SHEET_MODEL (ds), posn, 1);
897 g_warning ("Cannot insert case at position %ld\n", posn);
903 /* Copies the IDXth value from case CASENUM into VALUE.
904 If VALUE is null, then memory is allocated is allocated with
905 malloc. Returns the value if successful, NULL on failure. */
907 psppire_data_store_get_value (const PsppireDataStore *ds,
908 casenumber casenum, size_t idx,
909 union value *value, int width)
913 g_return_val_if_fail (ds, false);
914 g_return_val_if_fail (ds->datasheet, false);
916 g_return_val_if_fail (idx < datasheet_get_column_cnt (ds->datasheet), false);
920 value = xnmalloc (value_cnt_from_width (width), sizeof *value);
925 if (!datasheet_get_value (ds->datasheet, casenum, idx, value, width))
936 /* Set the IDXth value of case C to V.
937 Returns true if successful, false on I/O error. */
939 psppire_data_store_set_value (PsppireDataStore *ds, casenumber casenum,
940 gint idx, union value *v, gint width)
944 g_return_val_if_fail (ds, FALSE);
945 g_return_val_if_fail (ds->datasheet, FALSE);
947 g_return_val_if_fail (idx < datasheet_get_column_cnt (ds->datasheet), FALSE);
949 ok = datasheet_put_value (ds->datasheet, casenum, idx, v, width);
951 g_signal_emit (ds, signals [CASE_CHANGED], 0, casenum);
959 /* Set the IDXth value of case C using D_IN */
961 psppire_data_store_data_in (PsppireDataStore *ds, casenumber casenum, gint idx,
962 struct substring input, const struct fmt_spec *fmt)
964 union value *value = NULL;
968 g_return_val_if_fail (ds, FALSE);
969 g_return_val_if_fail (ds->datasheet, FALSE);
971 g_return_val_if_fail (idx < datasheet_get_column_cnt (ds->datasheet), FALSE);
973 width = fmt_var_width (fmt);
974 value = xmalloca (value_cnt_from_width (width) * sizeof *value);
975 ok = (datasheet_get_value (ds->datasheet, casenum, idx, value, width)
976 && data_in (input, LEGACY_NATIVE, fmt->type, 0, 0, 0, value, width)
977 && datasheet_put_value (ds->datasheet, casenum, idx, value, width));
982 g_signal_emit (ds, signals [CASE_CHANGED], 0, casenum);
987 /* Resize the cases in the casefile, by inserting N_VALUES into every
988 one of them at the position immediately preceeding WHERE.
991 psppire_data_store_insert_values (PsppireDataStore *ds,
992 gint n_values, gint where)
994 g_return_val_if_fail (ds, FALSE);
999 g_assert (n_values > 0);
1001 if ( ! ds->datasheet )
1002 ds->datasheet = datasheet_create (NULL);
1005 union value *values = xcalloc (n_values, sizeof *values);
1006 datasheet_insert_columns (ds->datasheet, values, n_values, where);