1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2005, 2009, 2010, 2011, 2012, 2015, 2016,
3 2020 Free Software Foundation
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19 /* This module describes the behaviour of the Value Labels dialog box,
20 used for input of the value labels in the variable sheet */
24 #include "ui/gui/val-labs-dialog.h"
28 #include "data/value-labels.h"
29 #include "data/format.h"
30 #include "libpspp/i18n.h"
31 #include "ui/gui/builder-wrapper.h"
32 #include "ui/gui/helper.h"
35 #define _(msgid) gettext (msgid)
36 #define N_(msgid) msgid
38 static GObject *psppire_val_labs_dialog_constructor (GType type, guint,
39 GObjectConstructParam *);
40 static void psppire_val_labs_dialog_finalize (GObject *);
42 G_DEFINE_TYPE (PsppireValLabsDialog,
43 psppire_val_labs_dialog,
52 static void do_change (PsppireValLabsDialog *);
55 psppire_val_labs_dialog_set_property (GObject *object,
60 PsppireValLabsDialog *obj = PSPPIRE_VAL_LABS_DIALOG (object);
65 psppire_val_labs_dialog_set_variable (obj, g_value_get_pointer (value));
67 case PROP_VALUE_LABELS:
69 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
75 psppire_val_labs_dialog_get_property (GObject *object,
80 PsppireValLabsDialog *obj = PSPPIRE_VAL_LABS_DIALOG (object);
84 case PROP_VALUE_LABELS:
85 g_value_set_pointer (value, obj->labs);
89 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
95 psppire_val_labs_dialog_class_init (PsppireValLabsDialogClass *class)
97 GObjectClass *gobject_class;
98 gobject_class = G_OBJECT_CLASS (class);
100 gobject_class->constructor = psppire_val_labs_dialog_constructor;
101 gobject_class->finalize = psppire_val_labs_dialog_finalize;
102 gobject_class->set_property = psppire_val_labs_dialog_set_property;
103 gobject_class->get_property = psppire_val_labs_dialog_get_property;
105 g_object_class_install_property (
106 gobject_class, PROP_VARIABLE,
107 g_param_spec_pointer ("variable",
109 "Variable whose value labels are to be edited. The "
110 "variable's print format and encoding are also used "
114 g_object_class_install_property (
115 gobject_class, PROP_VALUE_LABELS,
116 g_param_spec_pointer ("value-labels",
118 "Edited value labels.",
123 psppire_val_labs_dialog_init (PsppireValLabsDialog *obj)
125 /* We do all of our work on widgets in the constructor function, because that
126 runs after the construction properties have been set. Otherwise
127 PsppireDialog's "orientation" property hasn't been set and therefore we
128 have no box to populate. */
130 obj->labs = val_labs_create (0);
134 psppire_val_labs_dialog_finalize (GObject *obj)
136 PsppireValLabsDialog *dialog = PSPPIRE_VAL_LABS_DIALOG (obj);
138 val_labs_destroy (dialog->labs);
139 g_free (dialog->encoding);
141 G_OBJECT_CLASS (psppire_val_labs_dialog_parent_class)->finalize (obj);
144 PsppireValLabsDialog *
145 psppire_val_labs_dialog_new (const struct variable *var)
147 PsppireValLabsDialog *obj
148 = PSPPIRE_VAL_LABS_DIALOG (g_object_new (PSPPIRE_TYPE_VAL_LABS_DIALOG,
156 psppire_val_labs_dialog_run (GtkWindow *parent_window,
157 const struct variable *var)
159 PsppireValLabsDialog *dialog;
160 struct val_labs *labs;
162 dialog = psppire_val_labs_dialog_new (var);
163 gtk_window_set_transient_for (GTK_WINDOW (dialog), parent_window);
164 gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
165 gtk_widget_show (GTK_WIDGET (dialog));
167 gint response = psppire_dialog_run (PSPPIRE_DIALOG (dialog));
170 case GTK_RESPONSE_OK:
171 labs = val_labs_clone (psppire_val_labs_dialog_get_value_labels (dialog));
178 gtk_widget_destroy (GTK_WIDGET (dialog));
183 /* This callback occurs when the text in the label entry box
186 on_label_entry_change (GtkEntry *entry, gpointer data)
190 PsppireValLabsDialog *dialog = data;
191 g_assert (dialog->labs);
193 text = gtk_entry_get_text (GTK_ENTRY (dialog->value_entry));
195 text_to_value__ (text, dialog->format, dialog->encoding, &v);
197 if (val_labs_find (dialog->labs, &v))
199 gtk_widget_set_sensitive (dialog->change_button, TRUE);
200 gtk_widget_set_sensitive (dialog->add_button, FALSE);
204 gtk_widget_set_sensitive (dialog->change_button, FALSE);
205 gtk_widget_set_sensitive (dialog->add_button, TRUE);
208 value_destroy (&v, val_labs_get_width (dialog->labs));
211 /* This callback occurs when Enter is pressed in the label entry box. */
213 on_label_entry_activate (GtkEntry *entry, gpointer data)
215 PsppireValLabsDialog *dialog = data;
219 /* Return the value-label pair currently selected in the dialog box */
221 /* Set the TREEVIEW list cursor to the item which has the value VAL */
223 select_treeview_from_value (GtkTreeView *treeview, union value *val)
228 We do this with a linear search through the model --- hardly
229 efficient, but the list is short ... */
232 GtkTreeModel * model = gtk_tree_view_get_model (treeview);
235 for (success = gtk_tree_model_get_iter_first (model, &iter);
237 success = gtk_tree_model_iter_next (model, &iter))
242 gtk_tree_model_get_value (model, &iter, 1, &gvalue);
244 v.f = g_value_get_double (&gvalue);
246 if (0 == memcmp (&v, val, sizeof (union value)))
252 path = gtk_tree_model_get_path (model, &iter);
255 gtk_tree_view_set_cursor (treeview, path, 0, 0);
256 gtk_tree_path_free (path);
262 /* This callback occurs when the text in the value entry box is
265 on_value_entry_change (GtkEntry *entry, gpointer data)
269 PsppireValLabsDialog *dialog = data;
271 const gchar *text = gtk_entry_get_text (GTK_ENTRY (dialog->value_entry));
274 text_to_value__ (text, dialog->format, dialog->encoding, &v);
276 g_signal_handler_block (GTK_ENTRY (dialog->label_entry),
277 dialog->change_handler_id);
279 gtk_entry_set_text (GTK_ENTRY (dialog->label_entry),"");
282 if ((s = val_labs_find (dialog->labs, &v)))
284 gtk_entry_set_text (GTK_ENTRY (dialog->label_entry), s);
285 gtk_widget_set_sensitive (dialog->add_button, FALSE);
286 gtk_widget_set_sensitive (dialog->remove_button, TRUE);
287 select_treeview_from_value (GTK_TREE_VIEW (dialog->treeview), &v);
291 gtk_widget_set_sensitive (dialog->remove_button, FALSE);
292 gtk_widget_set_sensitive (dialog->add_button, TRUE);
295 g_signal_handler_unblock (GTK_ENTRY (dialog->label_entry),
296 dialog->change_handler_id);
298 value_destroy (&v, val_labs_get_width (dialog->labs));
301 /* This callback occurs when Enter is pressed in the value entry box. */
303 on_value_entry_activate (GtkEntry *entry, gpointer data)
305 PsppireValLabsDialog *dialog = data;
307 gtk_widget_grab_focus (dialog->label_entry);
311 get_selected_tuple (PsppireValLabsDialog *dialog,
312 union value *valuep, const char **label)
314 GtkTreeView *treeview = GTK_TREE_VIEW (dialog->treeview);
317 GValue the_value = {0};
320 GtkTreeSelection* sel = gtk_tree_view_get_selection (treeview);
322 GtkTreeModel * model = gtk_tree_view_get_model (treeview);
324 if (! gtk_tree_selection_get_selected (sel, &model, &iter))
327 gtk_tree_model_get_value (model, &iter, 1, &the_value);
329 value.f = g_value_get_double (&the_value);
330 g_value_unset (&the_value);
336 struct val_lab *vl = val_labs_lookup (dialog->labs, &value);
338 *label = val_lab_get_escaped_label (vl);
345 static void repopulate_dialog (PsppireValLabsDialog *dialog);
347 /* Callback which occurs when the "Change" button is clicked */
349 on_change (GtkWidget *w, gpointer data)
351 PsppireValLabsDialog *dialog = data;
356 do_change (PsppireValLabsDialog *dialog)
358 const gchar *val_text = gtk_entry_get_text (GTK_ENTRY (dialog->value_entry));
362 if (text_to_value__ (val_text, dialog->format, dialog->encoding, &v))
364 val_labs_replace (dialog->labs, &v,
365 gtk_entry_get_text (GTK_ENTRY (dialog->label_entry)));
367 gtk_widget_set_sensitive (dialog->change_button, FALSE);
369 repopulate_dialog (dialog);
370 gtk_widget_grab_focus (dialog->value_entry);
372 value_destroy (&v, val_labs_get_width (dialog->labs));
376 /* Callback which occurs when the "Add" button is clicked */
378 on_add (GtkWidget *w, gpointer data)
380 PsppireValLabsDialog *dialog = data;
384 const gchar *text = gtk_entry_get_text (GTK_ENTRY (dialog->value_entry));
386 if (text_to_value__ (text, dialog->format, dialog->encoding, &v))
388 if (val_labs_add (dialog->labs, &v,
390 (GTK_ENTRY (dialog->label_entry))))
392 gtk_widget_set_sensitive (dialog->add_button, FALSE);
394 repopulate_dialog (dialog);
395 gtk_widget_grab_focus (dialog->value_entry);
398 value_destroy (&v, val_labs_get_width (dialog->labs));
402 /* Callback which occurs when the "Remove" button is clicked */
404 on_remove (GtkWidget *w, gpointer data)
406 PsppireValLabsDialog *dialog = data;
411 if (! get_selected_tuple (dialog, &value, NULL))
414 vl = val_labs_lookup (dialog->labs, &value);
416 val_labs_remove (dialog->labs, vl);
418 repopulate_dialog (dialog);
419 gtk_widget_grab_focus (dialog->value_entry);
421 gtk_widget_set_sensitive (dialog->remove_button, FALSE);
426 /* Callback which occurs when a line item is selected in the list of
427 value--label pairs.*/
429 on_select_row (GtkTreeView *treeview, gpointer data)
431 PsppireValLabsDialog *dialog = data;
434 const char *label = NULL;
438 if (! get_selected_tuple (dialog, &value, &label))
441 text = value_to_text__ (value, dialog->format, dialog->encoding);
443 g_signal_handler_block (GTK_ENTRY (dialog->value_entry),
444 dialog->value_handler_id);
446 gtk_entry_set_text (GTK_ENTRY (dialog->value_entry), text);
448 g_signal_handler_unblock (GTK_ENTRY (dialog->value_entry),
449 dialog->value_handler_id);
452 g_signal_handler_block (GTK_ENTRY (dialog->label_entry),
453 dialog->change_handler_id);
456 gtk_entry_set_text (GTK_ENTRY (dialog->label_entry),
459 g_signal_handler_unblock (GTK_ENTRY (dialog->label_entry),
460 dialog->change_handler_id);
462 gtk_widget_set_sensitive (dialog->remove_button, TRUE);
463 gtk_widget_set_sensitive (dialog->change_button, FALSE);
467 /* Create a new dialog box
468 (there should normally be only one)*/
470 psppire_val_labs_dialog_constructor (GType type,
472 GObjectConstructParam *properties)
474 PsppireValLabsDialog *dialog;
475 GtkTreeViewColumn *column;
477 GtkCellRenderer *renderer ;
479 GtkBuilder *xml = builder_new ("val-labs-dialog.ui");
481 GtkContainer *content_area;
484 obj = G_OBJECT_CLASS (psppire_val_labs_dialog_parent_class)->constructor (
485 type, n_properties, properties);
486 dialog = PSPPIRE_VAL_LABS_DIALOG (obj);
488 g_object_set (dialog, "help-page", "VALUE-LABELS",
489 "title", _("Value Labels"), NULL);
491 content_area = GTK_CONTAINER (PSPPIRE_DIALOG (dialog));
492 gtk_container_add (GTK_CONTAINER (content_area),
493 get_widget_assert (xml, "val-labs-dialog"));
495 dialog->value_entry = get_widget_assert (xml,"value_entry");
496 dialog->label_entry = get_widget_assert (xml,"label_entry");
498 dialog->add_button = get_widget_assert (xml, "val_labs_add");
499 dialog->remove_button = get_widget_assert (xml, "val_labs_remove");
500 dialog->change_button = get_widget_assert (xml, "val_labs_change");
502 dialog->treeview = get_widget_assert (xml,"treeview1");
504 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (dialog->treeview), FALSE);
506 renderer = gtk_cell_renderer_text_new ();
508 column = gtk_tree_view_column_new_with_attributes ("Title",
514 gtk_tree_view_append_column (GTK_TREE_VIEW (dialog->treeview), column);
516 dialog->change_handler_id =
517 g_signal_connect (dialog->label_entry,
519 G_CALLBACK (on_label_entry_change), dialog);
520 g_signal_connect (dialog->label_entry, "activate",
521 G_CALLBACK (on_label_entry_activate), dialog);
523 dialog->value_handler_id =
524 g_signal_connect (dialog->value_entry,
526 G_CALLBACK (on_value_entry_change), dialog);
527 g_signal_connect (dialog->value_entry, "activate",
528 G_CALLBACK (on_value_entry_activate), dialog);
530 g_signal_connect (dialog->change_button,
532 G_CALLBACK (on_change), dialog);
535 g_signal_connect (dialog->treeview, "cursor-changed",
536 G_CALLBACK (on_select_row), dialog);
538 g_signal_connect (dialog->remove_button, "clicked",
539 G_CALLBACK (on_remove), dialog);
541 g_signal_connect (dialog->add_button, "clicked",
542 G_CALLBACK (on_add), dialog);
544 /* dialog->labs must not be set here, because as a member of a singleton
545 class its value persists "between" objects. */
546 /* dialog->labs = NULL; */
548 g_object_unref (xml);
554 /* Populate the components of the dialog box, from the 'labs' member
557 repopulate_dialog (PsppireValLabsDialog *dialog)
559 const struct val_lab **labels;
565 GtkListStore *list_store = gtk_list_store_new (2,
569 g_signal_handler_block (GTK_ENTRY (dialog->label_entry),
570 dialog->change_handler_id);
571 g_signal_handler_block (GTK_ENTRY (dialog->value_entry),
572 dialog->value_handler_id);
574 gtk_entry_set_text (GTK_ENTRY (dialog->value_entry), "");
575 gtk_entry_set_text (GTK_ENTRY (dialog->label_entry), "");
577 g_signal_handler_unblock (GTK_ENTRY (dialog->value_entry),
578 dialog->value_handler_id);
579 g_signal_handler_unblock (GTK_ENTRY (dialog->label_entry),
580 dialog->change_handler_id);
582 labels = val_labs_sorted (dialog->labs);
583 n_labels = val_labs_count (dialog->labs);
584 for (i = 0; i < n_labels; i++)
586 const struct val_lab *vl = labels[i];
589 value_to_text__ (vl->value, dialog->format, dialog->encoding);
591 gchar *const text = g_strdup_printf (_("%s = `%s'"), vstr,
592 val_lab_get_escaped_label (vl));
594 gtk_list_store_append (list_store, &iter);
595 gtk_list_store_set (list_store, &iter,
605 gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->treeview),
606 GTK_TREE_MODEL (list_store));
608 g_object_unref (list_store);
612 psppire_val_labs_dialog_set_variable (PsppireValLabsDialog *dialog,
613 const struct variable *var)
615 val_labs_destroy (dialog->labs);
618 g_free (dialog->encoding);
619 dialog->encoding = NULL;
623 dialog->labs = val_labs_clone (var_get_value_labels (var));
624 dialog->encoding = g_strdup (var_get_encoding (var));
625 dialog->format = var_get_print_format (var);
628 dialog->format = F_8_0;
630 if (dialog->labs == NULL)
631 dialog->labs = val_labs_create (var_get_width (var));
633 repopulate_dialog (dialog);
636 const struct val_labs *
637 psppire_val_labs_dialog_get_value_labels (const PsppireValLabsDialog *dialog)