1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2005, 2009, 2010, 2011, 2012, 2015, 2016 Free Software Foundation
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/>. */
18 /* This module describes the behaviour of the Value Labels dialog box,
19 used for input of the value labels in the variable sheet */
23 #include "ui/gui/val-labs-dialog.h"
27 #include "data/value-labels.h"
28 #include "data/format.h"
29 #include "libpspp/i18n.h"
30 #include "ui/gui/builder-wrapper.h"
31 #include "ui/gui/helper.h"
34 #define _(msgid) gettext (msgid)
35 #define N_(msgid) msgid
37 static GObject *psppire_val_labs_dialog_constructor (GType type, guint,
38 GObjectConstructParam *);
39 static void psppire_val_labs_dialog_finalize (GObject *);
41 G_DEFINE_TYPE (PsppireValLabsDialog,
42 psppire_val_labs_dialog,
51 static void do_change (PsppireValLabsDialog *);
54 psppire_val_labs_dialog_set_property (GObject *object,
59 PsppireValLabsDialog *obj = PSPPIRE_VAL_LABS_DIALOG (object);
64 psppire_val_labs_dialog_set_variable (obj, g_value_get_pointer (value));
66 case PROP_VALUE_LABELS:
68 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
74 psppire_val_labs_dialog_get_property (GObject *object,
79 PsppireValLabsDialog *obj = PSPPIRE_VAL_LABS_DIALOG (object);
83 case PROP_VALUE_LABELS:
84 g_value_set_pointer (value, obj->labs);
88 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
94 psppire_val_labs_dialog_class_init (PsppireValLabsDialogClass *class)
96 GObjectClass *gobject_class;
97 gobject_class = G_OBJECT_CLASS (class);
99 gobject_class->constructor = psppire_val_labs_dialog_constructor;
100 gobject_class->finalize = psppire_val_labs_dialog_finalize;
101 gobject_class->set_property = psppire_val_labs_dialog_set_property;
102 gobject_class->get_property = psppire_val_labs_dialog_get_property;
104 g_object_class_install_property (
105 gobject_class, PROP_VARIABLE,
106 g_param_spec_pointer ("variable",
108 "Variable whose value labels are to be edited. The "
109 "variable's print format and encoding are also used "
113 g_object_class_install_property (
114 gobject_class, PROP_VALUE_LABELS,
115 g_param_spec_pointer ("value-labels",
117 "Edited value labels.",
122 psppire_val_labs_dialog_init (PsppireValLabsDialog *obj)
124 /* We do all of our work on widgets in the constructor function, because that
125 runs after the construction properties have been set. Otherwise
126 PsppireDialog's "orientation" property hasn't been set and therefore we
127 have no box to populate. */
128 obj->labs = val_labs_create (0);
132 psppire_val_labs_dialog_finalize (GObject *obj)
134 PsppireValLabsDialog *dialog = PSPPIRE_VAL_LABS_DIALOG (obj);
136 val_labs_destroy (dialog->labs);
137 g_free (dialog->encoding);
139 G_OBJECT_CLASS (psppire_val_labs_dialog_parent_class)->finalize (obj);
142 PsppireValLabsDialog *
143 psppire_val_labs_dialog_new (const struct variable *var)
145 return PSPPIRE_VAL_LABS_DIALOG (
146 g_object_new (PSPPIRE_TYPE_VAL_LABS_DIALOG,
152 psppire_val_labs_dialog_run (GtkWindow *parent_window,
153 const struct variable *var)
155 PsppireValLabsDialog *dialog;
156 struct val_labs *labs;
158 dialog = psppire_val_labs_dialog_new (var);
159 gtk_window_set_transient_for (GTK_WINDOW (dialog), parent_window);
160 gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
161 gtk_widget_show (GTK_WIDGET (dialog));
163 labs = (psppire_dialog_run (PSPPIRE_DIALOG (dialog)) == GTK_RESPONSE_OK
164 ? val_labs_clone (psppire_val_labs_dialog_get_value_labels (dialog))
167 gtk_widget_destroy (GTK_WIDGET (dialog));
172 /* This callback occurs when the text in the label entry box
175 on_label_entry_change (GtkEntry *entry, gpointer data)
179 PsppireValLabsDialog *dialog = data;
180 g_assert (dialog->labs);
182 text = gtk_entry_get_text (GTK_ENTRY (dialog->value_entry));
184 text_to_value__ (text, &dialog->format, dialog->encoding, &v);
186 if (val_labs_find (dialog->labs, &v))
188 gtk_widget_set_sensitive (dialog->change_button, TRUE);
189 gtk_widget_set_sensitive (dialog->add_button, FALSE);
193 gtk_widget_set_sensitive (dialog->change_button, FALSE);
194 gtk_widget_set_sensitive (dialog->add_button, TRUE);
197 value_destroy (&v, val_labs_get_width (dialog->labs));
200 /* This callback occurs when Enter is pressed in the label entry box. */
202 on_label_entry_activate (GtkEntry *entry, gpointer data)
204 PsppireValLabsDialog *dialog = data;
208 /* Return the value-label pair currently selected in the dialog box */
210 /* Set the TREEVIEW list cursor to the item which has the value VAL */
212 select_treeview_from_value (GtkTreeView *treeview, union value *val)
217 We do this with a linear search through the model --- hardly
218 efficient, but the list is short ... */
221 GtkTreeModel * model = gtk_tree_view_get_model (treeview);
224 for (success = gtk_tree_model_get_iter_first (model, &iter);
226 success = gtk_tree_model_iter_next (model, &iter))
231 gtk_tree_model_get_value (model, &iter, 1, &gvalue);
233 v.f = g_value_get_double (&gvalue);
235 if ( 0 == memcmp (&v, val, sizeof (union value)))
241 path = gtk_tree_model_get_path (model, &iter);
244 gtk_tree_view_set_cursor (treeview, path, 0, 0);
245 gtk_tree_path_free (path);
251 /* This callback occurs when the text in the value entry box is
254 on_value_entry_change (GtkEntry *entry, gpointer data)
258 PsppireValLabsDialog *dialog = data;
260 const gchar *text = gtk_entry_get_text (GTK_ENTRY (dialog->value_entry));
263 text_to_value__ (text, &dialog->format, dialog->encoding, &v);
265 g_signal_handler_block (GTK_ENTRY (dialog->label_entry),
266 dialog->change_handler_id);
268 gtk_entry_set_text (GTK_ENTRY (dialog->label_entry),"");
271 if ( (s = val_labs_find (dialog->labs, &v)) )
273 gtk_entry_set_text (GTK_ENTRY (dialog->label_entry), s);
274 gtk_widget_set_sensitive (dialog->add_button, FALSE);
275 gtk_widget_set_sensitive (dialog->remove_button, TRUE);
276 select_treeview_from_value (GTK_TREE_VIEW (dialog->treeview), &v);
280 gtk_widget_set_sensitive (dialog->remove_button, FALSE);
281 gtk_widget_set_sensitive (dialog->add_button, TRUE);
284 g_signal_handler_unblock (GTK_ENTRY (dialog->label_entry),
285 dialog->change_handler_id);
287 value_destroy (&v, val_labs_get_width (dialog->labs));
290 /* This callback occurs when Enter is pressed in the value entry box. */
292 on_value_entry_activate (GtkEntry *entry, gpointer data)
294 PsppireValLabsDialog *dialog = data;
296 gtk_widget_grab_focus (dialog->label_entry);
300 get_selected_tuple (PsppireValLabsDialog *dialog,
301 union value *valuep, const char **label)
303 GtkTreeView *treeview = GTK_TREE_VIEW (dialog->treeview);
306 GValue the_value = {0};
309 GtkTreeSelection* sel = gtk_tree_view_get_selection (treeview);
311 GtkTreeModel * model = gtk_tree_view_get_model (treeview);
313 if (! gtk_tree_selection_get_selected (sel, &model, &iter))
316 gtk_tree_model_get_value (model, &iter, 1, &the_value);
318 value.f = g_value_get_double (&the_value);
319 g_value_unset (&the_value);
325 struct val_lab *vl = val_labs_lookup (dialog->labs, &value);
327 *label = val_lab_get_escaped_label (vl);
334 static void repopulate_dialog (PsppireValLabsDialog *dialog);
336 /* Callback which occurs when the "Change" button is clicked */
338 on_change (GtkWidget *w, gpointer data)
340 PsppireValLabsDialog *dialog = data;
345 do_change (PsppireValLabsDialog *dialog)
347 const gchar *val_text = gtk_entry_get_text (GTK_ENTRY (dialog->value_entry));
351 text_to_value__ (val_text, &dialog->format, dialog->encoding, &v);
353 val_labs_replace (dialog->labs, &v,
354 gtk_entry_get_text (GTK_ENTRY (dialog->label_entry)));
356 gtk_widget_set_sensitive (dialog->change_button, FALSE);
358 repopulate_dialog (dialog);
359 gtk_widget_grab_focus (dialog->value_entry);
361 value_destroy (&v, val_labs_get_width (dialog->labs));
364 /* Callback which occurs when the "Add" button is clicked */
366 on_add (GtkWidget *w, gpointer data)
368 PsppireValLabsDialog *dialog = data;
372 const gchar *text = gtk_entry_get_text (GTK_ENTRY (dialog->value_entry));
374 text_to_value__ (text, &dialog->format, dialog->encoding, &v);
376 if (val_labs_add (dialog->labs, &v,
378 ( GTK_ENTRY (dialog->label_entry)) ) )
380 gtk_widget_set_sensitive (dialog->add_button, FALSE);
382 repopulate_dialog (dialog);
383 gtk_widget_grab_focus (dialog->value_entry);
386 value_destroy (&v, val_labs_get_width (dialog->labs));
389 /* Callback which occurs when the "Remove" button is clicked */
391 on_remove (GtkWidget *w, gpointer data)
393 PsppireValLabsDialog *dialog = data;
398 if (! get_selected_tuple (dialog, &value, NULL))
401 vl = val_labs_lookup (dialog->labs, &value);
403 val_labs_remove (dialog->labs, vl);
405 repopulate_dialog (dialog);
406 gtk_widget_grab_focus (dialog->value_entry);
408 gtk_widget_set_sensitive (dialog->remove_button, FALSE);
413 /* Callback which occurs when a line item is selected in the list of
414 value--label pairs.*/
416 on_select_row (GtkTreeView *treeview, gpointer data)
418 PsppireValLabsDialog *dialog = data;
421 const char *label = NULL;
425 if (! get_selected_tuple (dialog, &value, &label))
428 text = value_to_text__ (value, &dialog->format, dialog->encoding);
430 g_signal_handler_block (GTK_ENTRY (dialog->value_entry),
431 dialog->value_handler_id);
433 gtk_entry_set_text (GTK_ENTRY (dialog->value_entry), text);
435 g_signal_handler_unblock (GTK_ENTRY (dialog->value_entry),
436 dialog->value_handler_id);
439 g_signal_handler_block (GTK_ENTRY (dialog->label_entry),
440 dialog->change_handler_id);
443 gtk_entry_set_text (GTK_ENTRY (dialog->label_entry),
446 g_signal_handler_unblock (GTK_ENTRY (dialog->label_entry),
447 dialog->change_handler_id);
449 gtk_widget_set_sensitive (dialog->remove_button, TRUE);
450 gtk_widget_set_sensitive (dialog->change_button, FALSE);
454 /* Create a new dialog box
455 (there should normally be only one)*/
457 psppire_val_labs_dialog_constructor (GType type,
459 GObjectConstructParam *properties)
461 PsppireValLabsDialog *dialog;
462 GtkTreeViewColumn *column;
464 GtkCellRenderer *renderer ;
466 GtkBuilder *xml = builder_new ("val-labs-dialog.ui");
468 GtkContainer *content_area;
471 obj = G_OBJECT_CLASS (psppire_val_labs_dialog_parent_class)->constructor (
472 type, n_properties, properties);
473 dialog = PSPPIRE_VAL_LABS_DIALOG (obj);
475 content_area = GTK_CONTAINER (PSPPIRE_DIALOG (dialog));
476 gtk_container_add (GTK_CONTAINER (content_area),
477 get_widget_assert (xml, "val-labs-dialog"));
479 dialog->value_entry = get_widget_assert (xml,"value_entry");
480 dialog->label_entry = get_widget_assert (xml,"label_entry");
482 dialog->add_button = get_widget_assert (xml, "val_labs_add");
483 dialog->remove_button = get_widget_assert (xml, "val_labs_remove");
484 dialog->change_button = get_widget_assert (xml, "val_labs_change");
486 dialog->treeview = get_widget_assert (xml,"treeview1");
488 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (dialog->treeview), FALSE);
490 renderer = gtk_cell_renderer_text_new ();
492 column = gtk_tree_view_column_new_with_attributes ("Title",
498 gtk_tree_view_append_column (GTK_TREE_VIEW (dialog->treeview), column);
500 dialog->change_handler_id =
501 g_signal_connect (dialog->label_entry,
503 G_CALLBACK (on_label_entry_change), dialog);
504 g_signal_connect (dialog->label_entry, "activate",
505 G_CALLBACK (on_label_entry_activate), dialog);
507 dialog->value_handler_id =
508 g_signal_connect (dialog->value_entry,
510 G_CALLBACK (on_value_entry_change), dialog);
511 g_signal_connect (dialog->value_entry, "activate",
512 G_CALLBACK (on_value_entry_activate), dialog);
514 g_signal_connect (dialog->change_button,
516 G_CALLBACK (on_change), dialog);
519 g_signal_connect (dialog->treeview, "cursor-changed",
520 G_CALLBACK (on_select_row), dialog);
522 g_signal_connect (dialog->remove_button, "clicked",
523 G_CALLBACK (on_remove), dialog);
525 g_signal_connect (dialog->add_button, "clicked",
526 G_CALLBACK (on_add), dialog);
530 g_object_unref (xml);
536 /* Populate the components of the dialog box, from the 'labs' member
539 repopulate_dialog (PsppireValLabsDialog *dialog)
541 const struct val_lab **labels;
547 GtkListStore *list_store = gtk_list_store_new (2,
551 g_signal_handler_block (GTK_ENTRY (dialog->label_entry),
552 dialog->change_handler_id);
553 g_signal_handler_block (GTK_ENTRY (dialog->value_entry),
554 dialog->value_handler_id);
556 gtk_entry_set_text (GTK_ENTRY (dialog->value_entry), "");
557 gtk_entry_set_text (GTK_ENTRY (dialog->label_entry), "");
559 g_signal_handler_unblock (GTK_ENTRY (dialog->value_entry),
560 dialog->value_handler_id);
561 g_signal_handler_unblock (GTK_ENTRY (dialog->label_entry),
562 dialog->change_handler_id);
564 labels = val_labs_sorted (dialog->labs);
565 n_labels = val_labs_count (dialog->labs);
566 for (i = 0; i < n_labels; i++)
568 const struct val_lab *vl = labels[i];
571 value_to_text__ (vl->value, &dialog->format, dialog->encoding);
573 gchar *const text = g_strdup_printf (_("%s = `%s'"), vstr,
574 val_lab_get_escaped_label (vl));
576 gtk_list_store_append (list_store, &iter);
577 gtk_list_store_set (list_store, &iter,
587 gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->treeview),
588 GTK_TREE_MODEL (list_store));
590 g_object_unref (list_store);
595 psppire_val_labs_dialog_set_variable (PsppireValLabsDialog *dialog,
596 const struct variable *var)
598 val_labs_destroy (dialog->labs);
601 g_free (dialog->encoding);
602 dialog->encoding = NULL;
606 dialog->labs = val_labs_clone (var_get_value_labels (var));
607 dialog->encoding = g_strdup (var_get_encoding (var));
608 dialog->format = *var_get_print_format (var);
611 dialog->format = F_8_0;
613 if (dialog->labs == NULL)
614 dialog->labs = val_labs_create (var_get_width (var));
616 repopulate_dialog (dialog);
619 const struct val_labs *
620 psppire_val_labs_dialog_get_value_labels (const PsppireValLabsDialog *dialog)