1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2006 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/gtksheet.h>
29 #include <gtksheet/gsheetmodel.h>
30 #include <gtksheet/gsheet-column-iface.h>
32 #include <pango/pango-context.h>
34 #include "psppire-data-store.h"
35 #include "psppire-case-file.h"
38 #include <data/dictionary.h>
39 #include <data/missing-values.h>
40 #include <data/value-labels.h>
41 #include <data/data-in.h>
44 static void psppire_data_store_init (PsppireDataStore *data_store);
45 static void psppire_data_store_class_init (PsppireDataStoreClass *class);
46 static void psppire_data_store_sheet_model_init (GSheetModelIface *iface);
47 static void psppire_data_store_sheet_column_init (GSheetColumnIface *iface);
48 static void psppire_data_store_sheet_row_init (GSheetRowIface *iface);
50 static void psppire_data_store_finalize (GObject *object);
52 static gboolean psppire_data_store_clear_datum (GSheetModel *model,
53 gint row, gint column);
56 #define MIN_COLUMNS 10
58 #define TRAILING_ROWS 10
60 static GObjectClass *parent_class = NULL;
66 static guint signals [n_SIGNALS];
70 psppire_data_store_get_type (void)
72 static GType data_store_type = 0;
76 static const GTypeInfo data_store_info =
78 sizeof (PsppireDataStoreClass),
80 NULL, /* base_finalize */
81 (GClassInitFunc) psppire_data_store_class_init,
82 NULL, /* class_finalize */
83 NULL, /* class_data */
84 sizeof (PsppireDataStore),
86 (GInstanceInitFunc) psppire_data_store_init,
89 static const GInterfaceInfo sheet_model_info =
91 (GInterfaceInitFunc) psppire_data_store_sheet_model_init,
96 static const GInterfaceInfo sheet_column_info =
98 (GInterfaceInitFunc) psppire_data_store_sheet_column_init,
103 static const GInterfaceInfo sheet_row_info =
105 (GInterfaceInitFunc) psppire_data_store_sheet_row_init,
111 data_store_type = g_type_register_static (G_TYPE_OBJECT, "PsppireDataStore",
112 &data_store_info, 0);
114 g_type_add_interface_static (data_store_type,
118 g_type_add_interface_static (data_store_type,
122 g_type_add_interface_static (data_store_type,
127 return data_store_type;
132 psppire_data_store_class_init (PsppireDataStoreClass *class)
134 GObjectClass *object_class;
136 parent_class = g_type_class_peek_parent (class);
137 object_class = (GObjectClass*) class;
139 object_class->finalize = psppire_data_store_finalize;
141 signals [FONT_CHANGED] =
142 g_signal_new ("font_changed",
143 G_TYPE_FROM_CLASS (class),
147 g_cclosure_marshal_VOID__VOID,
155 psppire_data_store_get_var_count (const GSheetModel *model)
157 const PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
159 return psppire_dict_get_var_cnt (store->dict);
163 psppire_data_store_get_case_count (const GSheetModel *model)
165 const PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
167 return psppire_case_file_get_case_count (store->case_file);
172 psppire_data_store_init (PsppireDataStore *data_store)
174 data_store->dict = 0;
175 data_store->case_file = 0;
176 data_store->width_of_m = 10;
179 const PangoFontDescription *
180 psppire_data_store_get_font_desc (const GSheetModel *model,
181 gint row, gint column)
183 PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
185 return store->font_desc;
188 static inline gchar *
189 psppire_data_store_get_string_wrapper (const GSheetModel *model, gint row,
192 return psppire_data_store_get_string (PSPPIRE_DATA_STORE (model), row, column);
196 static inline gboolean
197 psppire_data_store_set_string_wrapper (GSheetModel *model,
199 gint row, gint column)
201 return psppire_data_store_set_string (PSPPIRE_DATA_STORE (model), text,
209 psppire_data_store_sheet_model_init (GSheetModelIface *iface)
211 iface->free_strings = TRUE;
212 iface->get_string = psppire_data_store_get_string_wrapper;
213 iface->set_string = psppire_data_store_set_string_wrapper;
214 iface->clear_datum = psppire_data_store_clear_datum;
215 iface->is_editable = NULL;
216 iface->is_visible = NULL;
217 iface->get_foreground = NULL;
218 iface->get_background = NULL;
219 iface->get_font_desc = psppire_data_store_get_font_desc;
220 iface->get_cell_border = NULL;
221 iface->get_column_count = psppire_data_store_get_var_count;
222 iface->get_row_count = psppire_data_store_get_case_count;
226 gboolean always_true ()
233 delete_cases_callback (GtkWidget *w, gint first, gint n_cases, gpointer data)
235 PsppireDataStore *store ;
237 g_return_if_fail (data);
239 store = PSPPIRE_DATA_STORE (data);
241 g_assert (first >= 0);
243 g_sheet_model_rows_deleted (G_SHEET_MODEL (store), first, n_cases);
248 insert_case_callback (GtkWidget *w, gint casenum, gpointer data)
250 PsppireDataStore *store ;
252 g_return_if_fail (data);
254 store = PSPPIRE_DATA_STORE (data);
256 g_sheet_model_range_changed (G_SHEET_MODEL (store),
258 psppire_case_file_get_case_count (store->case_file),
261 g_sheet_model_rows_inserted (G_SHEET_MODEL (store), casenum, 1);
266 changed_case_callback (GtkWidget *w, gint casenum, gpointer data)
268 PsppireDataStore *store ;
269 g_return_if_fail (data);
271 store = PSPPIRE_DATA_STORE (data);
273 g_sheet_model_range_changed (G_SHEET_MODEL (store),
280 delete_variables_callback (GObject *obj, gint var_num, gint n_vars, gpointer data)
282 PsppireDataStore *store ;
284 g_return_if_fail (data);
286 store = PSPPIRE_DATA_STORE (data);
288 g_sheet_model_columns_deleted (G_SHEET_MODEL (store), var_num, n_vars);
290 g_sheet_column_columns_changed (G_SHEET_COLUMN (store),
296 variable_changed_callback (GObject *obj, gint var_num, gpointer data)
298 PsppireDataStore *store;
300 g_return_if_fail (data);
302 store = PSPPIRE_DATA_STORE (data);
304 g_sheet_column_columns_changed (G_SHEET_COLUMN (store),
308 g_sheet_model_range_changed (G_SHEET_MODEL (store),
314 insert_variable_callback (GObject *obj, gint var_num, gpointer data)
316 PsppireDataStore *store;
319 g_return_if_fail (data);
321 store = PSPPIRE_DATA_STORE (data);
325 struct variable *variable =
326 psppire_dict_get_variable (store->dict, var_num);
328 g_assert (variable != NULL);
330 posn = var_get_case_index (variable);
337 psppire_case_file_insert_values (store->case_file, 1, posn);
339 g_sheet_column_columns_changed (G_SHEET_COLUMN (store),
342 g_sheet_model_columns_inserted (G_SHEET_MODEL (store), var_num, 1);
347 dict_size_change_callback (GObject *obj,
348 gint posn, gint adjustment, gpointer data)
350 PsppireDataStore *store ;
352 g_return_if_fail (data);
354 store = PSPPIRE_DATA_STORE (data);
356 psppire_case_file_insert_values (store->case_file, adjustment, posn);
362 * psppire_data_store_new:
363 * @dict: The dictionary for this data_store.
366 * Return value: a new #PsppireDataStore
369 psppire_data_store_new (PsppireDict *dict)
371 PsppireDataStore *retval;
373 retval = g_object_new (GTK_TYPE_DATA_STORE, NULL);
375 psppire_data_store_set_dictionary (retval, dict);
382 psppire_data_store_set_case_file (PsppireDataStore *data_store,
385 if ( data_store->case_file)
387 g_object_unref (data_store->case_file);
390 data_store->case_file = cf;
392 g_signal_connect (data_store->case_file, "cases-deleted",
393 G_CALLBACK (delete_cases_callback),
396 g_signal_connect (data_store->case_file, "case-inserted",
397 G_CALLBACK (insert_case_callback),
401 g_signal_connect (data_store->case_file, "case-changed",
402 G_CALLBACK (changed_case_callback),
409 * psppire_data_store_replace_set_dictionary:
410 * @data_store: The variable store
411 * @dict: The dictionary to set
413 * If a dictionary is already associated with the data-store, then it will be
417 psppire_data_store_set_dictionary (PsppireDataStore *data_store, PsppireDict *dict)
419 data_store->dict = dict;
421 g_signal_connect (dict, "variable-inserted",
422 G_CALLBACK (insert_variable_callback),
425 g_signal_connect (dict, "variables-deleted",
426 G_CALLBACK (delete_variables_callback),
429 g_signal_connect (dict, "variable-changed",
430 G_CALLBACK (variable_changed_callback),
434 g_signal_connect (dict, "dict-size-changed",
435 G_CALLBACK (dict_size_change_callback),
438 /* The entire model has changed */
439 g_sheet_model_range_changed (G_SHEET_MODEL (data_store), -1, -1, -1, -1);
441 g_sheet_column_columns_changed (G_SHEET_COLUMN (data_store), 0, -1);
445 psppire_data_store_finalize (GObject *object)
449 (* parent_class->finalize) (object);
454 /* Insert a blank case before POSN */
456 psppire_data_store_insert_new_case (PsppireDataStore *ds, gint posn)
461 g_return_val_if_fail (ds, FALSE);
464 /* Opportunity for optimisation exists here when creating a blank case */
465 val_cnt = datasheet_get_column_cnt (ds->case_file->datasheet) ;
467 case_create (&cc, val_cnt);
469 memset ( case_data_rw_idx (&cc, 0), 0, val_cnt * MAX_SHORT_STRING);
471 for (v = 0 ; v < psppire_dict_get_var_cnt (ds->dict) ; ++v)
473 const struct variable *pv = psppire_dict_get_variable (ds->dict, v);
474 if ( var_is_alpha (pv))
477 case_data_rw (&cc, pv)->f = SYSMIS;
480 result = psppire_case_file_insert_case (ds->case_file, &cc, posn);
489 psppire_data_store_get_string (PsppireDataStore *store, gint row, gint column)
493 const struct fmt_spec *fp ;
494 const struct variable *pv ;
498 g_return_val_if_fail (store->dict, NULL);
499 g_return_val_if_fail (store->case_file, NULL);
501 if (column >= psppire_dict_get_var_cnt (store->dict))
504 if ( row >= psppire_case_file_get_case_count (store->case_file))
507 pv = psppire_dict_get_variable (store->dict, column);
511 idx = var_get_case_index (pv);
515 v = psppire_case_file_get_value (store->case_file, row, idx, NULL,
518 g_return_val_if_fail (v, NULL);
520 if ( store->show_labels)
522 const gchar *label = var_lookup_value_label (pv, v);
526 return pspp_locale_to_utf8 (label, -1, 0);
530 fp = var_get_write_format (pv);
532 s = g_string_sized_new (fp->w + 1);
533 g_string_set_size (s, fp->w);
535 memset (s->str, 0, fp->w);
537 g_assert (fp->w == s->len);
539 /* Converts binary value V into printable form in the exactly
540 FP->W character in buffer S according to format specification
541 FP. No null terminator is appended to the buffer. */
542 data_out (v, fp, s->str);
544 text = pspp_locale_to_utf8 (s->str, fp->w, 0);
545 g_string_free (s, TRUE);
555 psppire_data_store_clear_datum (GSheetModel *model,
559 PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
562 const struct variable *pv = psppire_dict_get_variable (store->dict, col);
564 const gint index = var_get_case_index (pv) ;
566 if ( var_is_numeric (pv))
569 memcpy (v.s, "", MAX_SHORT_STRING);
571 psppire_case_file_set_value (store->case_file, row, index, &v,
578 /* Attempts to update that part of the variable store which corresponds
579 to ROW, COL with the value TEXT.
580 Returns true if anything was updated, false otherwise.
583 psppire_data_store_set_string (PsppireDataStore *store,
584 const gchar *text, gint row, gint col)
586 const struct variable *pv = psppire_dict_get_variable (store->dict, col);
587 g_return_val_if_fail (pv, FALSE);
590 /* Allow the user to insert a lot of blank cases, simply by skipping rows */
591 for (r = psppire_case_file_get_case_count (store->case_file); r <= row ; ++r)
596 psppire_case_array_insert_case (store->cases, r, 0, 0);
599 for (c = 0 ; c < psppire_dict_get_var_cnt (store->dict); ++c )
600 psppire_data_store_clear_datum (model, r, c);
604 psppire_case_file_data_in (store->case_file, row,
605 var_get_case_index (pv), ss_cstr (text),
606 var_get_write_format (pv));
613 psppire_data_store_set_font (PsppireDataStore *store,
614 const PangoFontDescription *fd)
616 g_return_if_fail (store);
617 g_return_if_fail (PSPPIRE_IS_DATA_STORE (store));
619 store->font_desc = fd;
621 store->width_of_m = calc_m_width (fd);
623 g_signal_emit (store, signals [FONT_CHANGED], 0);
626 g_sheet_model_range_changed (G_SHEET_MODEL (store),
632 psppire_data_store_show_labels (PsppireDataStore *store, gboolean show_labels)
634 g_return_if_fail (store);
635 g_return_if_fail (PSPPIRE_IS_DATA_STORE (store));
637 store->show_labels = show_labels;
639 g_sheet_model_range_changed (G_SHEET_MODEL (store),
645 psppire_data_store_clear (PsppireDataStore *data_store)
647 psppire_case_file_clear (data_store->case_file);
649 psppire_dict_clear (data_store->dict);
654 /* Return a casereader made from this datastore */
656 psppire_data_store_get_reader (PsppireDataStore *ds)
658 struct casereader *reader ;
660 reader = psppire_case_file_make_reader (ds->case_file);
667 /* Column related funcs */
670 geometry_get_column_count (const GSheetColumn *geom)
672 PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
674 return MAX (MIN_COLUMNS, psppire_dict_get_var_cnt (ds->dict));
680 geometry_get_width (const GSheetColumn *geom, gint unit)
682 const struct variable *pv ;
683 PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
685 if ( unit >= psppire_dict_get_var_cnt (ds->dict) )
686 return ds->width_of_m * 8 ;
688 pv = psppire_dict_get_variable (ds->dict, unit);
691 return ds->width_of_m * 8 ;
693 return ds->width_of_m * var_get_display_width (pv);
697 geometry_set_width (GSheetColumn *geom, gint unit, gint width)
699 PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
701 struct variable *pv = psppire_dict_get_variable (ds->dict, unit);
703 var_set_display_width (pv, width / ds->width_of_m );
708 static GtkJustification
709 geometry_get_justification (const GSheetColumn *geom, gint unit)
711 PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
712 const struct variable *pv ;
715 if ( unit >= psppire_dict_get_var_cnt (ds->dict) )
716 return GTK_JUSTIFY_LEFT;
718 pv = psppire_dict_get_variable (ds->dict, unit);
720 return (var_get_alignment (pv) == ALIGN_LEFT ? GTK_JUSTIFY_LEFT
721 : var_get_alignment (pv) == ALIGN_RIGHT ? GTK_JUSTIFY_RIGHT
722 : GTK_JUSTIFY_CENTER);
726 static const gchar null_var_name[]=N_("var");
729 geometry_get_column_button_label (const GSheetColumn *geom, gint unit)
732 struct variable *pv ;
733 PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
735 if ( unit >= psppire_dict_get_var_cnt (ds->dict) )
736 return g_locale_to_utf8 (null_var_name, -1, 0, 0, 0);
738 pv = psppire_dict_get_variable (ds->dict, unit);
740 text = pspp_locale_to_utf8 (var_get_name (pv), -1, 0);
747 geometry_get_column_subtitle (const GSheetColumn *geom, gint unit)
750 const struct variable *v ;
751 PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
753 if ( unit >= psppire_dict_get_var_cnt (ds->dict) )
756 v = psppire_dict_get_variable (ds->dict, unit);
758 if ( ! var_has_label (v))
761 text = pspp_locale_to_utf8 (var_get_label (v), -1, 0);
768 geometry_get_sensitivity (const GSheetColumn *geom, gint unit)
770 PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
772 return (unit < psppire_dict_get_var_cnt (ds->dict));
777 psppire_data_store_sheet_column_init (GSheetColumnIface *iface)
779 iface->get_column_count = geometry_get_column_count;
780 iface->get_width = geometry_get_width;
781 iface->set_width = geometry_set_width;
782 iface->get_visibility = always_true;
783 iface->get_sensitivity = geometry_get_sensitivity;
784 iface->get_justification = geometry_get_justification;
785 iface->get_button_label = geometry_get_column_button_label;
786 iface->get_subtitle = geometry_get_column_subtitle;
790 /* Row related funcs */
793 geometry_get_row_count (const GSheetRow *geom, gpointer data)
795 PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
797 return TRAILING_ROWS + psppire_case_file_get_case_count (ds->case_file);
802 geometry_get_height (const GSheetRow *geom, gint unit, gpointer data)
809 geometry_get_row_sensitivity (const GSheetRow *geom, gint unit, gpointer data)
811 PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
814 return (unit < psppire_case_file_get_case_count (ds->case_file));
819 geometry_get_row_button_label (const GSheetRow *geom, gint unit, gpointer data)
823 PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
826 TRAILING_ROWS + psppire_case_file_get_case_count (ds->case_file))
829 s = g_strdup_printf (_("%d"), unit + FIRST_CASE_NUMBER);
831 text = pspp_locale_to_utf8 (s, -1, 0);
840 psppire_data_store_sheet_row_init (GSheetRowIface *iface)
842 iface->get_row_count = geometry_get_row_count;
844 iface->get_height = geometry_get_height;
845 iface->set_height = 0;
846 iface->get_visibility = always_true;
847 iface->get_sensitivity = geometry_get_row_sensitivity;
849 iface->get_button_label = geometry_get_row_button_label;