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, JMD_TYPE_SHEET)
40 set_var_type (GtkCellRenderer *renderer,
41 GtkCellEditable *editable,
45 PsppireVariableSheet *sheet = PSPPIRE_VARIABLE_SHEET (user_data);
46 gint row = -1, col = -1;
47 jmd_sheet_get_active_cell (JMD_SHEET (sheet), &col, &row);
49 PsppireDict *dict = NULL;
50 g_object_get (sheet, "data-model", &dict, NULL);
52 struct variable *var =
53 psppire_dict_get_variable (PSPPIRE_DICT (dict), row);
55 const struct fmt_spec *format = var_get_write_format (var);
56 struct fmt_spec fmt = *format;
57 GtkWindow *win = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (sheet)));
58 if (GTK_RESPONSE_OK == psppire_var_type_dialog_run (win, &fmt))
60 var_set_width_and_formats (var, fmt_var_width (&fmt), &fmt, &fmt);
65 set_missing_values (GtkCellRenderer *renderer,
66 GtkCellEditable *editable,
70 PsppireVariableSheet *sheet = PSPPIRE_VARIABLE_SHEET (user_data);
71 gint row = -1, col = -1;
72 jmd_sheet_get_active_cell (JMD_SHEET (sheet), &col, &row);
74 PsppireDict *dict = NULL;
75 g_object_get (sheet, "data-model", &dict, NULL);
77 struct variable *var =
78 psppire_dict_get_variable (PSPPIRE_DICT (dict), row);
80 struct missing_values mv;
81 if (GTK_RESPONSE_OK ==
82 psppire_missing_val_dialog_run (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (sheet))),
85 var_set_missing_values (var, &mv);
92 set_value_labels (GtkCellRenderer *renderer,
93 GtkCellEditable *editable,
97 PsppireVariableSheet *sheet = PSPPIRE_VARIABLE_SHEET (user_data);
98 gint row = -1, col = -1;
99 jmd_sheet_get_active_cell (JMD_SHEET (sheet), &col, &row);
101 PsppireDict *dict = NULL;
102 g_object_get (sheet, "data-model", &dict, NULL);
104 struct variable *var =
105 psppire_dict_get_variable (PSPPIRE_DICT (dict), row);
107 struct val_labs *vls =
108 psppire_val_labs_dialog_run (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (sheet))), var);
112 var_set_value_labels (var, vls);
113 val_labs_destroy (vls);
117 static GtkCellRenderer *
118 create_spin_renderer (GType type)
120 GtkCellRenderer *r = gtk_cell_renderer_spin_new ();
122 GtkAdjustment *adj = gtk_adjustment_new (0,
133 static GtkCellRenderer *
134 create_combo_renderer (GType type)
136 GtkListStore *list_store = gtk_list_store_new (2, G_TYPE_INT, G_TYPE_STRING);
138 GEnumClass *ec = g_type_class_ref (type);
140 const GEnumValue *ev ;
141 for (ev = ec->values; ev->value_name; ++ev)
145 gtk_list_store_append (list_store, &iter);
147 gtk_list_store_set (list_store, &iter,
149 1, gettext (ev->value_nick),
153 GtkCellRenderer *r = gtk_cell_renderer_combo_new ();
164 static GtkCellRenderer *spin_renderer;
165 static GtkCellRenderer *column_width_renderer;
166 static GtkCellRenderer *measure_renderer;
167 static GtkCellRenderer *alignment_renderer;
169 static GtkCellRenderer *
170 select_renderer_func (PsppireVariableSheet *sheet, gint col, gint row, GType type, gpointer ud)
173 spin_renderer = create_spin_renderer (type);
175 if (col == DICT_TVM_COL_ROLE && !column_width_renderer)
176 column_width_renderer = create_combo_renderer (type);
178 if (col == DICT_TVM_COL_MEASURE && !measure_renderer)
179 measure_renderer = create_combo_renderer (type);
181 if (col == DICT_TVM_COL_ALIGNMENT && !alignment_renderer)
182 alignment_renderer = create_combo_renderer (type);
186 case DICT_TVM_COL_WIDTH:
187 case DICT_TVM_COL_DECIMAL:
188 case DICT_TVM_COL_COLUMNS:
189 return spin_renderer;
191 case DICT_TVM_COL_TYPE:
192 return sheet->var_type_renderer;
194 case DICT_TVM_COL_VALUE_LABELS:
195 return sheet->value_label_renderer;
197 case DICT_TVM_COL_MISSING_VALUES:
198 return sheet->missing_values_renderer;
200 case DICT_TVM_COL_ALIGNMENT:
201 return alignment_renderer;
203 case DICT_TVM_COL_MEASURE:
204 return measure_renderer;
206 case DICT_TVM_COL_ROLE:
207 return column_width_renderer;
216 show_variables_row_popup (JmdSheet *sheet, int row, uint button,
217 uint state, gpointer p)
219 PsppireVariableSheet *var_sheet = PSPPIRE_VARIABLE_SHEET (sheet);
220 GListModel *vmodel = NULL;
221 g_object_get (sheet, "vmodel", &vmodel, NULL);
225 guint n_items = g_list_model_get_n_items (vmodel);
233 g_object_set_data (G_OBJECT (var_sheet->row_popup), "item",
234 GINT_TO_POINTER (row));
236 gtk_menu_popup_at_pointer (GTK_MENU (var_sheet->row_popup), NULL);
240 insert_new_variable_var (PsppireVariableSheet *var_sheet)
242 gint item = GPOINTER_TO_INT (g_object_get_data
243 (G_OBJECT (var_sheet->row_popup),
246 PsppireDict *dict = NULL;
247 g_object_get (var_sheet, "data-model", &dict, NULL);
249 psppire_dict_insert_variable (dict, item, NULL);
251 gtk_widget_queue_draw (GTK_WIDGET (var_sheet));
256 delete_variables (JmdSheet *sheet)
258 JmdRange *range = sheet->selection;
260 PsppireDict *dict = NULL;
261 g_object_get (sheet, "data-model", &dict, NULL);
263 psppire_dict_delete_variables (dict, range->start_y,
264 (range->end_y - range->start_y + 1));
266 gtk_widget_queue_draw (GTK_WIDGET (sheet));
270 create_var_row_header_popup_menu (PsppireVariableSheet *var_sheet)
272 GtkWidget *menu = gtk_menu_new ();
275 gtk_menu_item_new_with_mnemonic (_("_Insert Variable"));
276 g_signal_connect_swapped (item, "activate", G_CALLBACK (insert_new_variable_var),
278 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
280 item = gtk_separator_menu_item_new ();
281 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
283 var_sheet->clear_variables_menu_item =
284 gtk_menu_item_new_with_mnemonic (_("Cl_ear Variables"));
286 g_signal_connect_swapped (var_sheet->clear_variables_menu_item, "activate",
287 G_CALLBACK (delete_variables), var_sheet);
289 gtk_widget_set_sensitive (var_sheet->clear_variables_menu_item, FALSE);
290 gtk_menu_shell_append (GTK_MENU_SHELL (menu),
291 var_sheet->clear_variables_menu_item);
293 gtk_widget_show_all (menu);
299 set_var_popup_sensitivity (JmdSheet *sheet, gpointer selection, gpointer p)
301 PsppireVariableSheet *var_sheet = PSPPIRE_VARIABLE_SHEET (sheet);
302 JmdRange *range = selection;
303 gint width = gtk_tree_model_get_n_columns (sheet->data_model);
305 gboolean whole_row_selected = (range->start_x == 0 &&
306 range->end_x == width - 1 - 1);
307 /* PsppireDict has an "extra" column: TVM_COL_VAR ^^^ */
308 gtk_widget_set_sensitive (var_sheet->clear_variables_menu_item,
315 change_var_property (PsppireVariableSheet *var_sheet, gint col, gint row, const GValue *value)
317 PsppireDict *dict = NULL;
318 g_object_get (var_sheet, "data-model", &dict, NULL);
320 /* Return the IDXth variable */
321 struct variable *var = psppire_dict_get_variable (dict, row);
324 var = psppire_dict_insert_variable (dict, row, NULL);
328 case DICT_TVM_COL_NAME:
330 const char *name = g_value_get_string (value);
331 if (psppire_dict_check_name (dict, name, FALSE))
332 dict_rename_var (dict->dict, var, g_value_get_string (value));
335 case DICT_TVM_COL_WIDTH:
337 gint width = g_value_get_int (value);
338 if (var_is_numeric (var))
340 struct fmt_spec format = *var_get_print_format (var);
341 fmt_change_width (&format, width, FMT_FOR_OUTPUT);
342 var_set_both_formats (var, &format);
346 var_set_width (var, width);
350 case DICT_TVM_COL_DECIMAL:
352 gint decimals = g_value_get_int (value);
355 struct fmt_spec format = *var_get_print_format (var);
356 fmt_change_decimals (&format, decimals, FMT_FOR_OUTPUT);
357 var_set_both_formats (var, &format);
361 case DICT_TVM_COL_LABEL:
362 var_set_label (var, g_value_get_string (value));
364 case DICT_TVM_COL_COLUMNS:
365 var_set_display_width (var, g_value_get_int (value));
367 case DICT_TVM_COL_MEASURE:
368 var_set_measure (var, g_value_get_int (value));
370 case DICT_TVM_COL_ALIGNMENT:
371 var_set_alignment (var, g_value_get_int (value));
373 case DICT_TVM_COL_ROLE:
374 var_set_role (var, g_value_get_int (value));
377 g_warning ("Changing unknown column %d of variable sheet column not supported",
384 var_sheet_data_to_string (JmdSheet *sheet, GtkTreeModel *m,
385 gint col, gint row, const GValue *in)
387 if (col >= n_DICT_COLS - 1) /* -1 because psppire-dict has an extra column */
390 const struct variable *var = psppire_dict_get_variable (PSPPIRE_DICT (m), row);
394 if (col == DICT_TVM_COL_TYPE)
396 const struct fmt_spec *print = var_get_print_format (var);
397 return strdup (fmt_gui_name (print->type));
399 else if (col == DICT_TVM_COL_MISSING_VALUES)
400 return missing_values_to_string (var, NULL);
401 else if (col == DICT_TVM_COL_VALUE_LABELS)
403 const struct val_labs *vls = var_get_value_labels (var);
404 if (vls == NULL || val_labs_count (vls) == 0)
405 return strdup (_("None"));
406 const struct val_lab **labels = val_labs_sorted (vls);
407 const struct val_lab *vl = labels[0];
408 gchar *vstr = value_to_text (vl->value, var);
409 char *text = xasprintf (_("{%s, %s}..."), vstr,
410 val_lab_get_escaped_label (vl));
416 return jmd_sheet_default_forward_conversion (sheet, m, col, row, in);
421 static GObjectClass * parent_class = NULL;
424 psppire_variable_sheet_dispose (GObject *obj)
426 PsppireVariableSheet *sheet = PSPPIRE_VARIABLE_SHEET (obj);
428 if (sheet->dispose_has_run)
431 sheet->dispose_has_run = TRUE;
433 g_object_unref (sheet->value_label_renderer);
434 g_object_unref (sheet->missing_values_renderer);
435 g_object_unref (sheet->var_type_renderer);
437 /* Chain up to the parent class */
438 G_OBJECT_CLASS (parent_class)->dispose (obj);
442 psppire_variable_sheet_class_init (PsppireVariableSheetClass *class)
444 GObjectClass *object_class = G_OBJECT_CLASS (class);
445 object_class->dispose = psppire_variable_sheet_dispose;
449 psppire_variable_sheet_new (void)
451 PsppireVarSheetHeader *vsh =
452 g_object_new (PSPPIRE_TYPE_VAR_SHEET_HEADER, NULL);
455 g_object_new (PSPPIRE_TYPE_VARIABLE_SHEET,
456 "select-renderer-func", select_renderer_func,
458 "forward-conversion", var_sheet_data_to_string,
461 return GTK_WIDGET (obj);
465 psppire_variable_sheet_init (PsppireVariableSheet *sheet)
467 sheet->dispose_has_run = FALSE;
469 sheet->value_label_renderer = gtk_cell_renderer_text_new ();
470 g_signal_connect (sheet->value_label_renderer,
471 "editing-started", G_CALLBACK (set_value_labels),
474 sheet->missing_values_renderer = gtk_cell_renderer_text_new ();
475 g_signal_connect (sheet->missing_values_renderer,
476 "editing-started", G_CALLBACK (set_missing_values),
479 sheet->var_type_renderer = gtk_cell_renderer_text_new ();
480 g_signal_connect (sheet->var_type_renderer,
481 "editing-started", G_CALLBACK (set_var_type),
484 sheet->row_popup = create_var_row_header_popup_menu (sheet);
487 g_signal_connect (sheet, "selection-changed",
488 G_CALLBACK (set_var_popup_sensitivity), sheet);
490 g_signal_connect (sheet, "row-header-pressed",
491 G_CALLBACK (show_variables_row_popup), sheet);
493 g_signal_connect_swapped (sheet, "value-changed",
494 G_CALLBACK (change_var_property), sheet);