1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2008 Free Software Foundation, Inc.
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/>. */
18 #include "psppire-var-sheet.h"
19 #include <ui/gui/sheet/psppire-axis-impl.h>
23 #include "customentry.h"
24 #include <data/variable.h>
25 #include "psppire-var-store.h"
28 #define _(msgid) gettext (msgid)
29 #define N_(msgid) msgid
32 static void psppire_var_sheet_class_init (PsppireVarSheetClass *klass);
33 static void psppire_var_sheet_init (PsppireVarSheet *vs);
34 static void psppire_var_sheet_realize (GtkWidget *w);
35 static void psppire_var_sheet_unrealize (GtkWidget *w);
40 PSPPIRE_VAR_SHEET_MAY_CREATE_VARS = 1
44 psppire_var_sheet_get_type (void)
46 static GType vs_type = 0;
50 static const GTypeInfo vs_info =
52 sizeof (PsppireVarSheetClass),
54 NULL, /* base_finalize */
55 (GClassInitFunc) psppire_var_sheet_class_init,
56 NULL, /* class_finalize */
57 NULL, /* class_data */
58 sizeof (PsppireVarSheet),
60 (GInstanceInitFunc) psppire_var_sheet_init,
63 vs_type = g_type_register_static (PSPPIRE_TYPE_SHEET, "PsppireVarSheet",
70 static GObjectClass * parent_class = NULL;
73 psppire_var_sheet_dispose (GObject *obj)
75 PsppireVarSheet *vs = (PsppireVarSheet *)obj;
77 if (vs->dispose_has_run)
80 /* Make sure dispose does not run twice. */
81 vs->dispose_has_run = TRUE;
83 /* Chain up to the parent class */
84 G_OBJECT_CLASS (parent_class)->dispose (obj);
88 psppire_var_sheet_finalize (GObject *obj)
90 /* Chain up to the parent class */
91 G_OBJECT_CLASS (parent_class)->finalize (obj);
95 struct column_parameters
101 #define n_ALIGNMENTS 3
103 const gchar *const alignments[n_ALIGNMENTS + 1]={
110 const gchar *const measures[n_MEASURES + 1]={
119 /* Create a list store from an array of strings */
120 static GtkListStore *
121 create_label_list (const gchar *const *labels)
127 GtkListStore *list_store = gtk_list_store_new (1, G_TYPE_STRING);
129 while ( (s = labels[i++]))
131 gtk_list_store_append (list_store, &iter);
132 gtk_list_store_set (list_store, &iter,
142 psppire_var_sheet_set_property (GObject *object,
147 PsppireVarSheet *self = (PsppireVarSheet *) object;
151 case PSPPIRE_VAR_SHEET_MAY_CREATE_VARS:
152 self->may_create_vars = g_value_get_boolean (value);
156 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
162 psppire_var_sheet_get_property (GObject *object,
167 PsppireVarSheet *self = (PsppireVarSheet *) object;
171 case PSPPIRE_VAR_SHEET_MAY_CREATE_VARS:
172 g_value_set_boolean (value, self->may_create_vars);
176 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
183 psppire_var_sheet_class_init (PsppireVarSheetClass *klass)
185 GObjectClass *object_class = G_OBJECT_CLASS (klass);
186 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
189 parent_class = g_type_class_peek_parent (klass);
191 object_class->dispose = psppire_var_sheet_dispose;
192 object_class->finalize = psppire_var_sheet_finalize;
193 widget_class->realize = psppire_var_sheet_realize;
194 widget_class->unrealize = psppire_var_sheet_unrealize;
195 object_class->set_property = psppire_var_sheet_set_property;
196 object_class->get_property = psppire_var_sheet_get_property;
198 pspec = g_param_spec_boolean ("may-create-vars",
199 "May create variables",
200 "Whether the user may create more variables",
203 g_object_class_install_property (object_class,
204 PSPPIRE_VAR_SHEET_MAY_CREATE_VARS,
207 klass->measure_list = create_label_list (measures);
208 klass->alignment_list = create_label_list (alignments);
213 /* Callback for when the alignment combo box
216 change_alignment (GtkComboBox *cb,
217 struct variable *var)
219 gint active_item = gtk_combo_box_get_active (cb);
221 if ( active_item < 0 ) return ;
223 var_set_alignment (var, active_item);
228 /* Callback for when the measure combo box
231 change_measure (GtkComboBox *cb,
232 struct variable *var)
234 gint active_item = gtk_combo_box_get_active (cb);
236 if ( active_item < 0 ) return ;
238 var_set_measure (var, active_item);
242 /* Moves the focus to a new cell.
243 Returns TRUE iff the move should be disallowed */
245 traverse_cell_callback (PsppireSheet *sheet,
246 const PsppireSheetCell *existing_cell,
247 PsppireSheetCell *new_cell)
249 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (sheet);
250 PsppireVarStore *var_store = PSPPIRE_VAR_STORE (psppire_sheet_get_model (sheet));
252 gint n_vars = psppire_var_store_get_var_cnt (var_store);
254 if (new_cell->row >= n_vars && !var_sheet->may_create_vars)
257 if ( existing_cell->row == n_vars && new_cell->row >= n_vars)
259 GtkEntry *entry = psppire_sheet_get_entry (sheet);
261 const gchar *name = gtk_entry_get_text (entry);
263 if (! psppire_dict_check_name (var_store->dict, name, TRUE))
266 psppire_dict_insert_variable (var_store->dict, existing_cell->row, name);
272 /* If the destination cell is outside the current variables, then
273 automatically create variables for the new rows.
275 if ( ((new_cell->row > n_vars) ||
276 (new_cell->row == n_vars &&
277 new_cell->col != PSPPIRE_VAR_STORE_COL_NAME)) )
280 for ( i = n_vars ; i <= new_cell->row; ++i )
281 psppire_dict_insert_variable (var_store->dict, i, NULL);
291 Callback whenever the active cell changes on the var sheet.
294 var_sheet_change_active_cell (PsppireVarSheet *vs,
295 gint row, gint column,
296 gint oldrow, gint oldcolumn,
299 PsppireSheetCellAttr attributes;
300 PsppireVarStore *var_store;
301 PsppireVarSheetClass *vs_class =
302 PSPPIRE_VAR_SHEET_CLASS(G_OBJECT_GET_CLASS (vs));
304 struct variable *var ;
305 PsppireSheet *sheet = PSPPIRE_SHEET (vs);
307 g_return_if_fail (sheet != NULL);
309 var_store = PSPPIRE_VAR_STORE (psppire_sheet_get_model (sheet));
311 g_assert (var_store);
313 g_return_if_fail (oldcolumn == PSPPIRE_VAR_STORE_COL_NAME ||
314 row < psppire_var_store_get_var_cnt (var_store));
316 psppire_sheet_get_attributes (sheet, row, column, &attributes);
318 var = psppire_var_store_get_var (var_store, row);
322 case PSPPIRE_VAR_STORE_COL_ALIGN:
325 static GtkListStore *list_store = NULL;
326 GtkComboBoxEntry *cbe;
327 psppire_sheet_change_entry (sheet, GTK_TYPE_COMBO_BOX_ENTRY);
328 entry = psppire_sheet_get_entry (sheet);
329 cbe = GTK_COMBO_BOX_ENTRY (GTK_WIDGET (entry)->parent);
331 if ( ! list_store) list_store = create_label_list (alignments);
333 gtk_combo_box_set_model (GTK_COMBO_BOX (cbe),
334 GTK_TREE_MODEL (vs_class->alignment_list));
336 gtk_combo_box_entry_set_text_column (cbe, 0);
338 g_signal_connect (cbe, "changed",
339 G_CALLBACK (change_alignment), var);
343 case PSPPIRE_VAR_STORE_COL_MEASURE:
346 GtkComboBoxEntry *cbe;
347 psppire_sheet_change_entry (sheet, GTK_TYPE_COMBO_BOX_ENTRY);
348 entry = psppire_sheet_get_entry (sheet);
349 cbe = GTK_COMBO_BOX_ENTRY (GTK_WIDGET (entry)->parent);
351 gtk_combo_box_set_model (GTK_COMBO_BOX (cbe),
352 GTK_TREE_MODEL (vs_class->measure_list));
354 gtk_combo_box_entry_set_text_column (cbe, 0);
356 g_signal_connect (cbe, "changed",
357 G_CALLBACK (change_measure), var);
361 case PSPPIRE_VAR_STORE_COL_VALUES:
363 PsppireCustomEntry *customEntry;
365 psppire_sheet_change_entry (sheet, PSPPIRE_CUSTOM_ENTRY_TYPE);
368 PSPPIRE_CUSTOM_ENTRY (psppire_sheet_get_entry (sheet));
370 if ( var_is_long_string (var))
371 g_object_set (customEntry,
375 val_labs_dialog_set_target_variable (vs->val_labs_dialog, var);
377 g_signal_connect_swapped (customEntry,
379 G_CALLBACK (val_labs_dialog_show),
380 vs->val_labs_dialog);
384 case PSPPIRE_VAR_STORE_COL_MISSING:
386 PsppireCustomEntry *customEntry;
388 psppire_sheet_change_entry (sheet, PSPPIRE_CUSTOM_ENTRY_TYPE);
391 PSPPIRE_CUSTOM_ENTRY (psppire_sheet_get_entry (sheet));
393 if ( var_is_long_string (var))
394 g_object_set (customEntry,
399 vs->missing_val_dialog->pv =
400 psppire_var_store_get_var (var_store, row);
402 g_signal_connect_swapped (customEntry,
404 G_CALLBACK (missing_val_dialog_show),
405 vs->missing_val_dialog);
409 case PSPPIRE_VAR_STORE_COL_TYPE:
411 PsppireCustomEntry *customEntry;
413 psppire_sheet_change_entry (sheet, PSPPIRE_CUSTOM_ENTRY_TYPE);
416 PSPPIRE_CUSTOM_ENTRY (psppire_sheet_get_entry (sheet));
419 /* Popup the Variable Type dialog box */
420 vs->var_type_dialog->pv = var;
422 g_signal_connect_swapped (customEntry,
424 G_CALLBACK (var_type_dialog_show),
425 vs->var_type_dialog);
429 case PSPPIRE_VAR_STORE_COL_WIDTH:
430 case PSPPIRE_VAR_STORE_COL_DECIMALS:
431 case PSPPIRE_VAR_STORE_COL_COLUMNS:
433 if ( attributes.is_editable)
437 const gchar *s = psppire_sheet_cell_get_text (sheet, row, column);
441 GtkSpinButton *spinButton ;
442 const gint current_value = g_strtod (s, NULL);
445 const struct fmt_spec *fmt = var_get_write_format (var);
448 case PSPPIRE_VAR_STORE_COL_WIDTH:
449 r_min = MAX (fmt->d + 1, fmt_min_output_width (fmt->type));
450 r_max = fmt_max_output_width (fmt->type);
452 case PSPPIRE_VAR_STORE_COL_DECIMALS:
454 r_max = fmt_max_output_decimals (fmt->type, fmt->w);
456 case PSPPIRE_VAR_STORE_COL_COLUMNS:
458 r_max = 255 ; /* Is this a sensible value ? */
461 g_assert_not_reached ();
464 adj = gtk_adjustment_new (current_value,
466 1.0, 1.0, 1.0 /* steps */
469 psppire_sheet_change_entry (sheet, GTK_TYPE_SPIN_BUTTON);
472 GTK_SPIN_BUTTON (psppire_sheet_get_entry (sheet));
474 gtk_spin_button_set_adjustment (spinButton, GTK_ADJUSTMENT (adj));
475 gtk_spin_button_set_digits (spinButton, 0);
482 psppire_sheet_change_entry (sheet, GTK_TYPE_ENTRY);
489 psppire_var_sheet_realize (GtkWidget *w)
491 PsppireVarSheet *vs = PSPPIRE_VAR_SHEET (w);
493 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (vs));
495 vs->val_labs_dialog = val_labs_dialog_create (GTK_WINDOW (toplevel));
496 vs->missing_val_dialog = missing_val_dialog_create (GTK_WINDOW (toplevel));
497 vs->var_type_dialog = var_type_dialog_create (GTK_WINDOW (toplevel));
499 /* Chain up to the parent class */
500 GTK_WIDGET_CLASS (parent_class)->realize (w);
504 psppire_var_sheet_unrealize (GtkWidget *w)
506 PsppireVarSheet *vs = PSPPIRE_VAR_SHEET (w);
508 g_free (vs->val_labs_dialog);
509 g_free (vs->missing_val_dialog);
510 g_free (vs->var_type_dialog);
512 /* Chain up to the parent class */
513 GTK_WIDGET_CLASS (parent_class)->unrealize (w);
519 psppire_var_sheet_init (PsppireVarSheet *vs)
521 GtkBuilder *builder = builder_new ("data-editor.ui");
523 connect_help (builder);
525 g_object_unref (builder);
527 vs->dispose_has_run = FALSE;
528 vs->may_create_vars = TRUE;
530 g_signal_connect (vs, "activate",
531 G_CALLBACK (var_sheet_change_active_cell),
534 g_signal_connect (vs, "traverse",
535 G_CALLBACK (traverse_cell_callback), NULL);
539 static const struct column_parameters column_def[] = {
543 { N_("Decimals"),91},
545 { N_("Values"), 103},
546 { N_("Missing"), 95},
547 { N_("Columns"), 80},
549 { N_("Measure"), 99},
553 psppire_var_sheet_new (void)
556 PsppireAxisImpl *ha = psppire_axis_impl_new ();
557 PsppireAxisImpl *va = psppire_axis_impl_new ();
559 GtkWidget *w = g_object_new (psppire_var_sheet_get_type (), NULL);
561 for (i = 0 ; i < 10 ; ++i)
562 psppire_axis_impl_append (ha, column_def[i].width);
568 g_object_set (ha, "minimum-extent", 0,
572 "horizontal-axis", ha,