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 (GtkCellRenderer *renderer,
41 GtkCellEditable *editable,
45 PsppireVariableSheet *sheet = PSPPIRE_VARIABLE_SHEET (user_data);
46 gint row = -1, col = -1;
47 ssw_sheet_get_active_cell (SSW_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 ssw_sheet_get_active_cell (SSW_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 ssw_sheet_get_active_cell (SSW_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 (SswSheet *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 (SswSheet *sheet)
258 SswRange *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 (SswSheet *sheet, gpointer selection, gpointer p)
301 PsppireVariableSheet *var_sheet = PSPPIRE_VARIABLE_SHEET (sheet);
302 SswRange *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 (SswSheet *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 ssw_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;
447 parent_class = g_type_class_peek_parent (class);
451 psppire_variable_sheet_new (void)
453 PsppireVarSheetHeader *vsh =
454 g_object_new (PSPPIRE_TYPE_VAR_SHEET_HEADER, NULL);
457 g_object_new (PSPPIRE_TYPE_VARIABLE_SHEET,
458 "select-renderer-func", select_renderer_func,
460 "forward-conversion", var_sheet_data_to_string,
463 return GTK_WIDGET (obj);
467 move_variable (PsppireVariableSheet *sheet, gint from, gint to, gpointer ud)
469 PsppireDict *dict = NULL;
470 g_object_get (sheet, "data-model", &dict, NULL);
475 struct variable *var = psppire_dict_get_variable (dict, from);
480 /* The index refers to the final position, so if the source
481 is less than the destination, then we must subtract 1, to
482 account for the position vacated by the source */
485 dict_reorder_var (dict->dict, var, new_pos);
489 psppire_variable_sheet_init (PsppireVariableSheet *sheet)
491 sheet->dispose_has_run = FALSE;
493 sheet->value_label_renderer = gtk_cell_renderer_text_new ();
494 g_signal_connect (sheet->value_label_renderer,
495 "editing-started", G_CALLBACK (set_value_labels),
498 sheet->missing_values_renderer = gtk_cell_renderer_text_new ();
499 g_signal_connect (sheet->missing_values_renderer,
500 "editing-started", G_CALLBACK (set_missing_values),
503 sheet->var_type_renderer = gtk_cell_renderer_text_new ();
504 g_signal_connect (sheet->var_type_renderer,
505 "editing-started", G_CALLBACK (set_var_type),
508 sheet->row_popup = create_var_row_header_popup_menu (sheet);
511 g_signal_connect (sheet, "selection-changed",
512 G_CALLBACK (set_var_popup_sensitivity), sheet);
514 g_signal_connect (sheet, "row-header-pressed",
515 G_CALLBACK (show_variables_row_popup), sheet);
517 g_signal_connect_swapped (sheet, "value-changed",
518 G_CALLBACK (change_var_property), sheet);
520 g_signal_connect (sheet, "row-moved",
521 G_CALLBACK (move_variable), NULL);