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"
34 #include <libpspp/i18n.h>
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>
43 #include <math/sort.h>
50 static void psppire_data_store_init (PsppireDataStore *data_store);
51 static void psppire_data_store_class_init (PsppireDataStoreClass *class);
52 static void psppire_data_store_sheet_model_init (PsppireSheetModelIface *iface);
54 static void psppire_data_store_finalize (GObject *object);
55 static void psppire_data_store_dispose (GObject *object);
57 static gboolean psppire_data_store_clear_datum (PsppireSheetModel *model,
58 glong row, glong column);
61 static gboolean psppire_data_store_insert_case (PsppireDataStore *ds,
66 static gboolean psppire_data_store_data_in (PsppireDataStore *ds,
67 casenumber casenum, gint idx,
68 struct substring input,
69 const struct fmt_spec *fmt);
73 static GObjectClass *parent_class = NULL;
85 static guint signals [n_SIGNALS];
89 psppire_data_store_get_type (void)
91 static GType data_store_type = 0;
95 static const GTypeInfo data_store_info =
97 sizeof (PsppireDataStoreClass),
99 NULL, /* base_finalize */
100 (GClassInitFunc) psppire_data_store_class_init,
101 NULL, /* class_finalize */
102 NULL, /* class_data */
103 sizeof (PsppireDataStore),
105 (GInstanceInitFunc) psppire_data_store_init,
108 static const GInterfaceInfo sheet_model_info =
110 (GInterfaceInitFunc) psppire_data_store_sheet_model_init,
116 data_store_type = g_type_register_static (G_TYPE_OBJECT,
118 &data_store_info, 0);
120 g_type_add_interface_static (data_store_type,
121 PSPPIRE_TYPE_SHEET_MODEL,
126 return data_store_type;
131 psppire_data_store_class_init (PsppireDataStoreClass *class)
133 GObjectClass *object_class;
135 parent_class = g_type_class_peek_parent (class);
136 object_class = (GObjectClass*) class;
138 object_class->finalize = psppire_data_store_finalize;
139 object_class->dispose = psppire_data_store_dispose;
141 signals [BACKEND_CHANGED] =
142 g_signal_new ("backend-changed",
143 G_TYPE_FROM_CLASS (class),
147 g_cclosure_marshal_VOID__VOID,
151 signals [CASE_INSERTED] =
152 g_signal_new ("case-inserted",
153 G_TYPE_FROM_CLASS (class),
157 g_cclosure_marshal_VOID__INT,
163 signals [CASE_CHANGED] =
164 g_signal_new ("case-changed",
165 G_TYPE_FROM_CLASS (class),
169 g_cclosure_marshal_VOID__INT,
174 signals [CASES_DELETED] =
175 g_signal_new ("cases-deleted",
176 G_TYPE_FROM_CLASS (class),
180 psppire_marshal_VOID__INT_INT,
190 psppire_data_store_insert_values (PsppireDataStore *ds,
191 gint n_values, gint where);
194 psppire_data_store_get_value (const PsppireDataStore *ds,
195 casenumber casenum, size_t idx,
196 union value *value, int width);
200 psppire_data_store_set_value (PsppireDataStore *ds, casenumber casenum,
201 gint idx, union value *v, gint width);
207 psppire_data_store_get_var_count (const PsppireSheetModel *model)
209 const PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
211 return psppire_dict_get_var_cnt (store->dict);
215 psppire_data_store_get_case_count (const PsppireDataStore *store)
217 return datasheet_get_row_cnt (store->datasheet);
221 psppire_data_store_get_value_count (const PsppireDataStore *store)
223 return psppire_dict_get_value_cnt (store->dict);
227 psppire_data_store_get_case_count_wrapper (const PsppireSheetModel *model)
229 const PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
230 return psppire_data_store_get_case_count (store);
234 psppire_data_store_init (PsppireDataStore *data_store)
236 data_store->dict = 0;
237 data_store->datasheet = NULL;
238 data_store->dispose_has_run = FALSE;
241 static inline gchar *
242 psppire_data_store_get_string_wrapper (const PsppireSheetModel *model, glong row,
245 return psppire_data_store_get_string (PSPPIRE_DATA_STORE (model), row, column);
249 static inline gboolean
250 psppire_data_store_set_string_wrapper (PsppireSheetModel *model,
252 glong row, glong column)
254 return psppire_data_store_set_string (PSPPIRE_DATA_STORE (model), text,
260 static gchar * get_column_subtitle (const PsppireSheetModel *model, gint col);
261 static gchar * get_column_button_label (const PsppireSheetModel *model, gint col);
262 static gboolean get_column_sensitivity (const PsppireSheetModel *model, gint col);
263 static GtkJustification get_column_justification (const PsppireSheetModel *model, gint col);
265 static gchar * get_row_button_label (const PsppireSheetModel *model, gint row);
266 static gboolean get_row_sensitivity (const PsppireSheetModel *model, gint row);
267 static gboolean get_row_overstrike (const PsppireSheetModel *model, gint row);
271 psppire_data_store_sheet_model_init (PsppireSheetModelIface *iface)
273 iface->free_strings = TRUE;
274 iface->get_string = psppire_data_store_get_string_wrapper;
275 iface->set_string = psppire_data_store_set_string_wrapper;
276 iface->clear_datum = psppire_data_store_clear_datum;
277 iface->is_editable = NULL;
278 iface->get_foreground = NULL;
279 iface->get_background = NULL;
280 iface->get_column_count = psppire_data_store_get_var_count;
281 iface->get_row_count = psppire_data_store_get_case_count_wrapper;
283 iface->get_column_subtitle = get_column_subtitle;
284 iface->get_column_title = get_column_button_label;
285 iface->get_column_sensitivity = get_column_sensitivity;
286 iface->get_column_justification = get_column_justification;
288 iface->get_row_title = get_row_button_label;
289 iface->get_row_sensitivity = get_row_sensitivity;
290 iface->get_row_overstrike = get_row_overstrike;
295 A callback which occurs after a variable has been deleted.
298 delete_variable_callback (GObject *obj, gint dict_index,
299 gint case_index, gint val_cnt,
302 PsppireDataStore *store = PSPPIRE_DATA_STORE (data);
305 psppire_sheet_model_columns_deleted (PSPPIRE_SHEET_MODEL (store), dict_index, 1);
309 psppire_sheet_column_columns_changed (PSPPIRE_SHEET_COLUMN (store),
315 variable_changed_callback (GObject *obj, gint var_num, gpointer data)
318 PsppireDataStore *store = PSPPIRE_DATA_STORE (data);
320 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 cc = case_create (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 recode_string (UTF8, psppire_dict_encoding (store->dict),
607 fp = var_get_write_format (pv);
609 s = g_string_sized_new (fp->w + 1);
610 g_string_set_size (s, fp->w);
612 memset (s->str, 0, fp->w);
614 g_assert (fp->w == s->len);
616 /* Converts binary value V into printable form in the exactly
617 FP->W character in buffer S according to format specification
618 FP. No null terminator is appended to the buffer. */
619 data_out (v, fp, s->str);
621 text = recode_string (UTF8, psppire_dict_encoding (store->dict),
623 g_string_free (s, TRUE);
633 psppire_data_store_clear_datum (PsppireSheetModel *model,
634 glong row, glong col)
636 PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
639 const struct variable *pv = psppire_dict_get_variable (store->dict, col);
641 const gint index = var_get_case_index (pv) ;
643 if ( var_is_numeric (pv))
646 memcpy (v.s, "", MAX_SHORT_STRING);
648 psppire_data_store_set_value (store, row, index, &v,
651 psppire_sheet_model_range_changed (model, row, col, row, col);
657 /* Attempts to update that part of the variable store which corresponds
658 to ROW, COL with the value TEXT.
659 Returns true if anything was updated, false otherwise.
662 psppire_data_store_set_string (PsppireDataStore *store,
663 const gchar *text, glong row, glong col)
667 const struct variable *pv = psppire_dict_get_variable (store->dict, col);
671 n_cases = psppire_data_store_get_case_count (store);
677 psppire_data_store_insert_new_case (store, row);
679 s = recode_string (psppire_dict_encoding (store->dict), UTF8, text, -1);
681 psppire_data_store_data_in (store, row,
682 var_get_case_index (pv), ss_cstr (s),
683 var_get_write_format (pv));
686 psppire_sheet_model_range_changed (PSPPIRE_SHEET_MODEL (store), row, col, row, col);
694 psppire_data_store_show_labels (PsppireDataStore *store, gboolean show_labels)
696 g_return_if_fail (store);
697 g_return_if_fail (PSPPIRE_IS_DATA_STORE (store));
699 store->show_labels = show_labels;
701 psppire_sheet_model_range_changed (PSPPIRE_SHEET_MODEL (store),
707 psppire_data_store_clear (PsppireDataStore *ds)
709 datasheet_destroy (ds->datasheet);
710 ds->datasheet = NULL;
712 psppire_dict_clear (ds->dict);
714 g_signal_emit (ds, signals [CASES_DELETED], 0, 0, -1);
719 /* Return a casereader made from this datastore */
721 psppire_data_store_get_reader (PsppireDataStore *ds)
724 struct casereader *reader ;
727 for (i = 0 ; i < n_dict_signals; ++i )
729 g_signal_handler_block (ds->dict,
730 ds->dict_handler_id[i]);
733 reader = datasheet_make_reader (ds->datasheet);
735 /* We must not reference this again */
736 ds->datasheet = NULL;
743 /* Column related funcs */
746 static const gchar null_var_name[]=N_("var");
750 /* Row related funcs */
753 get_row_button_label (const PsppireSheetModel *model, gint unit)
755 PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
756 gchar *s = g_strdup_printf (_("%d"), unit + FIRST_CASE_NUMBER);
758 gchar *text = recode_string (UTF8, psppire_dict_encoding (ds->dict),
768 get_row_sensitivity (const PsppireSheetModel *model, gint unit)
770 PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
772 return (unit < psppire_data_store_get_case_count (ds));
778 /* Column related stuff */
781 get_column_subtitle (const PsppireSheetModel *model, gint col)
784 const struct variable *v ;
785 PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
787 if ( col >= psppire_dict_get_var_cnt (ds->dict) )
790 v = psppire_dict_get_variable (ds->dict, col);
792 if ( ! var_has_label (v))
795 text = recode_string (UTF8, psppire_dict_encoding (ds->dict),
796 var_get_label (v), -1);
802 get_column_button_label (const PsppireSheetModel *model, gint col)
805 struct variable *pv ;
806 PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
808 if ( col >= psppire_dict_get_var_cnt (ds->dict) )
809 return g_locale_to_utf8 (null_var_name, -1, 0, 0, 0);
811 pv = psppire_dict_get_variable (ds->dict, col);
813 text = recode_string (UTF8, psppire_dict_encoding (ds->dict),
814 var_get_name (pv), -1);
820 get_column_sensitivity (const PsppireSheetModel *model, gint col)
822 PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
824 return (col < psppire_dict_get_var_cnt (ds->dict));
829 static GtkJustification
830 get_column_justification (const PsppireSheetModel *model, gint col)
832 PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
833 const struct variable *pv ;
835 if ( col >= psppire_dict_get_var_cnt (ds->dict) )
836 return GTK_JUSTIFY_LEFT;
838 pv = psppire_dict_get_variable (ds->dict, col);
840 return (var_get_alignment (pv) == ALIGN_LEFT ? GTK_JUSTIFY_LEFT
841 : var_get_alignment (pv) == ALIGN_RIGHT ? GTK_JUSTIFY_RIGHT
842 : GTK_JUSTIFY_CENTER);
850 /* Returns the CASENUMth case, or a null pointer on failure.
853 psppire_data_store_get_case (const PsppireDataStore *ds,
856 g_return_val_if_fail (ds, FALSE);
857 g_return_val_if_fail (ds->datasheet, FALSE);
859 return datasheet_get_row (ds->datasheet, casenum);
864 psppire_data_store_delete_cases (PsppireDataStore *ds, casenumber first,
867 g_return_val_if_fail (ds, FALSE);
868 g_return_val_if_fail (ds->datasheet, FALSE);
870 g_return_val_if_fail (first + n_cases <=
871 psppire_data_store_get_case_count (ds), FALSE);
874 datasheet_delete_rows (ds->datasheet, first, n_cases);
876 g_signal_emit (ds, signals [CASES_DELETED], 0, first, n_cases);
877 psppire_sheet_model_rows_deleted (PSPPIRE_SHEET_MODEL (ds), first, n_cases);
884 /* Insert case CC into the case file before POSN */
886 psppire_data_store_insert_case (PsppireDataStore *ds,
892 g_return_val_if_fail (ds, FALSE);
893 g_return_val_if_fail (ds->datasheet, FALSE);
896 result = datasheet_insert_rows (ds->datasheet, posn, &cc, 1);
900 g_signal_emit (ds, signals [CASE_INSERTED], 0, posn);
901 psppire_sheet_model_rows_inserted (PSPPIRE_SHEET_MODEL (ds), posn, 1);
904 g_warning ("Cannot insert case at position %ld\n", posn);
910 /* Copies the IDXth value from case CASENUM into VALUE.
911 If VALUE is null, then memory is allocated is allocated with
912 malloc. Returns the value if successful, NULL on failure. */
914 psppire_data_store_get_value (const PsppireDataStore *ds,
915 casenumber casenum, size_t idx,
916 union value *value, int width)
920 g_return_val_if_fail (ds, false);
921 g_return_val_if_fail (ds->datasheet, false);
923 g_return_val_if_fail (idx < datasheet_get_column_cnt (ds->datasheet), false);
927 value = xnmalloc (value_cnt_from_width (width), sizeof *value);
932 if (!datasheet_get_value (ds->datasheet, casenum, idx, value, width))
943 /* Set the IDXth value of case C to V.
944 Returns true if successful, false on I/O error. */
946 psppire_data_store_set_value (PsppireDataStore *ds, casenumber casenum,
947 gint idx, union value *v, gint width)
951 g_return_val_if_fail (ds, FALSE);
952 g_return_val_if_fail (ds->datasheet, FALSE);
954 g_return_val_if_fail (idx < datasheet_get_column_cnt (ds->datasheet), FALSE);
956 ok = datasheet_put_value (ds->datasheet, casenum, idx, v, width);
958 g_signal_emit (ds, signals [CASE_CHANGED], 0, casenum);
966 /* Set the IDXth value of case C using D_IN */
968 psppire_data_store_data_in (PsppireDataStore *ds, casenumber casenum, gint idx,
969 struct substring input, const struct fmt_spec *fmt)
971 union value *value = NULL;
975 g_return_val_if_fail (ds, FALSE);
976 g_return_val_if_fail (ds->datasheet, FALSE);
978 g_return_val_if_fail (idx < datasheet_get_column_cnt (ds->datasheet), FALSE);
980 width = fmt_var_width (fmt);
981 value = xmalloca (value_cnt_from_width (width) * sizeof *value);
982 ok = (datasheet_get_value (ds->datasheet, casenum, idx, value, width)
983 && data_in (input, LEGACY_NATIVE, fmt->type, 0, 0, 0, value, width)
984 && datasheet_put_value (ds->datasheet, casenum, idx, value, width));
989 g_signal_emit (ds, signals [CASE_CHANGED], 0, casenum);
994 /* Resize the cases in the casefile, by inserting N_VALUES into every
995 one of them at the position immediately preceeding WHERE.
998 psppire_data_store_insert_values (PsppireDataStore *ds,
999 gint n_values, gint where)
1001 g_return_val_if_fail (ds, FALSE);
1003 if ( n_values == 0 )
1006 g_assert (n_values > 0);
1008 if ( ! ds->datasheet )
1009 ds->datasheet = datasheet_create (NULL);
1012 union value *values = xcalloc (n_values, sizeof *values);
1013 datasheet_insert_columns (ds->datasheet, values, n_values, where);
1021 get_row_overstrike (const PsppireSheetModel *model, gint row)
1024 PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
1026 const struct dictionary *dict = ds->dict->dict;
1028 const struct variable *filter = dict_get_filter (dict);
1030 if ( row < 0 || row >= datasheet_get_row_cnt (ds->datasheet))
1036 g_assert (var_is_numeric (filter));
1038 if ( ! datasheet_get_value (ds->datasheet, row,
1039 var_get_case_index (filter),
1044 return (val.f == 0.0);