1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2017 John Darrington
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/>.
19 #include "psppire-variable-sheet.h"
21 #include "ui/gui/psppire-var-sheet-header.h"
23 #include "psppire-dict.h"
24 #include "var-type-dialog.h"
25 #include "missing-val-dialog.h"
26 #include "val-labs-dialog.h"
27 #include "var-display.h"
28 #include "data/format.h"
29 #include "data/value-labels.h"
33 #define _(msgid) gettext (msgid)
37 G_DEFINE_TYPE (PsppireVariableSheet, psppire_variable_sheet, SSW_TYPE_SHEET)
40 set_var_type (PsppireVariableSheet *sheet)
42 gint row = -1, col = -1;
43 ssw_sheet_get_active_cell (SSW_SHEET (sheet), &col, &row);
45 PsppireDict *dict = NULL;
46 g_object_get (sheet, "data-model", &dict, NULL);
48 struct variable *var =
49 psppire_dict_get_variable (PSPPIRE_DICT (dict), row);
51 const struct fmt_spec *format = var_get_write_format (var);
52 struct fmt_spec fmt = *format;
53 GtkWindow *win = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (sheet)));
54 if (GTK_RESPONSE_OK == psppire_var_type_dialog_run (win, &fmt))
56 var_set_width_and_formats (var, fmt_var_width (&fmt), &fmt, &fmt);
61 set_missing_values (PsppireVariableSheet *sheet)
63 gint row = -1, col = -1;
64 ssw_sheet_get_active_cell (SSW_SHEET (sheet), &col, &row);
66 PsppireDict *dict = NULL;
67 g_object_get (sheet, "data-model", &dict, NULL);
69 struct variable *var =
70 psppire_dict_get_variable (PSPPIRE_DICT (dict), row);
72 struct missing_values mv;
73 if (GTK_RESPONSE_OK ==
74 psppire_missing_val_dialog_run (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (sheet))),
77 var_set_missing_values (var, &mv);
84 set_value_labels (PsppireVariableSheet *sheet)
86 gint row = -1, col = -1;
87 ssw_sheet_get_active_cell (SSW_SHEET (sheet), &col, &row);
89 PsppireDict *dict = NULL;
90 g_object_get (sheet, "data-model", &dict, NULL);
92 struct variable *var =
93 psppire_dict_get_variable (PSPPIRE_DICT (dict), row);
95 struct val_labs *vls =
96 psppire_val_labs_dialog_run (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (sheet))), var);
100 var_set_value_labels (var, vls);
101 val_labs_destroy (vls);
105 static GtkCellRenderer *
106 create_spin_renderer (GType type)
108 GtkCellRenderer *r = gtk_cell_renderer_spin_new ();
110 GtkAdjustment *adj = gtk_adjustment_new (0,
121 static GtkCellRenderer *
122 create_combo_renderer (GType type)
124 GtkListStore *list_store = gtk_list_store_new (2, G_TYPE_INT, G_TYPE_STRING);
126 GEnumClass *ec = g_type_class_ref (type);
128 const GEnumValue *ev ;
129 for (ev = ec->values; ev->value_name; ++ev)
133 gtk_list_store_append (list_store, &iter);
135 gtk_list_store_set (list_store, &iter,
137 1, gettext (ev->value_nick),
141 GtkCellRenderer *r = gtk_cell_renderer_combo_new ();
152 static GtkCellRenderer *spin_renderer;
153 static GtkCellRenderer *column_width_renderer;
154 static GtkCellRenderer *measure_renderer;
155 static GtkCellRenderer *alignment_renderer;
157 static GtkCellRenderer *
158 select_renderer_func (PsppireVariableSheet *sheet, gint col, gint row, GType type, gpointer ud)
161 spin_renderer = create_spin_renderer (type);
163 if (col == DICT_TVM_COL_ROLE && !column_width_renderer)
164 column_width_renderer = create_combo_renderer (type);
166 if (col == DICT_TVM_COL_MEASURE && !measure_renderer)
167 measure_renderer = create_combo_renderer (type);
169 if (col == DICT_TVM_COL_ALIGNMENT && !alignment_renderer)
170 alignment_renderer = create_combo_renderer (type);
174 case DICT_TVM_COL_WIDTH:
175 case DICT_TVM_COL_DECIMAL:
176 case DICT_TVM_COL_COLUMNS:
177 return spin_renderer;
179 case DICT_TVM_COL_TYPE:
180 return sheet->var_type_renderer;
182 case DICT_TVM_COL_VALUE_LABELS:
183 return sheet->value_label_renderer;
185 case DICT_TVM_COL_MISSING_VALUES:
186 return sheet->missing_values_renderer;
188 case DICT_TVM_COL_ALIGNMENT:
189 return alignment_renderer;
191 case DICT_TVM_COL_MEASURE:
192 return measure_renderer;
194 case DICT_TVM_COL_ROLE:
195 return column_width_renderer;
204 show_variables_row_popup (SswSheet *sheet, int row, guint button,
205 guint state, gpointer p)
207 PsppireVariableSheet *var_sheet = PSPPIRE_VARIABLE_SHEET (sheet);
208 GListModel *vmodel = NULL;
209 g_object_get (sheet, "vmodel", &vmodel, NULL);
213 guint n_items = g_list_model_get_n_items (vmodel);
221 g_object_set_data (G_OBJECT (var_sheet->row_popup), "item",
222 GINT_TO_POINTER (row));
224 gtk_menu_popup_at_pointer (GTK_MENU (var_sheet->row_popup), NULL);
228 insert_new_variable_var (PsppireVariableSheet *var_sheet)
230 gint item = GPOINTER_TO_INT (g_object_get_data
231 (G_OBJECT (var_sheet->row_popup),
234 PsppireDict *dict = NULL;
235 g_object_get (var_sheet, "data-model", &dict, NULL);
237 psppire_dict_insert_variable (dict, item, NULL);
239 gtk_widget_queue_draw (GTK_WIDGET (var_sheet));
244 delete_variables (SswSheet *sheet)
246 SswRange *range = sheet->selection;
248 PsppireDict *dict = NULL;
249 g_object_get (sheet, "data-model", &dict, NULL);
251 psppire_dict_delete_variables (dict, range->start_y,
252 (range->end_y - range->start_y + 1));
254 gtk_widget_queue_draw (GTK_WIDGET (sheet));
258 create_var_row_header_popup_menu (PsppireVariableSheet *var_sheet)
260 GtkWidget *menu = gtk_menu_new ();
263 gtk_menu_item_new_with_mnemonic (_("_Insert Variable"));
264 g_signal_connect_swapped (item, "activate", G_CALLBACK (insert_new_variable_var),
266 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
268 item = gtk_separator_menu_item_new ();
269 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
271 var_sheet->clear_variables_menu_item =
272 gtk_menu_item_new_with_mnemonic (_("Cl_ear Variables"));
274 g_signal_connect_swapped (var_sheet->clear_variables_menu_item, "activate",
275 G_CALLBACK (delete_variables), var_sheet);
277 gtk_widget_set_sensitive (var_sheet->clear_variables_menu_item, FALSE);
278 gtk_menu_shell_append (GTK_MENU_SHELL (menu),
279 var_sheet->clear_variables_menu_item);
281 gtk_widget_show_all (menu);
287 set_var_popup_sensitivity (SswSheet *sheet, gpointer selection, gpointer p)
289 PsppireVariableSheet *var_sheet = PSPPIRE_VARIABLE_SHEET (sheet);
290 SswRange *range = selection;
291 gint width = gtk_tree_model_get_n_columns (sheet->data_model);
293 gboolean whole_row_selected = (range->start_x == 0 &&
294 range->end_x == width - 1 - 1);
295 /* PsppireDict has an "extra" column: TVM_COL_VAR ^^^ */
296 gtk_widget_set_sensitive (var_sheet->clear_variables_menu_item,
303 change_var_property (PsppireVariableSheet *var_sheet, gint col, gint row, const GValue *value)
305 PsppireDict *dict = NULL;
306 g_object_get (var_sheet, "data-model", &dict, NULL);
308 int n_rows = psppire_dict_get_var_cnt (dict);
312 /* Return the IDXth variable */
313 struct variable *var = psppire_dict_get_variable (dict, row);
316 var = psppire_dict_insert_variable (dict, row, NULL);
320 case DICT_TVM_COL_NAME:
322 const char *name = g_value_get_string (value);
323 if (psppire_dict_check_name (dict, name, FALSE))
324 dict_rename_var (dict->dict, var, g_value_get_string (value));
327 case DICT_TVM_COL_WIDTH:
329 gint width = g_value_get_int (value);
330 if (var_is_numeric (var))
332 struct fmt_spec format = *var_get_print_format (var);
333 fmt_change_width (&format, width, FMT_FOR_OUTPUT);
334 var_set_both_formats (var, &format);
338 var_set_width (var, width);
342 case DICT_TVM_COL_DECIMAL:
344 gint decimals = g_value_get_int (value);
347 struct fmt_spec format = *var_get_print_format (var);
348 fmt_change_decimals (&format, decimals, FMT_FOR_OUTPUT);
349 var_set_both_formats (var, &format);
353 case DICT_TVM_COL_LABEL:
354 var_set_label (var, g_value_get_string (value));
356 case DICT_TVM_COL_COLUMNS:
357 var_set_display_width (var, g_value_get_int (value));
359 case DICT_TVM_COL_MEASURE:
360 var_set_measure (var, g_value_get_int (value));
362 case DICT_TVM_COL_ALIGNMENT:
363 var_set_alignment (var, g_value_get_int (value));
365 case DICT_TVM_COL_ROLE:
366 var_set_role (var, g_value_get_int (value));
369 g_warning ("Changing unknown column %d of variable sheet column not supported",
376 var_sheet_data_to_string (SswSheet *sheet, GtkTreeModel *m,
377 gint col, gint row, const GValue *in)
379 if (col >= n_DICT_COLS - 1) /* -1 because psppire-dict has an extra column */
382 const struct variable *var = psppire_dict_get_variable (PSPPIRE_DICT (m), row);
386 if (col == DICT_TVM_COL_TYPE)
388 const struct fmt_spec *print = var_get_print_format (var);
389 return strdup (fmt_gui_name (print->type));
391 else if (col == DICT_TVM_COL_MISSING_VALUES)
392 return missing_values_to_string (var, NULL);
393 else if (col == DICT_TVM_COL_VALUE_LABELS)
395 const struct val_labs *vls = var_get_value_labels (var);
396 if (vls == NULL || val_labs_count (vls) == 0)
397 return strdup (_("None"));
398 const struct val_lab **labels = val_labs_sorted (vls);
399 const struct val_lab *vl = labels[0];
400 gchar *vstr = value_to_text (vl->value, var);
401 char *text = xasprintf (_("{%s, %s}..."), vstr,
402 val_lab_get_escaped_label (vl));
408 return ssw_sheet_default_forward_conversion (sheet, m, col, row, in);
413 static GObjectClass * parent_class = NULL;
416 psppire_variable_sheet_dispose (GObject *obj)
418 PsppireVariableSheet *sheet = PSPPIRE_VARIABLE_SHEET (obj);
420 if (sheet->dispose_has_run)
423 sheet->dispose_has_run = TRUE;
425 g_object_unref (sheet->value_label_renderer);
426 g_object_unref (sheet->missing_values_renderer);
427 g_object_unref (sheet->var_type_renderer);
429 /* Chain up to the parent class */
430 G_OBJECT_CLASS (parent_class)->dispose (obj);
434 psppire_variable_sheet_finalize (GObject *object)
436 PsppireVariableSheet *sheet = PSPPIRE_VARIABLE_SHEET (object);
438 g_free (sheet->value_label_dispatch);
439 g_free (sheet->missing_values_dispatch);
440 g_free (sheet->var_type_dispatch);
442 if (G_OBJECT_CLASS (parent_class)->finalize)
443 (*G_OBJECT_CLASS (parent_class)->finalize) (object);
447 psppire_variable_sheet_class_init (PsppireVariableSheetClass *class)
449 GObjectClass *object_class = G_OBJECT_CLASS (class);
450 object_class->dispose = psppire_variable_sheet_dispose;
452 parent_class = g_type_class_peek_parent (class);
454 object_class->finalize = psppire_variable_sheet_finalize;
458 psppire_variable_sheet_new (void)
460 PsppireVarSheetHeader *vsh =
461 g_object_new (PSPPIRE_TYPE_VAR_SHEET_HEADER, NULL);
464 g_object_new (PSPPIRE_TYPE_VARIABLE_SHEET,
465 "select-renderer-func", select_renderer_func,
467 "forward-conversion", var_sheet_data_to_string,
469 "vertical-draggable", TRUE,
472 return GTK_WIDGET (obj);
476 move_variable (PsppireVariableSheet *sheet, gint from, gint to, gpointer ud)
478 PsppireDict *dict = NULL;
479 g_object_get (sheet, "data-model", &dict, NULL);
484 struct variable *var = psppire_dict_get_variable (dict, from);
489 /* The index refers to the final position, so if the source
490 is less than the destination, then we must subtract 1, to
491 account for the position vacated by the source */
494 dict_reorder_var (dict->dict, var, new_pos);
499 is_printable_key (gint keyval)
504 case GDK_KEY_ISO_Left_Tab:
510 return (0 != gdk_keyval_to_unicode (keyval));
515 PsppireVariableSheet *sheet;
516 void (*payload) (PsppireVariableSheet *);
521 on_key_press (GtkWidget *w, GdkEventKey *e, gpointer user_data)
523 const struct dispatch *d = user_data;
524 if (is_printable_key (e->keyval))
526 d->payload (d->sheet);
534 on_button_press (GtkWidget *w, GdkEventButton *e, gpointer user_data)
536 const struct dispatch *d = user_data;
540 d->payload (d->sheet);
545 on_edit_start (GtkCellRenderer *renderer,
546 GtkCellEditable *editable,
550 gtk_widget_grab_focus (GTK_WIDGET (editable));
551 g_signal_connect (editable, "key-press-event",
552 G_CALLBACK (on_key_press), user_data);
553 g_signal_connect (editable, "button-press-event",
554 G_CALLBACK (on_button_press), user_data);
559 psppire_variable_sheet_init (PsppireVariableSheet *sheet)
561 sheet->dispose_has_run = FALSE;
563 sheet->value_label_renderer = gtk_cell_renderer_text_new ();
564 sheet->value_label_dispatch = g_malloc (sizeof *sheet->value_label_dispatch);
565 sheet->value_label_dispatch->sheet = sheet;
566 sheet->value_label_dispatch->payload = set_value_labels;
567 g_signal_connect_after (sheet->value_label_renderer,
568 "editing-started", G_CALLBACK (on_edit_start),
569 sheet->value_label_dispatch);
571 sheet->missing_values_renderer = gtk_cell_renderer_text_new ();
572 sheet->missing_values_dispatch = g_malloc (sizeof *sheet->missing_values_dispatch);
573 sheet->missing_values_dispatch->sheet = sheet;
574 sheet->missing_values_dispatch->payload = set_missing_values;
575 g_signal_connect_after (sheet->missing_values_renderer,
576 "editing-started", G_CALLBACK (on_edit_start),
577 sheet->missing_values_dispatch);
579 sheet->var_type_renderer = gtk_cell_renderer_text_new ();
580 sheet->var_type_dispatch = g_malloc (sizeof *sheet->var_type_dispatch);
581 sheet->var_type_dispatch->sheet = sheet;
582 sheet->var_type_dispatch->payload = set_var_type;
583 g_signal_connect_after (sheet->var_type_renderer,
584 "editing-started", G_CALLBACK (on_edit_start),
585 sheet->var_type_dispatch);
587 sheet->row_popup = create_var_row_header_popup_menu (sheet);
590 g_signal_connect (sheet, "selection-changed",
591 G_CALLBACK (set_var_popup_sensitivity), sheet);
593 g_signal_connect (sheet, "row-header-pressed",
594 G_CALLBACK (show_variables_row_popup), sheet);
596 g_signal_connect_swapped (sheet, "value-changed",
597 G_CALLBACK (change_var_property), sheet);
599 g_signal_connect (sheet, "row-moved",
600 G_CALLBACK (move_variable), NULL);