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. */
129 obj->labs = val_labs_create (0);
133 psppire_val_labs_dialog_finalize (GObject *obj)
135 PsppireValLabsDialog *dialog = PSPPIRE_VAL_LABS_DIALOG (obj);
137 val_labs_destroy (dialog->labs);
138 g_free (dialog->encoding);
140 G_OBJECT_CLASS (psppire_val_labs_dialog_parent_class)->finalize (obj);
143 PsppireValLabsDialog *
144 psppire_val_labs_dialog_new (const struct variable *var)
146 return PSPPIRE_VAL_LABS_DIALOG (
147 g_object_new (PSPPIRE_TYPE_VAL_LABS_DIALOG,
153 psppire_val_labs_dialog_run (GtkWindow *parent_window,
154 const struct variable *var)
156 PsppireValLabsDialog *dialog;
157 struct val_labs *labs;
159 dialog = psppire_val_labs_dialog_new (var);
160 gtk_window_set_transient_for (GTK_WINDOW (dialog), parent_window);
161 gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
162 gtk_widget_show (GTK_WIDGET (dialog));
164 labs = (psppire_dialog_run (PSPPIRE_DIALOG (dialog)) == GTK_RESPONSE_OK
165 ? val_labs_clone (psppire_val_labs_dialog_get_value_labels (dialog))
168 gtk_widget_destroy (GTK_WIDGET (dialog));
173 /* This callback occurs when the text in the label entry box
176 on_label_entry_change (GtkEntry *entry, gpointer data)
180 PsppireValLabsDialog *dialog = data;
181 g_assert (dialog->labs);
183 text = gtk_entry_get_text (GTK_ENTRY (dialog->value_entry));
185 text_to_value__ (text, &dialog->format, dialog->encoding, &v);
187 if (val_labs_find (dialog->labs, &v))
189 gtk_widget_set_sensitive (dialog->change_button, TRUE);
190 gtk_widget_set_sensitive (dialog->add_button, FALSE);
194 gtk_widget_set_sensitive (dialog->change_button, FALSE);
195 gtk_widget_set_sensitive (dialog->add_button, TRUE);
198 value_destroy (&v, val_labs_get_width (dialog->labs));
201 /* This callback occurs when Enter is pressed in the label entry box. */
203 on_label_entry_activate (GtkEntry *entry, gpointer data)
205 PsppireValLabsDialog *dialog = data;
209 /* Return the value-label pair currently selected in the dialog box */
211 /* Set the TREEVIEW list cursor to the item which has the value VAL */
213 select_treeview_from_value (GtkTreeView *treeview, union value *val)
218 We do this with a linear search through the model --- hardly
219 efficient, but the list is short ... */
222 GtkTreeModel * model = gtk_tree_view_get_model (treeview);
225 for (success = gtk_tree_model_get_iter_first (model, &iter);
227 success = gtk_tree_model_iter_next (model, &iter))
232 gtk_tree_model_get_value (model, &iter, 1, &gvalue);
234 v.f = g_value_get_double (&gvalue);
236 if (0 == memcmp (&v, val, sizeof (union value)))
242 path = gtk_tree_model_get_path (model, &iter);
245 gtk_tree_view_set_cursor (treeview, path, 0, 0);
246 gtk_tree_path_free (path);
252 /* This callback occurs when the text in the value entry box is
255 on_value_entry_change (GtkEntry *entry, gpointer data)
259 PsppireValLabsDialog *dialog = data;
261 const gchar *text = gtk_entry_get_text (GTK_ENTRY (dialog->value_entry));
264 text_to_value__ (text, &dialog->format, dialog->encoding, &v);
266 g_signal_handler_block (GTK_ENTRY (dialog->label_entry),
267 dialog->change_handler_id);
269 gtk_entry_set_text (GTK_ENTRY (dialog->label_entry),"");
272 if ((s = val_labs_find (dialog->labs, &v)))
274 gtk_entry_set_text (GTK_ENTRY (dialog->label_entry), s);
275 gtk_widget_set_sensitive (dialog->add_button, FALSE);
276 gtk_widget_set_sensitive (dialog->remove_button, TRUE);
277 select_treeview_from_value (GTK_TREE_VIEW (dialog->treeview), &v);
281 gtk_widget_set_sensitive (dialog->remove_button, FALSE);
282 gtk_widget_set_sensitive (dialog->add_button, TRUE);
285 g_signal_handler_unblock (GTK_ENTRY (dialog->label_entry),
286 dialog->change_handler_id);
288 value_destroy (&v, val_labs_get_width (dialog->labs));
291 /* This callback occurs when Enter is pressed in the value entry box. */
293 on_value_entry_activate (GtkEntry *entry, gpointer data)
295 PsppireValLabsDialog *dialog = data;
297 gtk_widget_grab_focus (dialog->label_entry);
301 get_selected_tuple (PsppireValLabsDialog *dialog,
302 union value *valuep, const char **label)
304 GtkTreeView *treeview = GTK_TREE_VIEW (dialog->treeview);
307 GValue the_value = {0};
310 GtkTreeSelection* sel = gtk_tree_view_get_selection (treeview);
312 GtkTreeModel * model = gtk_tree_view_get_model (treeview);
314 if (! gtk_tree_selection_get_selected (sel, &model, &iter))
317 gtk_tree_model_get_value (model, &iter, 1, &the_value);
319 value.f = g_value_get_double (&the_value);
320 g_value_unset (&the_value);
326 struct val_lab *vl = val_labs_lookup (dialog->labs, &value);
328 *label = val_lab_get_escaped_label (vl);
335 static void repopulate_dialog (PsppireValLabsDialog *dialog);
337 /* Callback which occurs when the "Change" button is clicked */
339 on_change (GtkWidget *w, gpointer data)
341 PsppireValLabsDialog *dialog = data;
346 do_change (PsppireValLabsDialog *dialog)
348 const gchar *val_text = gtk_entry_get_text (GTK_ENTRY (dialog->value_entry));
352 if (text_to_value__ (val_text, &dialog->format, dialog->encoding, &v))
354 val_labs_replace (dialog->labs, &v,
355 gtk_entry_get_text (GTK_ENTRY (dialog->label_entry)));
357 gtk_widget_set_sensitive (dialog->change_button, FALSE);
359 repopulate_dialog (dialog);
360 gtk_widget_grab_focus (dialog->value_entry);
362 value_destroy (&v, val_labs_get_width (dialog->labs));
366 /* Callback which occurs when the "Add" button is clicked */
368 on_add (GtkWidget *w, gpointer data)
370 PsppireValLabsDialog *dialog = data;
374 const gchar *text = gtk_entry_get_text (GTK_ENTRY (dialog->value_entry));
376 if (text_to_value__ (text, &dialog->format, dialog->encoding, &v))
378 if (val_labs_add (dialog->labs, &v,
380 (GTK_ENTRY (dialog->label_entry))))
382 gtk_widget_set_sensitive (dialog->add_button, FALSE);
384 repopulate_dialog (dialog);
385 gtk_widget_grab_focus (dialog->value_entry);
388 value_destroy (&v, val_labs_get_width (dialog->labs));
392 /* Callback which occurs when the "Remove" button is clicked */
394 on_remove (GtkWidget *w, gpointer data)
396 PsppireValLabsDialog *dialog = data;
401 if (! get_selected_tuple (dialog, &value, NULL))
404 vl = val_labs_lookup (dialog->labs, &value);
406 val_labs_remove (dialog->labs, vl);
408 repopulate_dialog (dialog);
409 gtk_widget_grab_focus (dialog->value_entry);
411 gtk_widget_set_sensitive (dialog->remove_button, FALSE);
416 /* Callback which occurs when a line item is selected in the list of
417 value--label pairs.*/
419 on_select_row (GtkTreeView *treeview, gpointer data)
421 PsppireValLabsDialog *dialog = data;
424 const char *label = NULL;
428 if (! get_selected_tuple (dialog, &value, &label))
431 text = value_to_text__ (value, &dialog->format, dialog->encoding);
433 g_signal_handler_block (GTK_ENTRY (dialog->value_entry),
434 dialog->value_handler_id);
436 gtk_entry_set_text (GTK_ENTRY (dialog->value_entry), text);
438 g_signal_handler_unblock (GTK_ENTRY (dialog->value_entry),
439 dialog->value_handler_id);
442 g_signal_handler_block (GTK_ENTRY (dialog->label_entry),
443 dialog->change_handler_id);
446 gtk_entry_set_text (GTK_ENTRY (dialog->label_entry),
449 g_signal_handler_unblock (GTK_ENTRY (dialog->label_entry),
450 dialog->change_handler_id);
452 gtk_widget_set_sensitive (dialog->remove_button, TRUE);
453 gtk_widget_set_sensitive (dialog->change_button, FALSE);
457 /* Create a new dialog box
458 (there should normally be only one)*/
460 psppire_val_labs_dialog_constructor (GType type,
462 GObjectConstructParam *properties)
464 PsppireValLabsDialog *dialog;
465 GtkTreeViewColumn *column;
467 GtkCellRenderer *renderer ;
469 GtkBuilder *xml = builder_new ("val-labs-dialog.ui");
471 GtkContainer *content_area;
474 obj = G_OBJECT_CLASS (psppire_val_labs_dialog_parent_class)->constructor (
475 type, n_properties, properties);
476 dialog = PSPPIRE_VAL_LABS_DIALOG (obj);
478 g_object_set (dialog, "help_page", "VALUE-LABELS",
479 "title", _("Value Labels"), NULL);
481 content_area = GTK_CONTAINER (PSPPIRE_DIALOG (dialog));
482 gtk_container_add (GTK_CONTAINER (content_area),
483 get_widget_assert (xml, "val-labs-dialog"));
485 dialog->value_entry = get_widget_assert (xml,"value_entry");
486 dialog->label_entry = get_widget_assert (xml,"label_entry");
488 dialog->add_button = get_widget_assert (xml, "val_labs_add");
489 dialog->remove_button = get_widget_assert (xml, "val_labs_remove");
490 dialog->change_button = get_widget_assert (xml, "val_labs_change");
492 dialog->treeview = get_widget_assert (xml,"treeview1");
494 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (dialog->treeview), FALSE);
496 renderer = gtk_cell_renderer_text_new ();
498 column = gtk_tree_view_column_new_with_attributes ("Title",
504 gtk_tree_view_append_column (GTK_TREE_VIEW (dialog->treeview), column);
506 dialog->change_handler_id =
507 g_signal_connect (dialog->label_entry,
509 G_CALLBACK (on_label_entry_change), dialog);
510 g_signal_connect (dialog->label_entry, "activate",
511 G_CALLBACK (on_label_entry_activate), dialog);
513 dialog->value_handler_id =
514 g_signal_connect (dialog->value_entry,
516 G_CALLBACK (on_value_entry_change), dialog);
517 g_signal_connect (dialog->value_entry, "activate",
518 G_CALLBACK (on_value_entry_activate), dialog);
520 g_signal_connect (dialog->change_button,
522 G_CALLBACK (on_change), dialog);
525 g_signal_connect (dialog->treeview, "cursor-changed",
526 G_CALLBACK (on_select_row), dialog);
528 g_signal_connect (dialog->remove_button, "clicked",
529 G_CALLBACK (on_remove), dialog);
531 g_signal_connect (dialog->add_button, "clicked",
532 G_CALLBACK (on_add), dialog);
536 g_object_unref (xml);
542 /* Populate the components of the dialog box, from the 'labs' member
545 repopulate_dialog (PsppireValLabsDialog *dialog)
547 const struct val_lab **labels;
553 GtkListStore *list_store = gtk_list_store_new (2,
557 g_signal_handler_block (GTK_ENTRY (dialog->label_entry),
558 dialog->change_handler_id);
559 g_signal_handler_block (GTK_ENTRY (dialog->value_entry),
560 dialog->value_handler_id);
562 gtk_entry_set_text (GTK_ENTRY (dialog->value_entry), "");
563 gtk_entry_set_text (GTK_ENTRY (dialog->label_entry), "");
565 g_signal_handler_unblock (GTK_ENTRY (dialog->value_entry),
566 dialog->value_handler_id);
567 g_signal_handler_unblock (GTK_ENTRY (dialog->label_entry),
568 dialog->change_handler_id);
570 labels = val_labs_sorted (dialog->labs);
571 n_labels = val_labs_count (dialog->labs);
572 for (i = 0; i < n_labels; i++)
574 const struct val_lab *vl = labels[i];
577 value_to_text__ (vl->value, &dialog->format, dialog->encoding);
579 gchar *const text = g_strdup_printf (_("%s = `%s'"), vstr,
580 val_lab_get_escaped_label (vl));
582 gtk_list_store_append (list_store, &iter);
583 gtk_list_store_set (list_store, &iter,
593 gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->treeview),
594 GTK_TREE_MODEL (list_store));
596 g_object_unref (list_store);
601 psppire_val_labs_dialog_set_variable (PsppireValLabsDialog *dialog,
602 const struct variable *var)
604 val_labs_destroy (dialog->labs);
607 g_free (dialog->encoding);
608 dialog->encoding = NULL;
612 dialog->labs = val_labs_clone (var_get_value_labels (var));
613 dialog->encoding = g_strdup (var_get_encoding (var));
614 dialog->format = *var_get_print_format (var);
617 dialog->format = F_8_0;
619 if (dialog->labs == NULL)
620 dialog->labs = val_labs_create (var_get_width (var));
622 repopulate_dialog (dialog);
625 const struct val_labs *
626 psppire_val_labs_dialog_get_value_labels (const PsppireValLabsDialog *dialog)