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>
46 static void psppire_data_store_init (PsppireDataStore *data_store);
47 static void psppire_data_store_class_init (PsppireDataStoreClass *class);
48 static void psppire_data_store_sheet_model_init (GSheetModelIface *iface);
50 static void psppire_data_store_finalize (GObject *object);
51 static void psppire_data_store_dispose (GObject *object);
53 static gboolean psppire_data_store_clear_datum (GSheetModel *model,
54 glong row, glong column);
57 static gboolean psppire_data_store_insert_case (PsppireDataStore *ds,
62 static gboolean psppire_data_store_data_in (PsppireDataStore *ds,
63 casenumber casenum, gint idx,
64 struct substring input,
65 const struct fmt_spec *fmt);
69 static GObjectClass *parent_class = NULL;
76 static guint signals [n_SIGNALS];
80 psppire_data_store_get_type (void)
82 static GType data_store_type = 0;
86 static const GTypeInfo data_store_info =
88 sizeof (PsppireDataStoreClass),
90 NULL, /* base_finalize */
91 (GClassInitFunc) psppire_data_store_class_init,
92 NULL, /* class_finalize */
93 NULL, /* class_data */
94 sizeof (PsppireDataStore),
96 (GInstanceInitFunc) psppire_data_store_init,
99 static const GInterfaceInfo sheet_model_info =
101 (GInterfaceInitFunc) psppire_data_store_sheet_model_init,
107 data_store_type = g_type_register_static (G_TYPE_OBJECT, "PsppireDataStore",
108 &data_store_info, 0);
110 g_type_add_interface_static (data_store_type,
116 return data_store_type;
121 psppire_data_store_class_init (PsppireDataStoreClass *class)
123 GObjectClass *object_class;
125 parent_class = g_type_class_peek_parent (class);
126 object_class = (GObjectClass*) class;
128 object_class->finalize = psppire_data_store_finalize;
129 object_class->dispose = psppire_data_store_dispose;
131 signals [BACKEND_CHANGED] =
132 g_signal_new ("backend-changed",
133 G_TYPE_FROM_CLASS (class),
137 g_cclosure_marshal_VOID__VOID,
145 psppire_data_store_insert_values (PsppireDataStore *ds,
146 gint n_values, gint where);
149 psppire_data_store_get_value (const PsppireDataStore *ds,
150 casenumber casenum, size_t idx,
151 union value *value, int width);
155 psppire_data_store_set_value (PsppireDataStore *ds, casenumber casenum,
156 gint idx, union value *v, gint width);
162 psppire_data_store_get_var_count (const GSheetModel *model)
164 const PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
166 return psppire_dict_get_var_cnt (store->dict);
170 psppire_data_store_get_case_count (const PsppireDataStore *store)
172 return datasheet_get_row_cnt (store->datasheet);
176 psppire_data_store_get_value_count (const PsppireDataStore *store)
178 return psppire_dict_get_value_cnt (store->dict);
182 psppire_data_store_get_case_count_wrapper (const GSheetModel *model)
184 const PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
185 return psppire_data_store_get_case_count (store);
189 psppire_data_store_init (PsppireDataStore *data_store)
191 data_store->dict = 0;
192 data_store->datasheet = NULL;
193 data_store->dispose_has_run = FALSE;
196 static inline gchar *
197 psppire_data_store_get_string_wrapper (const GSheetModel *model, glong row,
200 return psppire_data_store_get_string (PSPPIRE_DATA_STORE (model), row, column);
204 static inline gboolean
205 psppire_data_store_set_string_wrapper (GSheetModel *model,
207 glong row, glong column)
209 return psppire_data_store_set_string (PSPPIRE_DATA_STORE (model), text,
215 static gchar * get_column_subtitle (const GSheetModel *model, gint col);
216 static gchar * get_column_button_label (const GSheetModel *model, gint col);
217 static gboolean get_column_sensitivity (const GSheetModel *model, gint col);
218 static GtkJustification get_column_justification (const GSheetModel *model, gint col);
220 static gchar * get_row_button_label (const GSheetModel *model, gint row);
221 static gboolean get_row_sensitivity (const GSheetModel *model, gint row);
225 psppire_data_store_sheet_model_init (GSheetModelIface *iface)
227 iface->free_strings = TRUE;
228 iface->get_string = psppire_data_store_get_string_wrapper;
229 iface->set_string = psppire_data_store_set_string_wrapper;
230 iface->clear_datum = psppire_data_store_clear_datum;
231 iface->is_editable = NULL;
232 iface->get_foreground = NULL;
233 iface->get_background = NULL;
234 iface->get_cell_border = NULL;
235 iface->get_column_count = psppire_data_store_get_var_count;
236 iface->get_row_count = psppire_data_store_get_case_count_wrapper;
238 iface->get_column_subtitle = get_column_subtitle;
239 iface->get_column_title = get_column_button_label;
240 iface->get_column_sensitivity = get_column_sensitivity;
241 iface->get_column_justification = get_column_justification;
243 iface->get_row_title = get_row_button_label;
244 iface->get_row_sensitivity = get_row_sensitivity;
248 delete_cases_callback (GtkWidget *w,
249 casenumber first, casenumber n_cases, gpointer data)
251 PsppireDataStore *store ;
253 g_return_if_fail (data);
255 store = PSPPIRE_DATA_STORE (data);
257 g_assert (first >= 0);
259 g_sheet_model_rows_deleted (G_SHEET_MODEL (store), first, n_cases);
264 insert_case_callback (GtkWidget *w, casenumber casenum, gpointer data)
266 PsppireDataStore *store = PSPPIRE_DATA_STORE (data);
268 g_return_if_fail (data);
270 g_print ("%s\n", __FUNCTION__);
272 g_sheet_model_range_changed (G_SHEET_MODEL (store),
274 psppire_data_store_get_case_count (store),
277 g_sheet_model_rows_inserted (G_SHEET_MODEL (store), casenum, 1);
282 changed_case_callback (GtkWidget *w, gint casenum, gpointer data)
284 PsppireDataStore *store ;
285 g_return_if_fail (data);
287 store = PSPPIRE_DATA_STORE (data);
289 g_sheet_model_range_changed (G_SHEET_MODEL (store),
296 A callback which occurs after a variable has been deleted.
299 delete_variable_callback (GObject *obj, gint dict_index,
300 gint case_index, gint val_cnt,
303 PsppireDataStore *store = PSPPIRE_DATA_STORE (data);
306 g_sheet_model_columns_deleted (G_SHEET_MODEL (store), dict_index, 1);
308 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);
357 g_sheet_column_columns_changed (G_SHEET_COLUMN (store),
361 g_sheet_model_columns_inserted (G_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)
408 ds->datasheet = datasheet_create (reader);
410 g_sheet_model_range_changed (G_SHEET_MODEL (ds),
414 for (i = 0 ; i < n_cf_signals ; ++i )
416 if ( ds->cf_handler_id [i] > 0 )
417 g_signal_handler_disconnect (ds->case_file,
418 ds->cf_handler_id[i]);
423 for (i = 0 ; i < n_dict_signals; ++i )
425 if ( ds->dict_handler_id [i] > 0)
427 g_signal_handler_unblock (ds->dict,
428 ds->dict_handler_id[i]);
433 ds->cf_handler_id [CASES_DELETED] =
434 g_signal_connect (ds->case_file, "cases-deleted",
435 G_CALLBACK (delete_cases_callback),
438 ds->cf_handler_id [CASE_INSERTED] =
439 g_signal_connect (ds->case_file, "case-inserted",
440 G_CALLBACK (insert_case_callback),
443 ds->cf_handler_id [CASE_CHANGED] =
444 g_signal_connect (ds->case_file, "case-changed",
445 G_CALLBACK (changed_case_callback),
449 g_signal_emit (ds, signals[BACKEND_CHANGED], 0);
454 * psppire_data_store_replace_set_dictionary:
455 * @data_store: The variable store
456 * @dict: The dictionary to set
458 * If a dictionary is already associated with the data-store, then it will be
462 psppire_data_store_set_dictionary (PsppireDataStore *data_store, PsppireDict *dict)
466 /* Disconnect any existing handlers */
467 if ( data_store->dict )
468 for (i = 0 ; i < n_dict_signals; ++i )
470 g_signal_handler_disconnect (data_store->dict,
471 data_store->dict_handler_id[i]);
474 data_store->dict = dict;
479 data_store->dict_handler_id [VARIABLE_INSERTED] =
480 g_signal_connect (dict, "variable-inserted",
481 G_CALLBACK (insert_variable_callback),
484 data_store->dict_handler_id [VARIABLE_DELETED] =
485 g_signal_connect (dict, "variable-deleted",
486 G_CALLBACK (delete_variable_callback),
489 data_store->dict_handler_id [VARIABLE_CHANGED] =
490 g_signal_connect (dict, "variable-changed",
491 G_CALLBACK (variable_changed_callback),
494 data_store->dict_handler_id [SIZE_CHANGED] =
495 g_signal_connect (dict, "dict-size-changed",
496 G_CALLBACK (dict_size_change_callback),
502 /* The entire model has changed */
503 g_sheet_model_range_changed (G_SHEET_MODEL (data_store), -1, -1, -1, -1);
506 g_sheet_column_columns_changed (G_SHEET_COLUMN (data_store), 0, -1);
509 if ( data_store->dict )
510 for (i = 0 ; i < n_dict_signals; ++i )
512 if ( data_store->dict_handler_id [i] > 0)
514 g_signal_handler_block (data_store->dict,
515 data_store->dict_handler_id[i]);
521 psppire_data_store_finalize (GObject *object)
525 (* parent_class->finalize) (object);
530 psppire_data_store_dispose (GObject *object)
532 PsppireDataStore *ds = PSPPIRE_DATA_STORE (object);
534 if (ds->dispose_has_run)
539 datasheet_destroy (ds->datasheet);
540 ds->datasheet = NULL;
544 (* parent_class->dispose) (object);
546 ds->dispose_has_run = TRUE;
551 /* Insert a blank case before POSN */
553 psppire_data_store_insert_new_case (PsppireDataStore *ds, casenumber posn)
558 g_return_val_if_fail (ds, FALSE);
560 val_cnt = datasheet_get_column_cnt (ds->datasheet) ;
562 g_return_val_if_fail (val_cnt > 0, FALSE);
564 g_return_val_if_fail (posn <= psppire_data_store_get_case_count (ds), FALSE);
566 case_create (&cc, val_cnt);
568 memset ( case_data_rw_idx (&cc, 0), 0, val_cnt * MAX_SHORT_STRING);
570 for (v = 0 ; v < psppire_dict_get_var_cnt (ds->dict) ; ++v)
572 const struct variable *pv = psppire_dict_get_variable (ds->dict, v);
573 if ( var_is_alpha (pv))
576 case_data_rw (&cc, pv)->f = SYSMIS;
579 result = psppire_data_store_insert_case (ds, &cc, posn);
588 psppire_data_store_get_string (PsppireDataStore *store, glong row, glong column)
592 const struct fmt_spec *fp ;
593 const struct variable *pv ;
597 g_return_val_if_fail (store->dict, NULL);
598 g_return_val_if_fail (store->datasheet, NULL);
600 if (column >= psppire_dict_get_var_cnt (store->dict))
603 if ( row >= psppire_data_store_get_case_count (store))
606 pv = psppire_dict_get_variable (store->dict, column);
610 idx = var_get_case_index (pv);
614 v = psppire_data_store_get_value (store, row, idx, NULL,
617 g_return_val_if_fail (v, NULL);
619 if ( store->show_labels)
621 const gchar *label = var_lookup_value_label (pv, v);
625 return pspp_locale_to_utf8 (label, -1, 0);
629 fp = var_get_write_format (pv);
631 s = g_string_sized_new (fp->w + 1);
632 g_string_set_size (s, fp->w);
634 memset (s->str, 0, fp->w);
636 g_assert (fp->w == s->len);
638 /* Converts binary value V into printable form in the exactly
639 FP->W character in buffer S according to format specification
640 FP. No null terminator is appended to the buffer. */
641 data_out (v, fp, s->str);
643 text = pspp_locale_to_utf8 (s->str, fp->w, 0);
644 g_string_free (s, TRUE);
654 psppire_data_store_clear_datum (GSheetModel *model,
655 glong row, glong col)
657 PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
660 const struct variable *pv = psppire_dict_get_variable (store->dict, col);
662 const gint index = var_get_case_index (pv) ;
664 if ( var_is_numeric (pv))
667 memcpy (v.s, "", MAX_SHORT_STRING);
669 psppire_data_store_set_value (store, row, index, &v,
676 /* Attempts to update that part of the variable store which corresponds
677 to ROW, COL with the value TEXT.
678 Returns true if anything was updated, false otherwise.
681 psppire_data_store_set_string (PsppireDataStore *store,
682 const gchar *text, glong row, glong col)
685 const struct variable *pv = psppire_dict_get_variable (store->dict, col);
686 g_return_val_if_fail (pv, FALSE);
688 n_cases = psppire_data_store_get_case_count (store);
694 psppire_data_store_insert_new_case (store, row);
696 psppire_data_store_data_in (store, row,
697 var_get_case_index (pv), ss_cstr (text),
698 var_get_write_format (pv));
706 psppire_data_store_show_labels (PsppireDataStore *store, gboolean show_labels)
708 g_return_if_fail (store);
709 g_return_if_fail (PSPPIRE_IS_DATA_STORE (store));
711 store->show_labels = show_labels;
713 g_sheet_model_range_changed (G_SHEET_MODEL (store),
719 psppire_data_store_clear (PsppireDataStore *ds)
721 datasheet_destroy (ds->datasheet);
722 ds->datasheet = NULL;
724 psppire_dict_clear (ds->dict);
726 g_signal_emit (ds, signals [CASES_DELETED], 0, 0, -1);
731 /* Return a casereader made from this datastore */
733 psppire_data_store_get_reader (PsppireDataStore *ds)
736 struct casereader *reader ;
739 for (i = 0 ; i < n_cf_signals ; ++i )
741 g_signal_handler_disconnect (ds->case_file, ds->cf_handler_id[i]);
742 ds->cf_handler_id[i] = 0 ;
747 for (i = 0 ; i < n_dict_signals; ++i )
749 g_signal_handler_block (ds->dict,
750 ds->dict_handler_id[i]);
753 return datasheet_make_reader (ds->datasheet);
758 /* Column related funcs */
761 static const gchar null_var_name[]=N_("var");
765 /* Row related funcs */
768 get_row_button_label (const GSheetModel *model, gint unit)
770 gchar *s = g_strdup_printf (_("%d"), unit + FIRST_CASE_NUMBER);
772 gchar *text = pspp_locale_to_utf8 (s, -1, 0);
781 get_row_sensitivity (const GSheetModel *model, gint unit)
783 PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
785 return (unit < psppire_data_store_get_case_count (ds));
791 /* Column related stuff */
794 get_column_subtitle (const GSheetModel *model, gint col)
797 const struct variable *v ;
798 PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
800 if ( col >= psppire_dict_get_var_cnt (ds->dict) )
803 v = psppire_dict_get_variable (ds->dict, col);
805 if ( ! var_has_label (v))
808 text = pspp_locale_to_utf8 (var_get_label (v), -1, 0);
814 get_column_button_label (const GSheetModel *model, gint col)
817 struct variable *pv ;
818 PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
820 if ( col >= psppire_dict_get_var_cnt (ds->dict) )
821 return g_locale_to_utf8 (null_var_name, -1, 0, 0, 0);
823 pv = psppire_dict_get_variable (ds->dict, col);
825 text = pspp_locale_to_utf8 (var_get_name (pv), -1, 0);
831 get_column_sensitivity (const GSheetModel *model, gint col)
833 PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
835 return (col < psppire_dict_get_var_cnt (ds->dict));
840 static GtkJustification
841 get_column_justification (const GSheetModel *model, gint col)
843 PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
844 const struct variable *pv ;
846 if ( col >= psppire_dict_get_var_cnt (ds->dict) )
847 return GTK_JUSTIFY_LEFT;
849 pv = psppire_dict_get_variable (ds->dict, col);
851 return (var_get_alignment (pv) == ALIGN_LEFT ? GTK_JUSTIFY_LEFT
852 : var_get_alignment (pv) == ALIGN_RIGHT ? GTK_JUSTIFY_RIGHT
853 : GTK_JUSTIFY_CENTER);
861 /* Fills C with the CASENUMth case.
862 Returns true on success, false otherwise.
865 psppire_data_store_get_case (const PsppireDataStore *ds,
869 g_return_val_if_fail (ds, FALSE);
870 g_return_val_if_fail (ds->datasheet, FALSE);
872 return datasheet_get_row (ds->datasheet, casenum, c);
877 psppire_data_store_delete_cases (PsppireDataStore *ds, casenumber n_cases,
880 g_return_val_if_fail (ds, FALSE);
881 g_return_val_if_fail (ds->datasheet, FALSE);
883 g_return_val_if_fail (first + n_cases <=
884 psppire_data_store_get_case_count (ds), FALSE);
886 datasheet_delete_rows (ds->datasheet, first, n_cases);
888 g_signal_emit (ds, signals [CASES_DELETED], 0, first, n_cases);
895 /* Insert case CC into the case file before POSN */
897 psppire_data_store_insert_case (PsppireDataStore *ds,
904 g_return_val_if_fail (ds, FALSE);
905 g_return_val_if_fail (ds->datasheet, FALSE);
907 case_clone (&tmp, cc);
908 result = datasheet_insert_rows (ds->datasheet, posn, &tmp, 1);
911 g_signal_emit (ds, signals [CASE_INSERTED], 0, posn);
913 g_warning ("Cannot insert case at position %ld\n", posn);
919 /* Copies the IDXth value from case CASENUM into VALUE.
920 If VALUE is null, then memory is allocated is allocated with
921 malloc. Returns the value if successful, NULL on failure. */
923 psppire_data_store_get_value (const PsppireDataStore *ds,
924 casenumber casenum, size_t idx,
925 union value *value, int width)
929 g_return_val_if_fail (ds, false);
930 g_return_val_if_fail (ds->datasheet, false);
932 g_return_val_if_fail (idx < datasheet_get_column_cnt (ds->datasheet), false);
936 value = xnmalloc (value_cnt_from_width (width), sizeof *value);
941 if (!datasheet_get_value (ds->datasheet, casenum, idx, value, width))
952 /* Set the IDXth value of case C to V.
953 Returns true if successful, false on I/O error. */
955 psppire_data_store_set_value (PsppireDataStore *ds, casenumber casenum,
956 gint idx, union value *v, gint width)
960 g_return_val_if_fail (ds, FALSE);
961 g_return_val_if_fail (ds->datasheet, FALSE);
963 g_return_val_if_fail (idx < datasheet_get_column_cnt (ds->datasheet), FALSE);
965 ok = datasheet_put_value (ds->datasheet, casenum, idx, v, width);
967 g_signal_emit (ds, signals [CASE_CHANGED], 0, casenum);
974 /* Set the IDXth value of case C using D_IN */
976 psppire_data_store_data_in (PsppireDataStore *ds, casenumber casenum, gint idx,
977 struct substring input, const struct fmt_spec *fmt)
979 union value *value = NULL;
983 g_return_val_if_fail (ds, FALSE);
984 g_return_val_if_fail (ds->datasheet, FALSE);
986 g_return_val_if_fail (idx < datasheet_get_column_cnt (ds->datasheet), FALSE);
988 width = fmt_var_width (fmt);
989 value = xmalloca (value_cnt_from_width (width) * sizeof *value);
990 ok = (datasheet_get_value (ds->datasheet, casenum, idx, value, width)
991 && data_in (input, LEGACY_NATIVE, fmt->type, 0, 0, 0, value, width)
992 && datasheet_put_value (ds->datasheet, casenum, idx, value, width));
995 g_signal_emit (ds, signals [CASE_CHANGED], 0, casenum);
1002 /* Resize the cases in the casefile, by inserting N_VALUES into every
1003 one of them at the position immediately preceeding WHERE.
1006 psppire_data_store_insert_values (PsppireDataStore *ds,
1007 gint n_values, gint where)
1009 g_return_val_if_fail (ds, FALSE);
1011 if ( n_values == 0 )
1014 g_assert (n_values > 0);
1016 if ( ! ds->datasheet )
1017 ds->datasheet = datasheet_create (NULL);
1020 union value *values = xcalloc (n_values, sizeof *values);
1021 datasheet_insert_columns (ds->datasheet, values, n_values, where);