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 /* Return the IDXth variable */
309 struct variable *var = psppire_dict_get_variable (dict, row);
312 var = psppire_dict_insert_variable (dict, row, NULL);
316 case DICT_TVM_COL_NAME:
318 const char *name = g_value_get_string (value);
319 if (psppire_dict_check_name (dict, name, FALSE))
320 dict_rename_var (dict->dict, var, g_value_get_string (value));
323 case DICT_TVM_COL_WIDTH:
325 gint width = g_value_get_int (value);
326 if (var_is_numeric (var))
328 struct fmt_spec format = *var_get_print_format (var);
329 fmt_change_width (&format, width, FMT_FOR_OUTPUT);
330 var_set_both_formats (var, &format);
334 var_set_width (var, width);
338 case DICT_TVM_COL_DECIMAL:
340 gint decimals = g_value_get_int (value);
343 struct fmt_spec format = *var_get_print_format (var);
344 fmt_change_decimals (&format, decimals, FMT_FOR_OUTPUT);
345 var_set_both_formats (var, &format);
349 case DICT_TVM_COL_LABEL:
350 var_set_label (var, g_value_get_string (value));
352 case DICT_TVM_COL_COLUMNS:
353 var_set_display_width (var, g_value_get_int (value));
355 case DICT_TVM_COL_MEASURE:
356 var_set_measure (var, g_value_get_int (value));
358 case DICT_TVM_COL_ALIGNMENT:
359 var_set_alignment (var, g_value_get_int (value));
361 case DICT_TVM_COL_ROLE:
362 var_set_role (var, g_value_get_int (value));
365 g_warning ("Changing unknown column %d of variable sheet column not supported",
372 var_sheet_data_to_string (SswSheet *sheet, GtkTreeModel *m,
373 gint col, gint row, const GValue *in)
375 if (col >= n_DICT_COLS - 1) /* -1 because psppire-dict has an extra column */
378 const struct variable *var = psppire_dict_get_variable (PSPPIRE_DICT (m), row);
382 if (col == DICT_TVM_COL_TYPE)
384 const struct fmt_spec *print = var_get_print_format (var);
385 return strdup (fmt_gui_name (print->type));
387 else if (col == DICT_TVM_COL_MISSING_VALUES)
388 return missing_values_to_string (var, NULL);
389 else if (col == DICT_TVM_COL_VALUE_LABELS)
391 const struct val_labs *vls = var_get_value_labels (var);
392 if (vls == NULL || val_labs_count (vls) == 0)
393 return strdup (_("None"));
394 const struct val_lab **labels = val_labs_sorted (vls);
395 const struct val_lab *vl = labels[0];
396 gchar *vstr = value_to_text (vl->value, var);
397 char *text = xasprintf (_("{%s, %s}..."), vstr,
398 val_lab_get_escaped_label (vl));
404 return ssw_sheet_default_forward_conversion (sheet, m, col, row, in);
409 static GObjectClass * parent_class = NULL;
412 psppire_variable_sheet_dispose (GObject *obj)
414 PsppireVariableSheet *sheet = PSPPIRE_VARIABLE_SHEET (obj);
416 if (sheet->dispose_has_run)
419 sheet->dispose_has_run = TRUE;
421 g_object_unref (sheet->value_label_renderer);
422 g_object_unref (sheet->missing_values_renderer);
423 g_object_unref (sheet->var_type_renderer);
425 /* Chain up to the parent class */
426 G_OBJECT_CLASS (parent_class)->dispose (obj);
430 psppire_variable_sheet_finalize (GObject *object)
432 PsppireVariableSheet *sheet = PSPPIRE_VARIABLE_SHEET (object);
434 g_free (sheet->value_label_dispatch);
435 g_free (sheet->missing_values_dispatch);
436 g_free (sheet->var_type_dispatch);
438 if (G_OBJECT_CLASS (parent_class)->finalize)
439 (*G_OBJECT_CLASS (parent_class)->finalize) (object);
443 psppire_variable_sheet_class_init (PsppireVariableSheetClass *class)
445 GObjectClass *object_class = G_OBJECT_CLASS (class);
446 object_class->dispose = psppire_variable_sheet_dispose;
448 parent_class = g_type_class_peek_parent (class);
450 object_class->finalize = psppire_variable_sheet_finalize;
454 psppire_variable_sheet_new (void)
456 PsppireVarSheetHeader *vsh =
457 g_object_new (PSPPIRE_TYPE_VAR_SHEET_HEADER, NULL);
460 g_object_new (PSPPIRE_TYPE_VARIABLE_SHEET,
461 "select-renderer-func", select_renderer_func,
463 "forward-conversion", var_sheet_data_to_string,
465 "vertical-draggable", TRUE,
468 return GTK_WIDGET (obj);
472 move_variable (PsppireVariableSheet *sheet, gint from, gint to, gpointer ud)
474 PsppireDict *dict = NULL;
475 g_object_get (sheet, "data-model", &dict, NULL);
480 struct variable *var = psppire_dict_get_variable (dict, from);
485 /* The index refers to the final position, so if the source
486 is less than the destination, then we must subtract 1, to
487 account for the position vacated by the source */
490 dict_reorder_var (dict->dict, var, new_pos);
495 is_printable_key (gint keyval)
500 case GDK_KEY_ISO_Left_Tab:
506 return (0 != gdk_keyval_to_unicode (keyval));
511 PsppireVariableSheet *sheet;
512 void (*payload) (PsppireVariableSheet *);
517 on_key_press (GtkWidget *w, GdkEventKey *e, gpointer user_data)
519 const struct dispatch *d = user_data;
520 if (is_printable_key (e->keyval))
522 d->payload (d->sheet);
530 on_button_press (GtkWidget *w, GdkEventButton *e, gpointer user_data)
532 const struct dispatch *d = user_data;
536 d->payload (d->sheet);
541 on_edit_start (GtkCellRenderer *renderer,
542 GtkCellEditable *editable,
546 gtk_widget_grab_focus (GTK_WIDGET (editable));
547 g_signal_connect (editable, "key-press-event",
548 G_CALLBACK (on_key_press), user_data);
549 g_signal_connect (editable, "button-press-event",
550 G_CALLBACK (on_button_press), user_data);
555 psppire_variable_sheet_init (PsppireVariableSheet *sheet)
557 sheet->dispose_has_run = FALSE;
559 sheet->value_label_renderer = gtk_cell_renderer_text_new ();
560 sheet->value_label_dispatch = g_malloc (sizeof *sheet->value_label_dispatch);
561 sheet->value_label_dispatch->sheet = sheet;
562 sheet->value_label_dispatch->payload = set_value_labels;
563 g_signal_connect_after (sheet->value_label_renderer,
564 "editing-started", G_CALLBACK (on_edit_start),
565 sheet->value_label_dispatch);
567 sheet->missing_values_renderer = gtk_cell_renderer_text_new ();
568 sheet->missing_values_dispatch = g_malloc (sizeof *sheet->missing_values_dispatch);
569 sheet->missing_values_dispatch->sheet = sheet;
570 sheet->missing_values_dispatch->payload = set_missing_values;
571 g_signal_connect_after (sheet->missing_values_renderer,
572 "editing-started", G_CALLBACK (on_edit_start),
573 sheet->missing_values_dispatch);
575 sheet->var_type_renderer = gtk_cell_renderer_text_new ();
576 sheet->var_type_dispatch = g_malloc (sizeof *sheet->var_type_dispatch);
577 sheet->var_type_dispatch->sheet = sheet;
578 sheet->var_type_dispatch->payload = set_var_type;
579 g_signal_connect_after (sheet->var_type_renderer,
580 "editing-started", G_CALLBACK (on_edit_start),
581 sheet->var_type_dispatch);
583 sheet->row_popup = create_var_row_header_popup_menu (sheet);
586 g_signal_connect (sheet, "selection-changed",
587 G_CALLBACK (set_var_popup_sensitivity), sheet);
589 g_signal_connect (sheet, "row-header-pressed",
590 G_CALLBACK (show_variables_row_popup), sheet);
592 g_signal_connect_swapped (sheet, "value-changed",
593 G_CALLBACK (change_var_property), sheet);
595 g_signal_connect (sheet, "row-moved",
596 G_CALLBACK (move_variable), NULL);