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 <gtksheet/gsheetmodel.h>
30 #include <pango/pango-context.h>
32 #include "psppire-data-store.h"
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>
41 #include <math/sort.h>
47 enum cf_signal_handler {
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);
59 static void psppire_data_store_finalize (GObject *object);
60 static void psppire_data_store_dispose (GObject *object);
62 static gboolean psppire_data_store_clear_datum (GSheetModel *model,
63 glong row, glong column);
66 static gboolean psppire_data_store_insert_case (PsppireDataStore *ds,
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);
78 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, "PsppireDataStore",
117 &data_store_info, 0);
119 g_type_add_interface_static (data_store_type,
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,
154 psppire_data_store_insert_values (PsppireDataStore *ds,
155 gint n_values, gint where);
158 psppire_data_store_get_value (const PsppireDataStore *ds,
159 casenumber casenum, size_t idx,
160 union value *value, int width);
164 psppire_data_store_set_value (PsppireDataStore *ds, casenumber casenum,
165 gint idx, union value *v, gint width);
171 psppire_data_store_get_var_count (const GSheetModel *model)
173 const PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
175 return psppire_dict_get_var_cnt (store->dict);
179 psppire_data_store_get_case_count (const PsppireDataStore *store)
181 return datasheet_get_row_cnt (store->datasheet);
185 psppire_data_store_get_value_count (const PsppireDataStore *store)
187 return psppire_dict_get_value_cnt (store->dict);
191 psppire_data_store_get_case_count_wrapper (const GSheetModel *model)
193 const PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
194 return psppire_data_store_get_case_count (store);
198 psppire_data_store_init (PsppireDataStore *data_store)
200 data_store->dict = 0;
201 data_store->datasheet = NULL;
202 data_store->dispose_has_run = FALSE;
205 static inline gchar *
206 psppire_data_store_get_string_wrapper (const GSheetModel *model, glong row,
209 return psppire_data_store_get_string (PSPPIRE_DATA_STORE (model), row, column);
213 static inline gboolean
214 psppire_data_store_set_string_wrapper (GSheetModel *model,
216 glong row, glong column)
218 return psppire_data_store_set_string (PSPPIRE_DATA_STORE (model), text,
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);
229 static gchar * get_row_button_label (const GSheetModel *model, gint row);
230 static gboolean get_row_sensitivity (const GSheetModel *model, gint row);
234 psppire_data_store_sheet_model_init (GSheetModelIface *iface)
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;
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;
252 iface->get_row_title = get_row_button_label;
253 iface->get_row_sensitivity = get_row_sensitivity;
257 delete_cases_callback (GtkWidget *w,
258 casenumber first, casenumber n_cases, gpointer data)
260 PsppireDataStore *store ;
262 g_return_if_fail (data);
264 store = PSPPIRE_DATA_STORE (data);
266 g_assert (first >= 0);
268 g_sheet_model_rows_deleted (G_SHEET_MODEL (store), first, n_cases);
273 insert_case_callback (GtkWidget *w, casenumber casenum, gpointer data)
275 PsppireDataStore *store = PSPPIRE_DATA_STORE (data);
277 g_return_if_fail (data);
279 g_print ("%s\n", __FUNCTION__);
281 g_sheet_model_range_changed (G_SHEET_MODEL (store),
283 psppire_data_store_get_case_count (store),
286 g_sheet_model_rows_inserted (G_SHEET_MODEL (store), casenum, 1);
291 changed_case_callback (GtkWidget *w, gint casenum, gpointer data)
293 PsppireDataStore *store ;
294 g_return_if_fail (data);
296 store = PSPPIRE_DATA_STORE (data);
298 g_sheet_model_range_changed (G_SHEET_MODEL (store),
305 A callback which occurs after a variable has been deleted.
308 delete_variable_callback (GObject *obj, gint dict_index,
309 gint case_index, gint val_cnt,
312 PsppireDataStore *store = PSPPIRE_DATA_STORE (data);
315 g_sheet_model_columns_deleted (G_SHEET_MODEL (store), dict_index, 1);
317 g_sheet_column_columns_changed (G_SHEET_COLUMN (store),
324 variable_changed_callback (GObject *obj, gint var_num, gpointer data)
326 PsppireDataStore *store = PSPPIRE_DATA_STORE (data);
329 g_sheet_column_columns_changed (G_SHEET_COLUMN (store),
333 g_sheet_model_range_changed (G_SHEET_MODEL (store),
340 insert_variable_callback (GObject *obj, gint var_num, gpointer data)
342 PsppireDataStore *store;
345 g_return_if_fail (data);
347 store = PSPPIRE_DATA_STORE (data);
351 struct variable *variable =
352 psppire_dict_get_variable (store->dict, var_num);
354 g_assert (variable != NULL);
356 posn = var_get_case_index (variable);
363 psppire_data_store_insert_values (store, 1, posn);
366 g_sheet_column_columns_changed (G_SHEET_COLUMN (store),
370 g_sheet_model_columns_inserted (G_SHEET_MODEL (store), var_num, 1);
375 dict_size_change_callback (GObject *obj,
376 gint posn, gint adjustment, gpointer data)
378 PsppireDataStore *store = PSPPIRE_DATA_STORE (data);
380 const struct variable *v = psppire_dict_get_variable (store->dict, posn);
382 const gint new_val_width = value_cnt_from_width (var_get_width (v));
384 if ( adjustment > 0 )
385 psppire_data_store_insert_values (store, adjustment,
386 new_val_width - adjustment +
387 var_get_case_index(v));
393 * psppire_data_store_new:
394 * @dict: The dictionary for this data_store.
397 * Return value: a new #PsppireDataStore
400 psppire_data_store_new (PsppireDict *dict)
402 PsppireDataStore *retval;
404 retval = g_object_new (GTK_TYPE_DATA_STORE, NULL);
406 psppire_data_store_set_dictionary (retval, dict);
412 psppire_data_store_set_reader (PsppireDataStore *ds,
413 struct casereader *reader)
418 datasheet_destroy (ds->datasheet);
420 ds->datasheet = datasheet_create (reader);
422 g_sheet_model_range_changed (G_SHEET_MODEL (ds),
426 for (i = 0 ; i < n_cf_signals ; ++i )
428 if ( ds->cf_handler_id [i] > 0 )
429 g_signal_handler_disconnect (ds->case_file,
430 ds->cf_handler_id[i]);
435 for (i = 0 ; i < n_dict_signals; ++i )
437 if ( ds->dict_handler_id [i] > 0)
439 g_signal_handler_unblock (ds->dict,
440 ds->dict_handler_id[i]);
445 ds->cf_handler_id [CASES_DELETED] =
446 g_signal_connect (ds->case_file, "cases-deleted",
447 G_CALLBACK (delete_cases_callback),
450 ds->cf_handler_id [CASE_INSERTED] =
451 g_signal_connect (ds->case_file, "case-inserted",
452 G_CALLBACK (insert_case_callback),
455 ds->cf_handler_id [CASE_CHANGED] =
456 g_signal_connect (ds->case_file, "case-changed",
457 G_CALLBACK (changed_case_callback),
461 g_signal_emit (ds, signals[BACKEND_CHANGED], 0);
466 * psppire_data_store_replace_set_dictionary:
467 * @data_store: The variable store
468 * @dict: The dictionary to set
470 * If a dictionary is already associated with the data-store, then it will be
474 psppire_data_store_set_dictionary (PsppireDataStore *data_store, PsppireDict *dict)
478 /* Disconnect any existing handlers */
479 if ( data_store->dict )
480 for (i = 0 ; i < n_dict_signals; ++i )
482 g_signal_handler_disconnect (data_store->dict,
483 data_store->dict_handler_id[i]);
486 data_store->dict = dict;
491 data_store->dict_handler_id [VARIABLE_INSERTED] =
492 g_signal_connect (dict, "variable-inserted",
493 G_CALLBACK (insert_variable_callback),
496 data_store->dict_handler_id [VARIABLE_DELETED] =
497 g_signal_connect (dict, "variable-deleted",
498 G_CALLBACK (delete_variable_callback),
501 data_store->dict_handler_id [VARIABLE_CHANGED] =
502 g_signal_connect (dict, "variable-changed",
503 G_CALLBACK (variable_changed_callback),
506 data_store->dict_handler_id [SIZE_CHANGED] =
507 g_signal_connect (dict, "dict-size-changed",
508 G_CALLBACK (dict_size_change_callback),
514 /* The entire model has changed */
515 g_sheet_model_range_changed (G_SHEET_MODEL (data_store), -1, -1, -1, -1);
518 g_sheet_column_columns_changed (G_SHEET_COLUMN (data_store), 0, -1);
521 if ( data_store->dict )
522 for (i = 0 ; i < n_dict_signals; ++i )
524 if ( data_store->dict_handler_id [i] > 0)
526 g_signal_handler_block (data_store->dict,
527 data_store->dict_handler_id[i]);
533 psppire_data_store_finalize (GObject *object)
537 (* parent_class->finalize) (object);
542 psppire_data_store_dispose (GObject *object)
544 PsppireDataStore *ds = PSPPIRE_DATA_STORE (object);
546 if (ds->dispose_has_run)
551 datasheet_destroy (ds->datasheet);
552 ds->datasheet = NULL;
556 (* parent_class->dispose) (object);
558 ds->dispose_has_run = TRUE;
563 /* Insert a blank case before POSN */
565 psppire_data_store_insert_new_case (PsppireDataStore *ds, casenumber posn)
570 g_return_val_if_fail (ds, FALSE);
572 val_cnt = datasheet_get_column_cnt (ds->datasheet) ;
574 g_return_val_if_fail (val_cnt > 0, FALSE);
576 g_return_val_if_fail (posn <= psppire_data_store_get_case_count (ds), FALSE);
578 case_create (&cc, val_cnt);
580 memset ( case_data_rw_idx (&cc, 0), 0, val_cnt * MAX_SHORT_STRING);
582 for (v = 0 ; v < psppire_dict_get_var_cnt (ds->dict) ; ++v)
584 const struct variable *pv = psppire_dict_get_variable (ds->dict, v);
585 if ( var_is_alpha (pv))
588 case_data_rw (&cc, pv)->f = SYSMIS;
591 result = psppire_data_store_insert_case (ds, &cc, posn);
600 psppire_data_store_get_string (PsppireDataStore *store, glong row, glong column)
604 const struct fmt_spec *fp ;
605 const struct variable *pv ;
609 g_return_val_if_fail (store->dict, NULL);
610 g_return_val_if_fail (store->datasheet, NULL);
612 if (column >= psppire_dict_get_var_cnt (store->dict))
615 if ( row >= psppire_data_store_get_case_count (store))
618 pv = psppire_dict_get_variable (store->dict, column);
622 idx = var_get_case_index (pv);
626 v = psppire_data_store_get_value (store, row, idx, NULL,
629 g_return_val_if_fail (v, NULL);
631 if ( store->show_labels)
633 const gchar *label = var_lookup_value_label (pv, v);
637 return pspp_locale_to_utf8 (label, -1, 0);
641 fp = var_get_write_format (pv);
643 s = g_string_sized_new (fp->w + 1);
644 g_string_set_size (s, fp->w);
646 memset (s->str, 0, fp->w);
648 g_assert (fp->w == s->len);
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);
655 text = pspp_locale_to_utf8 (s->str, fp->w, 0);
656 g_string_free (s, TRUE);
666 psppire_data_store_clear_datum (GSheetModel *model,
667 glong row, glong col)
669 PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
672 const struct variable *pv = psppire_dict_get_variable (store->dict, col);
674 const gint index = var_get_case_index (pv) ;
676 if ( var_is_numeric (pv))
679 memcpy (v.s, "", MAX_SHORT_STRING);
681 psppire_data_store_set_value (store, row, index, &v,
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.
693 psppire_data_store_set_string (PsppireDataStore *store,
694 const gchar *text, glong row, glong col)
697 const struct variable *pv = psppire_dict_get_variable (store->dict, col);
698 g_return_val_if_fail (pv, FALSE);
700 n_cases = psppire_data_store_get_case_count (store);
706 psppire_data_store_insert_new_case (store, row);
708 psppire_data_store_data_in (store, row,
709 var_get_case_index (pv), ss_cstr (text),
710 var_get_write_format (pv));
718 psppire_data_store_show_labels (PsppireDataStore *store, gboolean show_labels)
720 g_return_if_fail (store);
721 g_return_if_fail (PSPPIRE_IS_DATA_STORE (store));
723 store->show_labels = show_labels;
725 g_sheet_model_range_changed (G_SHEET_MODEL (store),
731 psppire_data_store_clear (PsppireDataStore *ds)
733 datasheet_destroy (ds->datasheet);
734 ds->datasheet = NULL;
736 psppire_dict_clear (ds->dict);
738 g_signal_emit (ds, signals [CASES_DELETED], 0, 0, -1);
743 /* Return a casereader made from this datastore */
745 psppire_data_store_get_reader (PsppireDataStore *ds)
748 struct casereader *reader ;
751 for (i = 0 ; i < n_cf_signals ; ++i )
753 g_signal_handler_disconnect (ds->case_file, ds->cf_handler_id[i]);
754 ds->cf_handler_id[i] = 0 ;
759 for (i = 0 ; i < n_dict_signals; ++i )
761 g_signal_handler_block (ds->dict,
762 ds->dict_handler_id[i]);
765 reader = datasheet_make_reader (ds->datasheet);
767 /* We must not reference this again */
768 ds->datasheet = NULL;
775 /* Column related funcs */
778 static const gchar null_var_name[]=N_("var");
782 /* Row related funcs */
785 get_row_button_label (const GSheetModel *model, gint unit)
787 gchar *s = g_strdup_printf (_("%d"), unit + FIRST_CASE_NUMBER);
789 gchar *text = pspp_locale_to_utf8 (s, -1, 0);
798 get_row_sensitivity (const GSheetModel *model, gint unit)
800 PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
802 return (unit < psppire_data_store_get_case_count (ds));
808 /* Column related stuff */
811 get_column_subtitle (const GSheetModel *model, gint col)
814 const struct variable *v ;
815 PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
817 if ( col >= psppire_dict_get_var_cnt (ds->dict) )
820 v = psppire_dict_get_variable (ds->dict, col);
822 if ( ! var_has_label (v))
825 text = pspp_locale_to_utf8 (var_get_label (v), -1, 0);
831 get_column_button_label (const GSheetModel *model, gint col)
834 struct variable *pv ;
835 PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
837 if ( col >= psppire_dict_get_var_cnt (ds->dict) )
838 return g_locale_to_utf8 (null_var_name, -1, 0, 0, 0);
840 pv = psppire_dict_get_variable (ds->dict, col);
842 text = pspp_locale_to_utf8 (var_get_name (pv), -1, 0);
848 get_column_sensitivity (const GSheetModel *model, gint col)
850 PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
852 return (col < psppire_dict_get_var_cnt (ds->dict));
857 static GtkJustification
858 get_column_justification (const GSheetModel *model, gint col)
860 PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
861 const struct variable *pv ;
863 if ( col >= psppire_dict_get_var_cnt (ds->dict) )
864 return GTK_JUSTIFY_LEFT;
866 pv = psppire_dict_get_variable (ds->dict, col);
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);
878 /* Fills C with the CASENUMth case.
879 Returns true on success, false otherwise.
882 psppire_data_store_get_case (const PsppireDataStore *ds,
886 g_return_val_if_fail (ds, FALSE);
887 g_return_val_if_fail (ds->datasheet, FALSE);
889 return datasheet_get_row (ds->datasheet, casenum, c);
894 psppire_data_store_delete_cases (PsppireDataStore *ds, casenumber n_cases,
897 g_return_val_if_fail (ds, FALSE);
898 g_return_val_if_fail (ds->datasheet, FALSE);
900 g_return_val_if_fail (first + n_cases <=
901 psppire_data_store_get_case_count (ds), FALSE);
903 datasheet_delete_rows (ds->datasheet, first, n_cases);
905 g_signal_emit (ds, signals [CASES_DELETED], 0, first, n_cases);
912 /* Insert case CC into the case file before POSN */
914 psppire_data_store_insert_case (PsppireDataStore *ds,
921 g_return_val_if_fail (ds, FALSE);
922 g_return_val_if_fail (ds->datasheet, FALSE);
924 case_clone (&tmp, cc);
925 result = datasheet_insert_rows (ds->datasheet, posn, &tmp, 1);
927 g_debug ("Result %d. Inserted case at posn %ld", result, posn);
929 g_signal_emit (ds, signals [CASE_INSERTED], 0, posn);
931 g_warning ("Cannot insert case at position %ld\n", posn);
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. */
941 psppire_data_store_get_value (const PsppireDataStore *ds,
942 casenumber casenum, size_t idx,
943 union value *value, int width)
947 g_return_val_if_fail (ds, false);
948 g_return_val_if_fail (ds->datasheet, false);
950 g_return_val_if_fail (idx < datasheet_get_column_cnt (ds->datasheet), false);
954 value = xnmalloc (value_cnt_from_width (width), sizeof *value);
959 if (!datasheet_get_value (ds->datasheet, casenum, idx, value, width))
970 /* Set the IDXth value of case C to V.
971 Returns true if successful, false on I/O error. */
973 psppire_data_store_set_value (PsppireDataStore *ds, casenumber casenum,
974 gint idx, union value *v, gint width)
978 g_return_val_if_fail (ds, FALSE);
979 g_return_val_if_fail (ds->datasheet, FALSE);
981 g_return_val_if_fail (idx < datasheet_get_column_cnt (ds->datasheet), FALSE);
983 ok = datasheet_put_value (ds->datasheet, casenum, idx, v, width);
985 g_signal_emit (ds, signals [CASE_CHANGED], 0, casenum);
992 /* Set the IDXth value of case C using D_IN */
994 psppire_data_store_data_in (PsppireDataStore *ds, casenumber casenum, gint idx,
995 struct substring input, const struct fmt_spec *fmt)
997 union value *value = NULL;
1001 g_return_val_if_fail (ds, FALSE);
1002 g_return_val_if_fail (ds->datasheet, FALSE);
1004 g_return_val_if_fail (idx < datasheet_get_column_cnt (ds->datasheet), FALSE);
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));
1013 g_signal_emit (ds, signals [CASE_CHANGED], 0, casenum);
1020 /* Resize the cases in the casefile, by inserting N_VALUES into every
1021 one of them at the position immediately preceeding WHERE.
1024 psppire_data_store_insert_values (PsppireDataStore *ds,
1025 gint n_values, gint where)
1027 g_return_val_if_fail (ds, FALSE);
1029 if ( n_values == 0 )
1032 g_assert (n_values > 0);
1034 if ( ! ds->datasheet )
1035 ds->datasheet = datasheet_create (NULL);
1038 union value *values = xcalloc (n_values, sizeof *values);
1039 datasheet_insert_columns (ds->datasheet, values, n_values, where);