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);
266 static gboolean get_row_overstrike (const PsppireSheetModel *model, gint row);
270 psppire_data_store_sheet_model_init (PsppireSheetModelIface *iface)
272 iface->free_strings = TRUE;
273 iface->get_string = psppire_data_store_get_string_wrapper;
274 iface->set_string = psppire_data_store_set_string_wrapper;
275 iface->clear_datum = psppire_data_store_clear_datum;
276 iface->is_editable = NULL;
277 iface->get_foreground = NULL;
278 iface->get_background = NULL;
279 iface->get_column_count = psppire_data_store_get_var_count;
280 iface->get_row_count = psppire_data_store_get_case_count_wrapper;
282 iface->get_column_subtitle = get_column_subtitle;
283 iface->get_column_title = get_column_button_label;
284 iface->get_column_sensitivity = get_column_sensitivity;
285 iface->get_column_justification = get_column_justification;
287 iface->get_row_title = get_row_button_label;
288 iface->get_row_sensitivity = get_row_sensitivity;
289 iface->get_row_overstrike = get_row_overstrike;
294 A callback which occurs after a variable has been deleted.
297 delete_variable_callback (GObject *obj, gint dict_index,
298 gint case_index, gint val_cnt,
301 PsppireDataStore *store = PSPPIRE_DATA_STORE (data);
304 psppire_sheet_model_columns_deleted (PSPPIRE_SHEET_MODEL (store), dict_index, 1);
308 psppire_sheet_column_columns_changed (PSPPIRE_SHEET_COLUMN (store),
314 variable_changed_callback (GObject *obj, gint var_num, gpointer data)
317 PsppireDataStore *store = PSPPIRE_DATA_STORE (data);
319 psppire_sheet_column_columns_changed (PSPPIRE_SHEET_COLUMN (store),
322 psppire_sheet_model_range_changed (PSPPIRE_SHEET_MODEL (store),
329 insert_variable_callback (GObject *obj, gint var_num, gpointer data)
331 PsppireDataStore *store;
334 g_return_if_fail (data);
336 store = PSPPIRE_DATA_STORE (data);
340 struct variable *variable =
341 psppire_dict_get_variable (store->dict, var_num);
343 g_assert (variable != NULL);
345 posn = var_get_case_index (variable);
352 psppire_data_store_insert_values (store, 1, posn);
356 psppire_sheet_column_columns_changed (PSPPIRE_SHEET_COLUMN (store),
360 psppire_sheet_model_columns_inserted (PSPPIRE_SHEET_MODEL (store), var_num, 1);
365 dict_size_change_callback (GObject *obj,
366 gint posn, gint adjustment, gpointer data)
368 PsppireDataStore *store = PSPPIRE_DATA_STORE (data);
370 const struct variable *v = psppire_dict_get_variable (store->dict, posn);
372 const gint new_val_width = value_cnt_from_width (var_get_width (v));
374 if ( adjustment > 0 )
375 psppire_data_store_insert_values (store, adjustment,
376 new_val_width - adjustment +
377 var_get_case_index(v));
383 * psppire_data_store_new:
384 * @dict: The dictionary for this data_store.
387 * Return value: a new #PsppireDataStore
390 psppire_data_store_new (PsppireDict *dict)
392 PsppireDataStore *retval;
394 retval = g_object_new (GTK_TYPE_DATA_STORE, NULL);
396 psppire_data_store_set_dictionary (retval, dict);
402 psppire_data_store_set_reader (PsppireDataStore *ds,
403 struct casereader *reader)
408 datasheet_destroy (ds->datasheet);
410 ds->datasheet = datasheet_create (reader);
412 psppire_sheet_model_range_changed (PSPPIRE_SHEET_MODEL (ds),
416 for (i = 0 ; i < n_dict_signals; ++i )
418 if ( ds->dict_handler_id [i] > 0)
420 g_signal_handler_unblock (ds->dict,
421 ds->dict_handler_id[i]);
425 g_signal_emit (ds, signals[BACKEND_CHANGED], 0);
430 * psppire_data_store_replace_set_dictionary:
431 * @data_store: The variable store
432 * @dict: The dictionary to set
434 * If a dictionary is already associated with the data-store, then it will be
438 psppire_data_store_set_dictionary (PsppireDataStore *data_store, PsppireDict *dict)
442 /* Disconnect any existing handlers */
443 if ( data_store->dict )
444 for (i = 0 ; i < n_dict_signals; ++i )
446 g_signal_handler_disconnect (data_store->dict,
447 data_store->dict_handler_id[i]);
450 data_store->dict = dict;
455 data_store->dict_handler_id [VARIABLE_INSERTED] =
456 g_signal_connect (dict, "variable-inserted",
457 G_CALLBACK (insert_variable_callback),
460 data_store->dict_handler_id [VARIABLE_DELETED] =
461 g_signal_connect (dict, "variable-deleted",
462 G_CALLBACK (delete_variable_callback),
465 data_store->dict_handler_id [VARIABLE_CHANGED] =
466 g_signal_connect (dict, "variable-changed",
467 G_CALLBACK (variable_changed_callback),
470 data_store->dict_handler_id [SIZE_CHANGED] =
471 g_signal_connect (dict, "dict-size-changed",
472 G_CALLBACK (dict_size_change_callback),
478 /* The entire model has changed */
479 psppire_sheet_model_range_changed (PSPPIRE_SHEET_MODEL (data_store), -1, -1, -1, -1);
482 psppire_sheet_column_columns_changed (PSPPIRE_SHEET_COLUMN (data_store), 0, -1);
485 if ( data_store->dict )
486 for (i = 0 ; i < n_dict_signals; ++i )
488 if ( data_store->dict_handler_id [i] > 0)
490 g_signal_handler_block (data_store->dict,
491 data_store->dict_handler_id[i]);
497 psppire_data_store_finalize (GObject *object)
501 (* parent_class->finalize) (object);
506 psppire_data_store_dispose (GObject *object)
508 PsppireDataStore *ds = PSPPIRE_DATA_STORE (object);
510 if (ds->dispose_has_run)
515 datasheet_destroy (ds->datasheet);
516 ds->datasheet = NULL;
520 (* parent_class->dispose) (object);
522 ds->dispose_has_run = TRUE;
527 /* Insert a blank case before POSN */
529 psppire_data_store_insert_new_case (PsppireDataStore *ds, casenumber posn)
534 g_return_val_if_fail (ds, FALSE);
536 val_cnt = datasheet_get_column_cnt (ds->datasheet) ;
538 g_return_val_if_fail (val_cnt > 0, FALSE);
540 g_return_val_if_fail (posn <= psppire_data_store_get_case_count (ds), FALSE);
542 cc = case_create (val_cnt);
544 memset ( case_data_rw_idx (cc, 0), 0, val_cnt * MAX_SHORT_STRING);
546 for (v = 0 ; v < psppire_dict_get_var_cnt (ds->dict) ; ++v)
548 const struct variable *pv = psppire_dict_get_variable (ds->dict, v);
549 if ( var_is_alpha (pv))
552 case_data_rw (cc, pv)->f = SYSMIS;
555 result = psppire_data_store_insert_case (ds, cc, posn);
564 psppire_data_store_get_string (PsppireDataStore *store, glong row, glong column)
568 const struct fmt_spec *fp ;
569 const struct variable *pv ;
573 g_return_val_if_fail (store->dict, NULL);
574 g_return_val_if_fail (store->datasheet, NULL);
576 if (column >= psppire_dict_get_var_cnt (store->dict))
579 if ( row >= psppire_data_store_get_case_count (store))
582 pv = psppire_dict_get_variable (store->dict, column);
586 idx = var_get_case_index (pv);
590 v = psppire_data_store_get_value (store, row, idx, NULL,
593 g_return_val_if_fail (v, NULL);
595 if ( store->show_labels)
597 const gchar *label = var_lookup_value_label (pv, v);
601 return pspp_locale_to_utf8 (label, -1, 0);
605 fp = var_get_write_format (pv);
607 s = g_string_sized_new (fp->w + 1);
608 g_string_set_size (s, fp->w);
610 memset (s->str, 0, fp->w);
612 g_assert (fp->w == s->len);
614 /* Converts binary value V into printable form in the exactly
615 FP->W character in buffer S according to format specification
616 FP. No null terminator is appended to the buffer. */
617 data_out (v, fp, s->str);
619 text = pspp_locale_to_utf8 (s->str, fp->w, 0);
620 g_string_free (s, TRUE);
630 psppire_data_store_clear_datum (PsppireSheetModel *model,
631 glong row, glong col)
633 PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
636 const struct variable *pv = psppire_dict_get_variable (store->dict, col);
638 const gint index = var_get_case_index (pv) ;
640 if ( var_is_numeric (pv))
643 memcpy (v.s, "", MAX_SHORT_STRING);
645 psppire_data_store_set_value (store, row, index, &v,
648 psppire_sheet_model_range_changed (model, row, col, row, col);
654 /* Attempts to update that part of the variable store which corresponds
655 to ROW, COL with the value TEXT.
656 Returns true if anything was updated, false otherwise.
659 psppire_data_store_set_string (PsppireDataStore *store,
660 const gchar *text, glong row, glong col)
663 const struct variable *pv = psppire_dict_get_variable (store->dict, col);
667 n_cases = psppire_data_store_get_case_count (store);
673 psppire_data_store_insert_new_case (store, row);
675 psppire_data_store_data_in (store, row,
676 var_get_case_index (pv), ss_cstr (text),
677 var_get_write_format (pv));
679 psppire_sheet_model_range_changed (PSPPIRE_SHEET_MODEL (store), row, col, row, col);
687 psppire_data_store_show_labels (PsppireDataStore *store, gboolean show_labels)
689 g_return_if_fail (store);
690 g_return_if_fail (PSPPIRE_IS_DATA_STORE (store));
692 store->show_labels = show_labels;
694 psppire_sheet_model_range_changed (PSPPIRE_SHEET_MODEL (store),
700 psppire_data_store_clear (PsppireDataStore *ds)
702 datasheet_destroy (ds->datasheet);
703 ds->datasheet = NULL;
705 psppire_dict_clear (ds->dict);
707 g_signal_emit (ds, signals [CASES_DELETED], 0, 0, -1);
712 /* Return a casereader made from this datastore */
714 psppire_data_store_get_reader (PsppireDataStore *ds)
717 struct casereader *reader ;
720 for (i = 0 ; i < n_dict_signals; ++i )
722 g_signal_handler_block (ds->dict,
723 ds->dict_handler_id[i]);
726 reader = datasheet_make_reader (ds->datasheet);
728 /* We must not reference this again */
729 ds->datasheet = NULL;
736 /* Column related funcs */
739 static const gchar null_var_name[]=N_("var");
743 /* Row related funcs */
746 get_row_button_label (const PsppireSheetModel *model, gint unit)
748 gchar *s = g_strdup_printf (_("%d"), unit + FIRST_CASE_NUMBER);
750 gchar *text = pspp_locale_to_utf8 (s, -1, 0);
759 get_row_sensitivity (const PsppireSheetModel *model, gint unit)
761 PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
763 return (unit < psppire_data_store_get_case_count (ds));
769 /* Column related stuff */
772 get_column_subtitle (const PsppireSheetModel *model, gint col)
775 const struct variable *v ;
776 PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
778 if ( col >= psppire_dict_get_var_cnt (ds->dict) )
781 v = psppire_dict_get_variable (ds->dict, col);
783 if ( ! var_has_label (v))
786 text = pspp_locale_to_utf8 (var_get_label (v), -1, 0);
792 get_column_button_label (const PsppireSheetModel *model, gint col)
795 struct variable *pv ;
796 PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
798 if ( col >= psppire_dict_get_var_cnt (ds->dict) )
799 return g_locale_to_utf8 (null_var_name, -1, 0, 0, 0);
801 pv = psppire_dict_get_variable (ds->dict, col);
803 text = pspp_locale_to_utf8 (var_get_name (pv), -1, 0);
809 get_column_sensitivity (const PsppireSheetModel *model, gint col)
811 PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
813 return (col < psppire_dict_get_var_cnt (ds->dict));
818 static GtkJustification
819 get_column_justification (const PsppireSheetModel *model, gint col)
821 PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
822 const struct variable *pv ;
824 if ( col >= psppire_dict_get_var_cnt (ds->dict) )
825 return GTK_JUSTIFY_LEFT;
827 pv = psppire_dict_get_variable (ds->dict, col);
829 return (var_get_alignment (pv) == ALIGN_LEFT ? GTK_JUSTIFY_LEFT
830 : var_get_alignment (pv) == ALIGN_RIGHT ? GTK_JUSTIFY_RIGHT
831 : GTK_JUSTIFY_CENTER);
839 /* Returns the CASENUMth case, or a null pointer on failure.
842 psppire_data_store_get_case (const PsppireDataStore *ds,
845 g_return_val_if_fail (ds, FALSE);
846 g_return_val_if_fail (ds->datasheet, FALSE);
848 return datasheet_get_row (ds->datasheet, casenum);
853 psppire_data_store_delete_cases (PsppireDataStore *ds, casenumber first,
856 g_return_val_if_fail (ds, FALSE);
857 g_return_val_if_fail (ds->datasheet, FALSE);
859 g_return_val_if_fail (first + n_cases <=
860 psppire_data_store_get_case_count (ds), FALSE);
863 datasheet_delete_rows (ds->datasheet, first, n_cases);
865 g_signal_emit (ds, signals [CASES_DELETED], 0, first, n_cases);
866 psppire_sheet_model_rows_deleted (PSPPIRE_SHEET_MODEL (ds), first, n_cases);
873 /* Insert case CC into the case file before POSN */
875 psppire_data_store_insert_case (PsppireDataStore *ds,
881 g_return_val_if_fail (ds, FALSE);
882 g_return_val_if_fail (ds->datasheet, FALSE);
885 result = datasheet_insert_rows (ds->datasheet, posn, &cc, 1);
889 g_signal_emit (ds, signals [CASE_INSERTED], 0, posn);
890 psppire_sheet_model_rows_inserted (PSPPIRE_SHEET_MODEL (ds), posn, 1);
893 g_warning ("Cannot insert case at position %ld\n", posn);
899 /* Copies the IDXth value from case CASENUM into VALUE.
900 If VALUE is null, then memory is allocated is allocated with
901 malloc. Returns the value if successful, NULL on failure. */
903 psppire_data_store_get_value (const PsppireDataStore *ds,
904 casenumber casenum, size_t idx,
905 union value *value, int width)
909 g_return_val_if_fail (ds, false);
910 g_return_val_if_fail (ds->datasheet, false);
912 g_return_val_if_fail (idx < datasheet_get_column_cnt (ds->datasheet), false);
916 value = xnmalloc (value_cnt_from_width (width), sizeof *value);
921 if (!datasheet_get_value (ds->datasheet, casenum, idx, value, width))
932 /* Set the IDXth value of case C to V.
933 Returns true if successful, false on I/O error. */
935 psppire_data_store_set_value (PsppireDataStore *ds, casenumber casenum,
936 gint idx, union value *v, gint width)
940 g_return_val_if_fail (ds, FALSE);
941 g_return_val_if_fail (ds->datasheet, FALSE);
943 g_return_val_if_fail (idx < datasheet_get_column_cnt (ds->datasheet), FALSE);
945 ok = datasheet_put_value (ds->datasheet, casenum, idx, v, width);
947 g_signal_emit (ds, signals [CASE_CHANGED], 0, casenum);
955 /* Set the IDXth value of case C using D_IN */
957 psppire_data_store_data_in (PsppireDataStore *ds, casenumber casenum, gint idx,
958 struct substring input, const struct fmt_spec *fmt)
960 union value *value = NULL;
964 g_return_val_if_fail (ds, FALSE);
965 g_return_val_if_fail (ds->datasheet, FALSE);
967 g_return_val_if_fail (idx < datasheet_get_column_cnt (ds->datasheet), FALSE);
969 width = fmt_var_width (fmt);
970 value = xmalloca (value_cnt_from_width (width) * sizeof *value);
971 ok = (datasheet_get_value (ds->datasheet, casenum, idx, value, width)
972 && data_in (input, LEGACY_NATIVE, fmt->type, 0, 0, 0, value, width)
973 && datasheet_put_value (ds->datasheet, casenum, idx, value, width));
978 g_signal_emit (ds, signals [CASE_CHANGED], 0, casenum);
983 /* Resize the cases in the casefile, by inserting N_VALUES into every
984 one of them at the position immediately preceeding WHERE.
987 psppire_data_store_insert_values (PsppireDataStore *ds,
988 gint n_values, gint where)
990 g_return_val_if_fail (ds, FALSE);
995 g_assert (n_values > 0);
997 if ( ! ds->datasheet )
998 ds->datasheet = datasheet_create (NULL);
1001 union value *values = xcalloc (n_values, sizeof *values);
1002 datasheet_insert_columns (ds->datasheet, values, n_values, where);
1010 get_row_overstrike (const PsppireSheetModel *model, gint row)
1013 PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
1015 const struct dictionary *dict = ds->dict->dict;
1017 const struct variable *filter = dict_get_filter (dict);
1019 if ( row < 0 || row >= datasheet_get_row_cnt (ds->datasheet))
1025 g_assert (var_is_numeric (filter));
1027 if ( ! datasheet_get_value (ds->datasheet, row,
1028 var_get_case_index (filter),
1033 return (val.f == 0.0);