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);
305 g_sheet_column_columns_changed (G_SHEET_COLUMN (store),
313 variable_changed_callback (GObject *obj, gint var_num, gpointer data)
315 PsppireDataStore *store = PSPPIRE_DATA_STORE (data);
318 g_sheet_column_columns_changed (G_SHEET_COLUMN (store),
322 g_sheet_model_range_changed (G_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);
355 g_sheet_column_columns_changed (G_SHEET_COLUMN (store),
359 g_sheet_model_columns_inserted (G_SHEET_MODEL (store), var_num, 1);
364 dict_size_change_callback (GObject *obj,
365 gint posn, gint adjustment, gpointer data)
367 PsppireDataStore *store = PSPPIRE_DATA_STORE (data);
369 const struct variable *v = psppire_dict_get_variable (store->dict, posn);
371 const gint new_val_width = value_cnt_from_width (var_get_width (v));
373 if ( adjustment > 0 )
374 psppire_data_store_insert_values (store, adjustment,
375 new_val_width - adjustment +
376 var_get_case_index(v));
382 * psppire_data_store_new:
383 * @dict: The dictionary for this data_store.
386 * Return value: a new #PsppireDataStore
389 psppire_data_store_new (PsppireDict *dict)
391 PsppireDataStore *retval;
393 retval = g_object_new (GTK_TYPE_DATA_STORE, NULL);
395 psppire_data_store_set_dictionary (retval, dict);
401 psppire_data_store_set_reader (PsppireDataStore *ds,
402 struct casereader *reader)
407 datasheet_destroy (ds->datasheet);
409 ds->datasheet = datasheet_create (reader);
411 g_sheet_model_range_changed (G_SHEET_MODEL (ds),
415 for (i = 0 ; i < n_dict_signals; ++i )
417 if ( ds->dict_handler_id [i] > 0)
419 g_signal_handler_unblock (ds->dict,
420 ds->dict_handler_id[i]);
424 g_signal_emit (ds, signals[BACKEND_CHANGED], 0);
429 * psppire_data_store_replace_set_dictionary:
430 * @data_store: The variable store
431 * @dict: The dictionary to set
433 * If a dictionary is already associated with the data-store, then it will be
437 psppire_data_store_set_dictionary (PsppireDataStore *data_store, PsppireDict *dict)
441 /* Disconnect any existing handlers */
442 if ( data_store->dict )
443 for (i = 0 ; i < n_dict_signals; ++i )
445 g_signal_handler_disconnect (data_store->dict,
446 data_store->dict_handler_id[i]);
449 data_store->dict = dict;
454 data_store->dict_handler_id [VARIABLE_INSERTED] =
455 g_signal_connect (dict, "variable-inserted",
456 G_CALLBACK (insert_variable_callback),
459 data_store->dict_handler_id [VARIABLE_DELETED] =
460 g_signal_connect (dict, "variable-deleted",
461 G_CALLBACK (delete_variable_callback),
464 data_store->dict_handler_id [VARIABLE_CHANGED] =
465 g_signal_connect (dict, "variable-changed",
466 G_CALLBACK (variable_changed_callback),
469 data_store->dict_handler_id [SIZE_CHANGED] =
470 g_signal_connect (dict, "dict-size-changed",
471 G_CALLBACK (dict_size_change_callback),
477 /* The entire model has changed */
478 g_sheet_model_range_changed (G_SHEET_MODEL (data_store), -1, -1, -1, -1);
481 g_sheet_column_columns_changed (G_SHEET_COLUMN (data_store), 0, -1);
484 if ( data_store->dict )
485 for (i = 0 ; i < n_dict_signals; ++i )
487 if ( data_store->dict_handler_id [i] > 0)
489 g_signal_handler_block (data_store->dict,
490 data_store->dict_handler_id[i]);
496 psppire_data_store_finalize (GObject *object)
500 (* parent_class->finalize) (object);
505 psppire_data_store_dispose (GObject *object)
507 PsppireDataStore *ds = PSPPIRE_DATA_STORE (object);
509 if (ds->dispose_has_run)
514 datasheet_destroy (ds->datasheet);
515 ds->datasheet = NULL;
519 (* parent_class->dispose) (object);
521 ds->dispose_has_run = TRUE;
526 /* Insert a blank case before POSN */
528 psppire_data_store_insert_new_case (PsppireDataStore *ds, casenumber posn)
533 g_return_val_if_fail (ds, FALSE);
535 val_cnt = datasheet_get_column_cnt (ds->datasheet) ;
537 g_return_val_if_fail (val_cnt > 0, FALSE);
539 g_return_val_if_fail (posn <= psppire_data_store_get_case_count (ds), FALSE);
541 case_create (&cc, val_cnt);
543 memset ( case_data_rw_idx (&cc, 0), 0, val_cnt * MAX_SHORT_STRING);
545 for (v = 0 ; v < psppire_dict_get_var_cnt (ds->dict) ; ++v)
547 const struct variable *pv = psppire_dict_get_variable (ds->dict, v);
548 if ( var_is_alpha (pv))
551 case_data_rw (&cc, pv)->f = SYSMIS;
554 result = psppire_data_store_insert_case (ds, &cc, posn);
563 psppire_data_store_get_string (PsppireDataStore *store, glong row, glong column)
567 const struct fmt_spec *fp ;
568 const struct variable *pv ;
572 g_return_val_if_fail (store->dict, NULL);
573 g_return_val_if_fail (store->datasheet, NULL);
575 if (column >= psppire_dict_get_var_cnt (store->dict))
578 if ( row >= psppire_data_store_get_case_count (store))
581 pv = psppire_dict_get_variable (store->dict, column);
585 idx = var_get_case_index (pv);
589 v = psppire_data_store_get_value (store, row, idx, NULL,
592 g_return_val_if_fail (v, NULL);
594 if ( store->show_labels)
596 const gchar *label = var_lookup_value_label (pv, v);
600 return pspp_locale_to_utf8 (label, -1, 0);
604 fp = var_get_write_format (pv);
606 s = g_string_sized_new (fp->w + 1);
607 g_string_set_size (s, fp->w);
609 memset (s->str, 0, fp->w);
611 g_assert (fp->w == s->len);
613 /* Converts binary value V into printable form in the exactly
614 FP->W character in buffer S according to format specification
615 FP. No null terminator is appended to the buffer. */
616 data_out (v, fp, s->str);
618 text = pspp_locale_to_utf8 (s->str, fp->w, 0);
619 g_string_free (s, TRUE);
629 psppire_data_store_clear_datum (GSheetModel *model,
630 glong row, glong col)
632 PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
635 const struct variable *pv = psppire_dict_get_variable (store->dict, col);
637 const gint index = var_get_case_index (pv) ;
639 if ( var_is_numeric (pv))
642 memcpy (v.s, "", MAX_SHORT_STRING);
644 psppire_data_store_set_value (store, row, index, &v,
651 /* Attempts to update that part of the variable store which corresponds
652 to ROW, COL with the value TEXT.
653 Returns true if anything was updated, false otherwise.
656 psppire_data_store_set_string (PsppireDataStore *store,
657 const gchar *text, glong row, glong col)
660 const struct variable *pv = psppire_dict_get_variable (store->dict, col);
661 g_return_val_if_fail (pv, FALSE);
663 n_cases = psppire_data_store_get_case_count (store);
669 psppire_data_store_insert_new_case (store, row);
671 psppire_data_store_data_in (store, row,
672 var_get_case_index (pv), ss_cstr (text),
673 var_get_write_format (pv));
681 psppire_data_store_show_labels (PsppireDataStore *store, gboolean show_labels)
683 g_return_if_fail (store);
684 g_return_if_fail (PSPPIRE_IS_DATA_STORE (store));
686 store->show_labels = show_labels;
688 g_sheet_model_range_changed (G_SHEET_MODEL (store),
694 psppire_data_store_clear (PsppireDataStore *ds)
696 datasheet_destroy (ds->datasheet);
697 ds->datasheet = NULL;
699 psppire_dict_clear (ds->dict);
701 g_signal_emit (ds, signals [CASES_DELETED], 0, 0, -1);
706 /* Return a casereader made from this datastore */
708 psppire_data_store_get_reader (PsppireDataStore *ds)
711 struct casereader *reader ;
714 for (i = 0 ; i < n_dict_signals; ++i )
716 g_signal_handler_block (ds->dict,
717 ds->dict_handler_id[i]);
720 reader = datasheet_make_reader (ds->datasheet);
722 /* We must not reference this again */
723 ds->datasheet = NULL;
730 /* Column related funcs */
733 static const gchar null_var_name[]=N_("var");
737 /* Row related funcs */
740 get_row_button_label (const GSheetModel *model, gint unit)
742 gchar *s = g_strdup_printf (_("%d"), unit + FIRST_CASE_NUMBER);
744 gchar *text = pspp_locale_to_utf8 (s, -1, 0);
753 get_row_sensitivity (const GSheetModel *model, gint unit)
755 PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
757 return (unit < psppire_data_store_get_case_count (ds));
763 /* Column related stuff */
766 get_column_subtitle (const GSheetModel *model, gint col)
769 const struct variable *v ;
770 PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
772 if ( col >= psppire_dict_get_var_cnt (ds->dict) )
775 v = psppire_dict_get_variable (ds->dict, col);
777 if ( ! var_has_label (v))
780 text = pspp_locale_to_utf8 (var_get_label (v), -1, 0);
786 get_column_button_label (const GSheetModel *model, gint col)
789 struct variable *pv ;
790 PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
792 if ( col >= psppire_dict_get_var_cnt (ds->dict) )
793 return g_locale_to_utf8 (null_var_name, -1, 0, 0, 0);
795 pv = psppire_dict_get_variable (ds->dict, col);
797 text = pspp_locale_to_utf8 (var_get_name (pv), -1, 0);
803 get_column_sensitivity (const GSheetModel *model, gint col)
805 PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
807 return (col < psppire_dict_get_var_cnt (ds->dict));
812 static GtkJustification
813 get_column_justification (const GSheetModel *model, gint col)
815 PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
816 const struct variable *pv ;
818 if ( col >= psppire_dict_get_var_cnt (ds->dict) )
819 return GTK_JUSTIFY_LEFT;
821 pv = psppire_dict_get_variable (ds->dict, col);
823 return (var_get_alignment (pv) == ALIGN_LEFT ? GTK_JUSTIFY_LEFT
824 : var_get_alignment (pv) == ALIGN_RIGHT ? GTK_JUSTIFY_RIGHT
825 : GTK_JUSTIFY_CENTER);
833 /* Fills C with the CASENUMth case.
834 Returns true on success, false otherwise.
837 psppire_data_store_get_case (const PsppireDataStore *ds,
841 g_return_val_if_fail (ds, FALSE);
842 g_return_val_if_fail (ds->datasheet, FALSE);
844 return datasheet_get_row (ds->datasheet, casenum, c);
849 psppire_data_store_delete_cases (PsppireDataStore *ds, casenumber first,
852 g_return_val_if_fail (ds, FALSE);
853 g_return_val_if_fail (ds->datasheet, FALSE);
855 g_return_val_if_fail (first + n_cases <=
856 psppire_data_store_get_case_count (ds), FALSE);
859 datasheet_delete_rows (ds->datasheet, first, n_cases);
861 g_signal_emit (ds, signals [CASES_DELETED], 0, first, n_cases);
868 /* Insert case CC into the case file before POSN */
870 psppire_data_store_insert_case (PsppireDataStore *ds,
877 g_return_val_if_fail (ds, FALSE);
878 g_return_val_if_fail (ds->datasheet, FALSE);
880 case_clone (&tmp, cc);
881 result = datasheet_insert_rows (ds->datasheet, posn, &tmp, 1);
884 g_signal_emit (ds, signals [CASE_INSERTED], 0, posn);
886 g_warning ("Cannot insert case at position %ld\n", posn);
892 /* Copies the IDXth value from case CASENUM into VALUE.
893 If VALUE is null, then memory is allocated is allocated with
894 malloc. Returns the value if successful, NULL on failure. */
896 psppire_data_store_get_value (const PsppireDataStore *ds,
897 casenumber casenum, size_t idx,
898 union value *value, int width)
902 g_return_val_if_fail (ds, false);
903 g_return_val_if_fail (ds->datasheet, false);
905 g_return_val_if_fail (idx < datasheet_get_column_cnt (ds->datasheet), false);
909 value = xnmalloc (value_cnt_from_width (width), sizeof *value);
914 if (!datasheet_get_value (ds->datasheet, casenum, idx, value, width))
925 /* Set the IDXth value of case C to V.
926 Returns true if successful, false on I/O error. */
928 psppire_data_store_set_value (PsppireDataStore *ds, casenumber casenum,
929 gint idx, union value *v, gint width)
933 g_return_val_if_fail (ds, FALSE);
934 g_return_val_if_fail (ds->datasheet, FALSE);
936 g_return_val_if_fail (idx < datasheet_get_column_cnt (ds->datasheet), FALSE);
938 ok = datasheet_put_value (ds->datasheet, casenum, idx, v, width);
940 g_signal_emit (ds, signals [CASE_CHANGED], 0, casenum);
947 /* Set the IDXth value of case C using D_IN */
949 psppire_data_store_data_in (PsppireDataStore *ds, casenumber casenum, gint idx,
950 struct substring input, const struct fmt_spec *fmt)
952 union value *value = NULL;
956 g_return_val_if_fail (ds, FALSE);
957 g_return_val_if_fail (ds->datasheet, FALSE);
959 g_return_val_if_fail (idx < datasheet_get_column_cnt (ds->datasheet), FALSE);
961 width = fmt_var_width (fmt);
962 value = xmalloca (value_cnt_from_width (width) * sizeof *value);
963 ok = (datasheet_get_value (ds->datasheet, casenum, idx, value, width)
964 && data_in (input, LEGACY_NATIVE, fmt->type, 0, 0, 0, value, width)
965 && datasheet_put_value (ds->datasheet, casenum, idx, value, width));
968 g_signal_emit (ds, signals [CASE_CHANGED], 0, casenum);
975 /* Resize the cases in the casefile, by inserting N_VALUES into every
976 one of them at the position immediately preceeding WHERE.
979 psppire_data_store_insert_values (PsppireDataStore *ds,
980 gint n_values, gint where)
982 g_return_val_if_fail (ds, FALSE);
987 g_assert (n_values > 0);
989 if ( ! ds->datasheet )
990 ds->datasheet = datasheet_create (NULL);
993 union value *values = xcalloc (n_values, sizeof *values);
994 datasheet_insert_columns (ds->datasheet, values, n_values, where);