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 if (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));
365 /* Callback which occurs when the "Add" button is clicked */
367 on_add (GtkWidget *w, gpointer data)
369 PsppireValLabsDialog *dialog = data;
373 const gchar *text = gtk_entry_get_text (GTK_ENTRY (dialog->value_entry));
375 if (text_to_value__ (text, &dialog->format, dialog->encoding, &v))
377 if (val_labs_add (dialog->labs, &v,
379 ( GTK_ENTRY (dialog->label_entry)) ) )
381 gtk_widget_set_sensitive (dialog->add_button, FALSE);
383 repopulate_dialog (dialog);
384 gtk_widget_grab_focus (dialog->value_entry);
387 value_destroy (&v, val_labs_get_width (dialog->labs));
391 /* Callback which occurs when the "Remove" button is clicked */
393 on_remove (GtkWidget *w, gpointer data)
395 PsppireValLabsDialog *dialog = data;
400 if (! get_selected_tuple (dialog, &value, NULL))
403 vl = val_labs_lookup (dialog->labs, &value);
405 val_labs_remove (dialog->labs, vl);
407 repopulate_dialog (dialog);
408 gtk_widget_grab_focus (dialog->value_entry);
410 gtk_widget_set_sensitive (dialog->remove_button, FALSE);
415 /* Callback which occurs when a line item is selected in the list of
416 value--label pairs.*/
418 on_select_row (GtkTreeView *treeview, gpointer data)
420 PsppireValLabsDialog *dialog = data;
423 const char *label = NULL;
427 if (! get_selected_tuple (dialog, &value, &label))
430 text = value_to_text__ (value, &dialog->format, dialog->encoding);
432 g_signal_handler_block (GTK_ENTRY (dialog->value_entry),
433 dialog->value_handler_id);
435 gtk_entry_set_text (GTK_ENTRY (dialog->value_entry), text);
437 g_signal_handler_unblock (GTK_ENTRY (dialog->value_entry),
438 dialog->value_handler_id);
441 g_signal_handler_block (GTK_ENTRY (dialog->label_entry),
442 dialog->change_handler_id);
445 gtk_entry_set_text (GTK_ENTRY (dialog->label_entry),
448 g_signal_handler_unblock (GTK_ENTRY (dialog->label_entry),
449 dialog->change_handler_id);
451 gtk_widget_set_sensitive (dialog->remove_button, TRUE);
452 gtk_widget_set_sensitive (dialog->change_button, FALSE);
456 /* Create a new dialog box
457 (there should normally be only one)*/
459 psppire_val_labs_dialog_constructor (GType type,
461 GObjectConstructParam *properties)
463 PsppireValLabsDialog *dialog;
464 GtkTreeViewColumn *column;
466 GtkCellRenderer *renderer ;
468 GtkBuilder *xml = builder_new ("val-labs-dialog.ui");
470 GtkContainer *content_area;
473 obj = G_OBJECT_CLASS (psppire_val_labs_dialog_parent_class)->constructor (
474 type, n_properties, properties);
475 dialog = PSPPIRE_VAL_LABS_DIALOG (obj);
477 content_area = GTK_CONTAINER (PSPPIRE_DIALOG (dialog));
478 gtk_container_add (GTK_CONTAINER (content_area),
479 get_widget_assert (xml, "val-labs-dialog"));
481 dialog->value_entry = get_widget_assert (xml,"value_entry");
482 dialog->label_entry = get_widget_assert (xml,"label_entry");
484 dialog->add_button = get_widget_assert (xml, "val_labs_add");
485 dialog->remove_button = get_widget_assert (xml, "val_labs_remove");
486 dialog->change_button = get_widget_assert (xml, "val_labs_change");
488 dialog->treeview = get_widget_assert (xml,"treeview1");
490 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (dialog->treeview), FALSE);
492 renderer = gtk_cell_renderer_text_new ();
494 column = gtk_tree_view_column_new_with_attributes ("Title",
500 gtk_tree_view_append_column (GTK_TREE_VIEW (dialog->treeview), column);
502 dialog->change_handler_id =
503 g_signal_connect (dialog->label_entry,
505 G_CALLBACK (on_label_entry_change), dialog);
506 g_signal_connect (dialog->label_entry, "activate",
507 G_CALLBACK (on_label_entry_activate), dialog);
509 dialog->value_handler_id =
510 g_signal_connect (dialog->value_entry,
512 G_CALLBACK (on_value_entry_change), dialog);
513 g_signal_connect (dialog->value_entry, "activate",
514 G_CALLBACK (on_value_entry_activate), dialog);
516 g_signal_connect (dialog->change_button,
518 G_CALLBACK (on_change), dialog);
521 g_signal_connect (dialog->treeview, "cursor-changed",
522 G_CALLBACK (on_select_row), dialog);
524 g_signal_connect (dialog->remove_button, "clicked",
525 G_CALLBACK (on_remove), dialog);
527 g_signal_connect (dialog->add_button, "clicked",
528 G_CALLBACK (on_add), dialog);
532 g_object_unref (xml);
538 /* Populate the components of the dialog box, from the 'labs' member
541 repopulate_dialog (PsppireValLabsDialog *dialog)
543 const struct val_lab **labels;
549 GtkListStore *list_store = gtk_list_store_new (2,
553 g_signal_handler_block (GTK_ENTRY (dialog->label_entry),
554 dialog->change_handler_id);
555 g_signal_handler_block (GTK_ENTRY (dialog->value_entry),
556 dialog->value_handler_id);
558 gtk_entry_set_text (GTK_ENTRY (dialog->value_entry), "");
559 gtk_entry_set_text (GTK_ENTRY (dialog->label_entry), "");
561 g_signal_handler_unblock (GTK_ENTRY (dialog->value_entry),
562 dialog->value_handler_id);
563 g_signal_handler_unblock (GTK_ENTRY (dialog->label_entry),
564 dialog->change_handler_id);
566 labels = val_labs_sorted (dialog->labs);
567 n_labels = val_labs_count (dialog->labs);
568 for (i = 0; i < n_labels; i++)
570 const struct val_lab *vl = labels[i];
573 value_to_text__ (vl->value, &dialog->format, dialog->encoding);
575 gchar *const text = g_strdup_printf (_("%s = `%s'"), vstr,
576 val_lab_get_escaped_label (vl));
578 gtk_list_store_append (list_store, &iter);
579 gtk_list_store_set (list_store, &iter,
589 gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->treeview),
590 GTK_TREE_MODEL (list_store));
592 g_object_unref (list_store);
597 psppire_val_labs_dialog_set_variable (PsppireValLabsDialog *dialog,
598 const struct variable *var)
600 val_labs_destroy (dialog->labs);
603 g_free (dialog->encoding);
604 dialog->encoding = NULL;
608 dialog->labs = val_labs_clone (var_get_value_labels (var));
609 dialog->encoding = g_strdup (var_get_encoding (var));
610 dialog->format = *var_get_print_format (var);
613 dialog->format = F_8_0;
615 if (dialog->labs == NULL)
616 dialog->labs = val_labs_create (var_get_width (var));
618 repopulate_dialog (dialog);
621 const struct val_labs *
622 psppire_val_labs_dialog_get_value_labels (const PsppireValLabsDialog *dialog)