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>
29 #include <gtksheet/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 (GSheetModelIface *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 (GSheetModel *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,
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 GSheetModel *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 GSheetModel *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 GSheetModel *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 (GSheetModel *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 GSheetModel *model, gint col);
260 static gchar * get_column_button_label (const GSheetModel *model, gint col);
261 static gboolean get_column_sensitivity (const GSheetModel *model, gint col);
262 static GtkJustification get_column_justification (const GSheetModel *model, gint col);
264 static gchar * get_row_button_label (const GSheetModel *model, gint row);
265 static gboolean get_row_sensitivity (const GSheetModel *model, gint row);
269 psppire_data_store_sheet_model_init (GSheetModelIface *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_cell_border = 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;
293 A callback which occurs after a variable has been deleted.
296 delete_variable_callback (GObject *obj, gint dict_index,
297 gint case_index, gint val_cnt,
300 PsppireDataStore *store = PSPPIRE_DATA_STORE (data);
303 g_sheet_model_columns_deleted (G_SHEET_MODEL (store), dict_index, 1);
307 g_sheet_column_columns_changed (G_SHEET_COLUMN (store),
315 variable_changed_callback (GObject *obj, gint var_num, gpointer data)
317 PsppireDataStore *store = PSPPIRE_DATA_STORE (data);
320 g_sheet_column_columns_changed (G_SHEET_COLUMN (store),
324 g_sheet_model_range_changed (G_SHEET_MODEL (store),
331 insert_variable_callback (GObject *obj, gint var_num, gpointer data)
333 PsppireDataStore *store;
336 g_return_if_fail (data);
338 store = PSPPIRE_DATA_STORE (data);
342 struct variable *variable =
343 psppire_dict_get_variable (store->dict, var_num);
345 g_assert (variable != NULL);
347 posn = var_get_case_index (variable);
354 psppire_data_store_insert_values (store, 1, posn);
358 g_sheet_column_columns_changed (G_SHEET_COLUMN (store),
362 g_sheet_model_columns_inserted (G_SHEET_MODEL (store), var_num, 1);
367 dict_size_change_callback (GObject *obj,
368 gint posn, gint adjustment, gpointer data)
370 PsppireDataStore *store = PSPPIRE_DATA_STORE (data);
372 const struct variable *v = psppire_dict_get_variable (store->dict, posn);
374 const gint new_val_width = value_cnt_from_width (var_get_width (v));
376 if ( adjustment > 0 )
377 psppire_data_store_insert_values (store, adjustment,
378 new_val_width - adjustment +
379 var_get_case_index(v));
385 * psppire_data_store_new:
386 * @dict: The dictionary for this data_store.
389 * Return value: a new #PsppireDataStore
392 psppire_data_store_new (PsppireDict *dict)
394 PsppireDataStore *retval;
396 retval = g_object_new (GTK_TYPE_DATA_STORE, NULL);
398 psppire_data_store_set_dictionary (retval, dict);
404 psppire_data_store_set_reader (PsppireDataStore *ds,
405 struct casereader *reader)
410 datasheet_destroy (ds->datasheet);
412 ds->datasheet = datasheet_create (reader);
414 g_sheet_model_range_changed (G_SHEET_MODEL (ds),
418 for (i = 0 ; i < n_dict_signals; ++i )
420 if ( ds->dict_handler_id [i] > 0)
422 g_signal_handler_unblock (ds->dict,
423 ds->dict_handler_id[i]);
427 g_signal_emit (ds, signals[BACKEND_CHANGED], 0);
432 * psppire_data_store_replace_set_dictionary:
433 * @data_store: The variable store
434 * @dict: The dictionary to set
436 * If a dictionary is already associated with the data-store, then it will be
440 psppire_data_store_set_dictionary (PsppireDataStore *data_store, PsppireDict *dict)
444 /* Disconnect any existing handlers */
445 if ( data_store->dict )
446 for (i = 0 ; i < n_dict_signals; ++i )
448 g_signal_handler_disconnect (data_store->dict,
449 data_store->dict_handler_id[i]);
452 data_store->dict = dict;
457 data_store->dict_handler_id [VARIABLE_INSERTED] =
458 g_signal_connect (dict, "variable-inserted",
459 G_CALLBACK (insert_variable_callback),
462 data_store->dict_handler_id [VARIABLE_DELETED] =
463 g_signal_connect (dict, "variable-deleted",
464 G_CALLBACK (delete_variable_callback),
467 data_store->dict_handler_id [VARIABLE_CHANGED] =
468 g_signal_connect (dict, "variable-changed",
469 G_CALLBACK (variable_changed_callback),
472 data_store->dict_handler_id [SIZE_CHANGED] =
473 g_signal_connect (dict, "dict-size-changed",
474 G_CALLBACK (dict_size_change_callback),
480 /* The entire model has changed */
481 g_sheet_model_range_changed (G_SHEET_MODEL (data_store), -1, -1, -1, -1);
484 g_sheet_column_columns_changed (G_SHEET_COLUMN (data_store), 0, -1);
487 if ( data_store->dict )
488 for (i = 0 ; i < n_dict_signals; ++i )
490 if ( data_store->dict_handler_id [i] > 0)
492 g_signal_handler_block (data_store->dict,
493 data_store->dict_handler_id[i]);
499 psppire_data_store_finalize (GObject *object)
503 (* parent_class->finalize) (object);
508 psppire_data_store_dispose (GObject *object)
510 PsppireDataStore *ds = PSPPIRE_DATA_STORE (object);
512 if (ds->dispose_has_run)
517 datasheet_destroy (ds->datasheet);
518 ds->datasheet = NULL;
522 (* parent_class->dispose) (object);
524 ds->dispose_has_run = TRUE;
529 /* Insert a blank case before POSN */
531 psppire_data_store_insert_new_case (PsppireDataStore *ds, casenumber posn)
536 g_return_val_if_fail (ds, FALSE);
538 val_cnt = datasheet_get_column_cnt (ds->datasheet) ;
540 g_return_val_if_fail (val_cnt > 0, FALSE);
542 g_return_val_if_fail (posn <= psppire_data_store_get_case_count (ds), FALSE);
544 case_create (&cc, val_cnt);
546 memset ( case_data_rw_idx (&cc, 0), 0, val_cnt * MAX_SHORT_STRING);
548 for (v = 0 ; v < psppire_dict_get_var_cnt (ds->dict) ; ++v)
550 const struct variable *pv = psppire_dict_get_variable (ds->dict, v);
551 if ( var_is_alpha (pv))
554 case_data_rw (&cc, pv)->f = SYSMIS;
557 result = psppire_data_store_insert_case (ds, &cc, posn);
566 psppire_data_store_get_string (PsppireDataStore *store, glong row, glong column)
570 const struct fmt_spec *fp ;
571 const struct variable *pv ;
575 g_return_val_if_fail (store->dict, NULL);
576 g_return_val_if_fail (store->datasheet, NULL);
578 if (column >= psppire_dict_get_var_cnt (store->dict))
581 if ( row >= psppire_data_store_get_case_count (store))
584 pv = psppire_dict_get_variable (store->dict, column);
588 idx = var_get_case_index (pv);
592 v = psppire_data_store_get_value (store, row, idx, NULL,
595 g_return_val_if_fail (v, NULL);
597 if ( store->show_labels)
599 const gchar *label = var_lookup_value_label (pv, v);
603 return pspp_locale_to_utf8 (label, -1, 0);
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 = pspp_locale_to_utf8 (s->str, fp->w, 0);
622 g_string_free (s, TRUE);
632 psppire_data_store_clear_datum (GSheetModel *model,
633 glong row, glong col)
635 PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
638 const struct variable *pv = psppire_dict_get_variable (store->dict, col);
640 const gint index = var_get_case_index (pv) ;
642 if ( var_is_numeric (pv))
645 memcpy (v.s, "", MAX_SHORT_STRING);
647 psppire_data_store_set_value (store, row, index, &v,
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);
664 g_return_val_if_fail (pv, FALSE);
666 n_cases = psppire_data_store_get_case_count (store);
672 psppire_data_store_insert_new_case (store, row);
674 psppire_data_store_data_in (store, row,
675 var_get_case_index (pv), ss_cstr (text),
676 var_get_write_format (pv));
684 psppire_data_store_show_labels (PsppireDataStore *store, gboolean show_labels)
686 g_return_if_fail (store);
687 g_return_if_fail (PSPPIRE_IS_DATA_STORE (store));
689 store->show_labels = show_labels;
691 g_sheet_model_range_changed (G_SHEET_MODEL (store),
697 psppire_data_store_clear (PsppireDataStore *ds)
699 datasheet_destroy (ds->datasheet);
700 ds->datasheet = NULL;
702 psppire_dict_clear (ds->dict);
704 g_signal_emit (ds, signals [CASES_DELETED], 0, 0, -1);
709 /* Return a casereader made from this datastore */
711 psppire_data_store_get_reader (PsppireDataStore *ds)
714 struct casereader *reader ;
717 for (i = 0 ; i < n_dict_signals; ++i )
719 g_signal_handler_block (ds->dict,
720 ds->dict_handler_id[i]);
723 reader = datasheet_make_reader (ds->datasheet);
725 /* We must not reference this again */
726 ds->datasheet = NULL;
733 /* Column related funcs */
736 static const gchar null_var_name[]=N_("var");
740 /* Row related funcs */
743 get_row_button_label (const GSheetModel *model, gint unit)
745 gchar *s = g_strdup_printf (_("%d"), unit + FIRST_CASE_NUMBER);
747 gchar *text = pspp_locale_to_utf8 (s, -1, 0);
756 get_row_sensitivity (const GSheetModel *model, gint unit)
758 PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
760 return (unit < psppire_data_store_get_case_count (ds));
766 /* Column related stuff */
769 get_column_subtitle (const GSheetModel *model, gint col)
772 const struct variable *v ;
773 PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
775 if ( col >= psppire_dict_get_var_cnt (ds->dict) )
778 v = psppire_dict_get_variable (ds->dict, col);
780 if ( ! var_has_label (v))
783 text = pspp_locale_to_utf8 (var_get_label (v), -1, 0);
789 get_column_button_label (const GSheetModel *model, gint col)
792 struct variable *pv ;
793 PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
795 if ( col >= psppire_dict_get_var_cnt (ds->dict) )
796 return g_locale_to_utf8 (null_var_name, -1, 0, 0, 0);
798 pv = psppire_dict_get_variable (ds->dict, col);
800 text = pspp_locale_to_utf8 (var_get_name (pv), -1, 0);
806 get_column_sensitivity (const GSheetModel *model, gint col)
808 PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
810 return (col < psppire_dict_get_var_cnt (ds->dict));
815 static GtkJustification
816 get_column_justification (const GSheetModel *model, gint col)
818 PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
819 const struct variable *pv ;
821 if ( col >= psppire_dict_get_var_cnt (ds->dict) )
822 return GTK_JUSTIFY_LEFT;
824 pv = psppire_dict_get_variable (ds->dict, col);
826 return (var_get_alignment (pv) == ALIGN_LEFT ? GTK_JUSTIFY_LEFT
827 : var_get_alignment (pv) == ALIGN_RIGHT ? GTK_JUSTIFY_RIGHT
828 : GTK_JUSTIFY_CENTER);
836 /* Fills C with the CASENUMth case.
837 Returns true on success, false otherwise.
840 psppire_data_store_get_case (const PsppireDataStore *ds,
844 g_return_val_if_fail (ds, FALSE);
845 g_return_val_if_fail (ds->datasheet, FALSE);
847 return datasheet_get_row (ds->datasheet, casenum, c);
852 psppire_data_store_delete_cases (PsppireDataStore *ds, casenumber first,
855 g_return_val_if_fail (ds, FALSE);
856 g_return_val_if_fail (ds->datasheet, FALSE);
858 g_return_val_if_fail (first + n_cases <=
859 psppire_data_store_get_case_count (ds), FALSE);
862 datasheet_delete_rows (ds->datasheet, first, n_cases);
864 g_signal_emit (ds, signals [CASES_DELETED], 0, first, n_cases);
865 g_sheet_model_rows_deleted (G_SHEET_MODEL (ds), first, n_cases);
872 /* Insert case CC into the case file before POSN */
874 psppire_data_store_insert_case (PsppireDataStore *ds,
881 g_return_val_if_fail (ds, FALSE);
882 g_return_val_if_fail (ds->datasheet, FALSE);
884 case_clone (&tmp, cc);
885 result = datasheet_insert_rows (ds->datasheet, posn, &tmp, 1);
889 g_signal_emit (ds, signals [CASE_INSERTED], 0, posn);
890 g_sheet_model_rows_inserted (G_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);
954 /* Set the IDXth value of case C using D_IN */
956 psppire_data_store_data_in (PsppireDataStore *ds, casenumber casenum, gint idx,
957 struct substring input, const struct fmt_spec *fmt)
959 union value *value = NULL;
963 g_return_val_if_fail (ds, FALSE);
964 g_return_val_if_fail (ds->datasheet, FALSE);
966 g_return_val_if_fail (idx < datasheet_get_column_cnt (ds->datasheet), FALSE);
968 width = fmt_var_width (fmt);
969 value = xmalloca (value_cnt_from_width (width) * sizeof *value);
970 ok = (datasheet_get_value (ds->datasheet, casenum, idx, value, width)
971 && data_in (input, LEGACY_NATIVE, fmt->type, 0, 0, 0, value, width)
972 && datasheet_put_value (ds->datasheet, casenum, idx, value, width));
975 g_signal_emit (ds, signals [CASE_CHANGED], 0, casenum);
982 /* Resize the cases in the casefile, by inserting N_VALUES into every
983 one of them at the position immediately preceeding WHERE.
986 psppire_data_store_insert_values (PsppireDataStore *ds,
987 gint n_values, gint where)
989 g_return_val_if_fail (ds, FALSE);
994 g_assert (n_values > 0);
996 if ( ! ds->datasheet )
997 ds->datasheet = datasheet_create (NULL);
1000 union value *values = xcalloc (n_values, sizeof *values);
1001 datasheet_insert_columns (ds->datasheet, values, n_values, where);