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 if (range->start_x > range->end_x)
253 gint temp = range->start_x;
254 range->start_x = range->end_x;
258 psppire_dict_delete_variables (dict, range->start_y,
259 (range->end_y - range->start_y + 1));
261 gtk_widget_queue_draw (GTK_WIDGET (sheet));
265 create_var_row_header_popup_menu (PsppireVariableSheet *var_sheet)
267 GtkWidget *menu = gtk_menu_new ();
270 gtk_menu_item_new_with_mnemonic (_("_Insert Variable"));
271 g_signal_connect_swapped (item, "activate", G_CALLBACK (insert_new_variable_var),
273 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
275 item = gtk_separator_menu_item_new ();
276 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
278 var_sheet->clear_variables_menu_item =
279 gtk_menu_item_new_with_mnemonic (_("Cl_ear Variables"));
281 g_signal_connect_swapped (var_sheet->clear_variables_menu_item, "activate",
282 G_CALLBACK (delete_variables), var_sheet);
284 gtk_widget_set_sensitive (var_sheet->clear_variables_menu_item, FALSE);
285 gtk_menu_shell_append (GTK_MENU_SHELL (menu),
286 var_sheet->clear_variables_menu_item);
288 gtk_widget_show_all (menu);
294 set_var_popup_sensitivity (SswSheet *sheet, gpointer selection, gpointer p)
296 PsppireVariableSheet *var_sheet = PSPPIRE_VARIABLE_SHEET (sheet);
297 SswRange *range = selection;
298 gint width = gtk_tree_model_get_n_columns (sheet->data_model);
300 gboolean whole_row_selected = (range->start_x == 0 &&
301 range->end_x == width - 1 - 1);
302 /* PsppireDict has an "extra" column: TVM_COL_VAR ^^^ */
303 gtk_widget_set_sensitive (var_sheet->clear_variables_menu_item,
310 change_var_property (PsppireVariableSheet *var_sheet, gint col, gint row, const GValue *value)
312 PsppireDict *dict = NULL;
313 g_object_get (var_sheet, "data-model", &dict, NULL);
315 int n_rows = psppire_dict_get_var_cnt (dict);
319 /* Return the IDXth variable */
320 struct variable *var = psppire_dict_get_variable (dict, row);
323 var = psppire_dict_insert_variable (dict, row, NULL);
327 case DICT_TVM_COL_NAME:
329 const char *name = g_value_get_string (value);
330 if (psppire_dict_check_name (dict, name, FALSE))
331 dict_rename_var (dict->dict, var, g_value_get_string (value));
334 case DICT_TVM_COL_WIDTH:
336 gint width = g_value_get_int (value);
337 if (var_is_numeric (var))
339 struct fmt_spec format = *var_get_print_format (var);
340 fmt_change_width (&format, width, FMT_FOR_OUTPUT);
341 var_set_both_formats (var, &format);
345 var_set_width (var, width);
349 case DICT_TVM_COL_DECIMAL:
351 gint decimals = g_value_get_int (value);
354 struct fmt_spec format = *var_get_print_format (var);
355 fmt_change_decimals (&format, decimals, FMT_FOR_OUTPUT);
356 var_set_both_formats (var, &format);
360 case DICT_TVM_COL_LABEL:
361 var_set_label (var, g_value_get_string (value));
363 case DICT_TVM_COL_COLUMNS:
364 var_set_display_width (var, g_value_get_int (value));
366 case DICT_TVM_COL_MEASURE:
367 var_set_measure (var, g_value_get_int (value));
369 case DICT_TVM_COL_ALIGNMENT:
370 var_set_alignment (var, g_value_get_int (value));
372 case DICT_TVM_COL_ROLE:
373 var_set_role (var, g_value_get_int (value));
376 g_warning ("Changing unknown column %d of variable sheet column not supported",
383 var_sheet_data_to_string (SswSheet *sheet, GtkTreeModel *m,
384 gint col, gint row, const GValue *in)
386 if (col >= n_DICT_COLS - 1) /* -1 because psppire-dict has an extra column */
389 const struct variable *var = psppire_dict_get_variable (PSPPIRE_DICT (m), row);
393 if (col == DICT_TVM_COL_TYPE)
395 const struct fmt_spec *print = var_get_print_format (var);
396 return strdup (fmt_gui_name (print->type));
398 else if (col == DICT_TVM_COL_MISSING_VALUES)
399 return missing_values_to_string (var, NULL);
400 else if (col == DICT_TVM_COL_VALUE_LABELS)
402 const struct val_labs *vls = var_get_value_labels (var);
403 if (vls == NULL || val_labs_count (vls) == 0)
404 return strdup (_("None"));
405 const struct val_lab **labels = val_labs_sorted (vls);
406 const struct val_lab *vl = labels[0];
407 gchar *vstr = value_to_text (vl->value, var);
408 char *text = xasprintf (_("{%s, %s}..."), vstr,
409 val_lab_get_escaped_label (vl));
415 return ssw_sheet_default_forward_conversion (sheet, m, col, row, in);
420 static GObjectClass * parent_class = NULL;
423 psppire_variable_sheet_dispose (GObject *obj)
425 PsppireVariableSheet *sheet = PSPPIRE_VARIABLE_SHEET (obj);
427 if (sheet->dispose_has_run)
430 sheet->dispose_has_run = TRUE;
432 g_object_unref (sheet->value_label_renderer);
433 g_object_unref (sheet->missing_values_renderer);
434 g_object_unref (sheet->var_type_renderer);
436 /* Chain up to the parent class */
437 G_OBJECT_CLASS (parent_class)->dispose (obj);
441 psppire_variable_sheet_finalize (GObject *object)
443 PsppireVariableSheet *sheet = PSPPIRE_VARIABLE_SHEET (object);
445 g_free (sheet->value_label_dispatch);
446 g_free (sheet->missing_values_dispatch);
447 g_free (sheet->var_type_dispatch);
449 if (G_OBJECT_CLASS (parent_class)->finalize)
450 (*G_OBJECT_CLASS (parent_class)->finalize) (object);
454 psppire_variable_sheet_class_init (PsppireVariableSheetClass *class)
456 GObjectClass *object_class = G_OBJECT_CLASS (class);
457 object_class->dispose = psppire_variable_sheet_dispose;
459 parent_class = g_type_class_peek_parent (class);
461 object_class->finalize = psppire_variable_sheet_finalize;
465 psppire_variable_sheet_new (void)
467 PsppireVarSheetHeader *vsh =
468 g_object_new (PSPPIRE_TYPE_VAR_SHEET_HEADER, NULL);
471 g_object_new (PSPPIRE_TYPE_VARIABLE_SHEET,
472 "select-renderer-func", select_renderer_func,
474 "forward-conversion", var_sheet_data_to_string,
476 "vertical-draggable", TRUE,
479 return GTK_WIDGET (obj);
483 move_variable (PsppireVariableSheet *sheet, gint from, gint to, gpointer ud)
485 PsppireDict *dict = NULL;
486 g_object_get (sheet, "data-model", &dict, NULL);
491 struct variable *var = psppire_dict_get_variable (dict, from);
496 /* The index refers to the final position, so if the source
497 is less than the destination, then we must subtract 1, to
498 account for the position vacated by the source */
501 dict_reorder_var (dict->dict, var, new_pos);
506 is_printable_key (gint keyval)
511 case GDK_KEY_ISO_Left_Tab:
517 return (0 != gdk_keyval_to_unicode (keyval));
522 PsppireVariableSheet *sheet;
523 void (*payload) (PsppireVariableSheet *);
528 on_key_press (GtkWidget *w, GdkEventKey *e, gpointer user_data)
530 const struct dispatch *d = user_data;
531 if (is_printable_key (e->keyval))
533 d->payload (d->sheet);
541 on_button_press (GtkWidget *w, GdkEventButton *e, gpointer user_data)
543 const struct dispatch *d = user_data;
547 d->payload (d->sheet);
552 on_edit_start (GtkCellRenderer *renderer,
553 GtkCellEditable *editable,
557 gtk_widget_grab_focus (GTK_WIDGET (editable));
558 g_signal_connect (editable, "key-press-event",
559 G_CALLBACK (on_key_press), user_data);
560 g_signal_connect (editable, "button-press-event",
561 G_CALLBACK (on_button_press), user_data);
566 psppire_variable_sheet_init (PsppireVariableSheet *sheet)
568 sheet->dispose_has_run = FALSE;
570 sheet->value_label_renderer = gtk_cell_renderer_text_new ();
571 sheet->value_label_dispatch = g_malloc (sizeof *sheet->value_label_dispatch);
572 sheet->value_label_dispatch->sheet = sheet;
573 sheet->value_label_dispatch->payload = set_value_labels;
574 g_signal_connect_after (sheet->value_label_renderer,
575 "editing-started", G_CALLBACK (on_edit_start),
576 sheet->value_label_dispatch);
578 sheet->missing_values_renderer = gtk_cell_renderer_text_new ();
579 sheet->missing_values_dispatch = g_malloc (sizeof *sheet->missing_values_dispatch);
580 sheet->missing_values_dispatch->sheet = sheet;
581 sheet->missing_values_dispatch->payload = set_missing_values;
582 g_signal_connect_after (sheet->missing_values_renderer,
583 "editing-started", G_CALLBACK (on_edit_start),
584 sheet->missing_values_dispatch);
586 sheet->var_type_renderer = gtk_cell_renderer_text_new ();
587 sheet->var_type_dispatch = g_malloc (sizeof *sheet->var_type_dispatch);
588 sheet->var_type_dispatch->sheet = sheet;
589 sheet->var_type_dispatch->payload = set_var_type;
590 g_signal_connect_after (sheet->var_type_renderer,
591 "editing-started", G_CALLBACK (on_edit_start),
592 sheet->var_type_dispatch);
594 sheet->row_popup = create_var_row_header_popup_menu (sheet);
597 g_signal_connect (sheet, "selection-changed",
598 G_CALLBACK (set_var_popup_sensitivity), sheet);
600 g_signal_connect (sheet, "row-header-pressed",
601 G_CALLBACK (show_variables_row_popup), sheet);
603 g_signal_connect_swapped (sheet, "value-changed",
604 G_CALLBACK (change_var_property), sheet);
606 g_signal_connect (sheet, "row-moved",
607 G_CALLBACK (move_variable), NULL);