1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2005, 2009, 2010, 2011, 2012 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,
52 psppire_val_labs_dialog_set_property (GObject *object,
57 PsppireValLabsDialog *obj = PSPPIRE_VAL_LABS_DIALOG (object);
62 psppire_val_labs_dialog_set_variable (obj, g_value_get_pointer (value));
64 case PROP_VALUE_LABELS:
66 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
72 psppire_val_labs_dialog_get_property (GObject *object,
77 PsppireValLabsDialog *obj = PSPPIRE_VAL_LABS_DIALOG (object);
81 case PROP_VALUE_LABELS:
82 g_value_set_pointer (value, obj->labs);
86 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
92 psppire_val_labs_dialog_class_init (PsppireValLabsDialogClass *class)
94 GObjectClass *gobject_class;
95 gobject_class = G_OBJECT_CLASS (class);
97 gobject_class->constructor = psppire_val_labs_dialog_constructor;
98 gobject_class->finalize = psppire_val_labs_dialog_finalize;
99 gobject_class->set_property = psppire_val_labs_dialog_set_property;
100 gobject_class->get_property = psppire_val_labs_dialog_get_property;
102 g_object_class_install_property (
103 gobject_class, PROP_VARIABLE,
104 g_param_spec_pointer ("variable",
106 "Variable whose value labels are to be edited. The "
107 "variable's print format and encoding are also used "
111 g_object_class_install_property (
112 gobject_class, PROP_VALUE_LABELS,
113 g_param_spec_pointer ("value-labels",
115 "Edited value labels.",
120 psppire_val_labs_dialog_init (PsppireValLabsDialog *obj)
122 /* We do all of our work on widgets in the constructor function, because that
123 runs after the construction properties have been set. Otherwise
124 PsppireDialog's "orientation" property hasn't been set and therefore we
125 have no box to populate. */
126 obj->labs = val_labs_create (0);
130 psppire_val_labs_dialog_finalize (GObject *obj)
132 PsppireValLabsDialog *dialog = PSPPIRE_VAL_LABS_DIALOG (obj);
134 val_labs_destroy (dialog->labs);
135 g_free (dialog->encoding);
137 G_OBJECT_CLASS (psppire_val_labs_dialog_parent_class)->finalize (obj);
140 PsppireValLabsDialog *
141 psppire_val_labs_dialog_new (const struct variable *var)
143 return PSPPIRE_VAL_LABS_DIALOG (
144 g_object_new (PSPPIRE_TYPE_VAL_LABS_DIALOG,
145 "orientation", PSPPIRE_HORIZONTAL,
151 psppire_val_labs_dialog_run (GtkWindow *parent_window,
152 const struct variable *var)
154 PsppireValLabsDialog *dialog;
155 struct val_labs *labs;
157 dialog = psppire_val_labs_dialog_new (var);
158 gtk_window_set_transient_for (GTK_WINDOW (dialog), parent_window);
159 gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
160 gtk_widget_show (GTK_WIDGET (dialog));
162 labs = (psppire_dialog_run (PSPPIRE_DIALOG (dialog)) == GTK_RESPONSE_OK
163 ? val_labs_clone (psppire_val_labs_dialog_get_value_labels (dialog))
166 gtk_widget_destroy (GTK_WIDGET (dialog));
171 /* This callback occurs when the text in the label entry box
174 on_label_entry_change (GtkEntry *entry, gpointer data)
178 PsppireValLabsDialog *dialog = data;
179 g_assert (dialog->labs);
181 text = gtk_entry_get_text (GTK_ENTRY (dialog->value_entry));
183 text_to_value__ (text, &dialog->format, dialog->encoding, &v);
185 if (val_labs_find (dialog->labs, &v))
187 gtk_widget_set_sensitive (dialog->change_button, TRUE);
188 gtk_widget_set_sensitive (dialog->add_button, FALSE);
192 gtk_widget_set_sensitive (dialog->change_button, FALSE);
193 gtk_widget_set_sensitive (dialog->add_button, TRUE);
196 value_destroy (&v, val_labs_get_width (dialog->labs));
200 /* Set the TREEVIEW list cursor to the item which has the value VAL */
202 select_treeview_from_value (GtkTreeView *treeview, union value *val)
207 We do this with a linear search through the model --- hardly
208 efficient, but the list is short ... */
211 GtkTreeModel * model = gtk_tree_view_get_model (treeview);
214 for (success = gtk_tree_model_get_iter_first (model, &iter);
216 success = gtk_tree_model_iter_next (model, &iter))
221 gtk_tree_model_get_value (model, &iter, 1, &gvalue);
223 v.f = g_value_get_double (&gvalue);
225 if ( 0 == memcmp (&v, val, sizeof (union value)))
231 path = gtk_tree_model_get_path (model, &iter);
234 gtk_tree_view_set_cursor (treeview, path, 0, 0);
235 gtk_tree_path_free (path);
241 /* This callback occurs when the text in the value entry box is
244 on_value_entry_change (GtkEntry *entry, gpointer data)
248 PsppireValLabsDialog *dialog = data;
250 const gchar *text = gtk_entry_get_text (GTK_ENTRY (dialog->value_entry));
253 text_to_value__ (text, &dialog->format, dialog->encoding, &v);
255 g_signal_handler_block (GTK_ENTRY (dialog->label_entry),
256 dialog->change_handler_id);
258 gtk_entry_set_text (GTK_ENTRY (dialog->label_entry),"");
261 if ( (s = val_labs_find (dialog->labs, &v)) )
263 gtk_entry_set_text (GTK_ENTRY (dialog->label_entry), s);
264 gtk_widget_set_sensitive (dialog->add_button, FALSE);
265 gtk_widget_set_sensitive (dialog->remove_button, TRUE);
266 select_treeview_from_value (GTK_TREE_VIEW (dialog->treeview), &v);
270 gtk_widget_set_sensitive (dialog->remove_button, FALSE);
271 gtk_widget_set_sensitive (dialog->add_button, TRUE);
274 g_signal_handler_unblock (GTK_ENTRY (dialog->label_entry),
275 dialog->change_handler_id);
277 value_destroy (&v, val_labs_get_width (dialog->labs));
281 /* Return the value-label pair currently selected in the dialog box */
283 get_selected_tuple (PsppireValLabsDialog *dialog,
284 union value *valuep, const char **label)
286 GtkTreeView *treeview = GTK_TREE_VIEW (dialog->treeview);
289 GValue the_value = {0};
292 GtkTreeSelection* sel = gtk_tree_view_get_selection (treeview);
294 GtkTreeModel * model = gtk_tree_view_get_model (treeview);
296 if (! gtk_tree_selection_get_selected (sel, &model, &iter))
299 gtk_tree_model_get_value (model, &iter, 1, &the_value);
301 value.f = g_value_get_double (&the_value);
302 g_value_unset (&the_value);
308 struct val_lab *vl = val_labs_lookup (dialog->labs, &value);
310 *label = val_lab_get_escaped_label (vl);
317 static void repopulate_dialog (PsppireValLabsDialog *dialog);
319 /* Callback which occurs when the "Change" button is clicked */
321 on_change (GtkWidget *w, gpointer data)
323 PsppireValLabsDialog *dialog = data;
325 const gchar *val_text = gtk_entry_get_text (GTK_ENTRY (dialog->value_entry));
329 text_to_value__ (val_text, &dialog->format, dialog->encoding, &v);
331 val_labs_replace (dialog->labs, &v,
332 gtk_entry_get_text (GTK_ENTRY (dialog->label_entry)));
334 gtk_widget_set_sensitive (dialog->change_button, FALSE);
336 repopulate_dialog (dialog);
337 gtk_widget_grab_focus (dialog->value_entry);
339 value_destroy (&v, val_labs_get_width (dialog->labs));
342 /* Callback which occurs when the "Add" button is clicked */
344 on_add (GtkWidget *w, gpointer data)
346 PsppireValLabsDialog *dialog = data;
350 const gchar *text = gtk_entry_get_text (GTK_ENTRY (dialog->value_entry));
352 text_to_value__ (text, &dialog->format, dialog->encoding, &v);
354 if (val_labs_add (dialog->labs, &v,
356 ( GTK_ENTRY (dialog->label_entry)) ) )
358 gtk_widget_set_sensitive (dialog->add_button, FALSE);
360 repopulate_dialog (dialog);
361 gtk_widget_grab_focus (dialog->value_entry);
364 value_destroy (&v, val_labs_get_width (dialog->labs));
367 /* Callback which occurs when the "Remove" button is clicked */
369 on_remove (GtkWidget *w, gpointer data)
371 PsppireValLabsDialog *dialog = data;
376 if (! get_selected_tuple (dialog, &value, NULL))
379 vl = val_labs_lookup (dialog->labs, &value);
381 val_labs_remove (dialog->labs, vl);
383 repopulate_dialog (dialog);
384 gtk_widget_grab_focus (dialog->value_entry);
386 gtk_widget_set_sensitive (dialog->remove_button, FALSE);
391 /* Callback which occurs when a line item is selected in the list of
392 value--label pairs.*/
394 on_select_row (GtkTreeView *treeview, gpointer data)
396 PsppireValLabsDialog *dialog = data;
399 const char *label = NULL;
403 if (! get_selected_tuple (dialog, &value, &label))
406 text = value_to_text__ (value, &dialog->format, dialog->encoding);
408 g_signal_handler_block (GTK_ENTRY (dialog->value_entry),
409 dialog->value_handler_id);
411 gtk_entry_set_text (GTK_ENTRY (dialog->value_entry), text);
413 g_signal_handler_unblock (GTK_ENTRY (dialog->value_entry),
414 dialog->value_handler_id);
417 g_signal_handler_block (GTK_ENTRY (dialog->label_entry),
418 dialog->change_handler_id);
421 gtk_entry_set_text (GTK_ENTRY (dialog->label_entry),
424 g_signal_handler_unblock (GTK_ENTRY (dialog->label_entry),
425 dialog->change_handler_id);
427 gtk_widget_set_sensitive (dialog->remove_button, TRUE);
428 gtk_widget_set_sensitive (dialog->change_button, FALSE);
432 /* Create a new dialog box
433 (there should normally be only one)*/
435 psppire_val_labs_dialog_constructor (GType type,
437 GObjectConstructParam *properties)
439 PsppireValLabsDialog *dialog;
440 GtkTreeViewColumn *column;
442 GtkCellRenderer *renderer ;
444 GtkBuilder *xml = builder_new ("val-labs-dialog.ui");
446 GtkContainer *content_area;
449 obj = G_OBJECT_CLASS (psppire_val_labs_dialog_parent_class)->constructor (
450 type, n_properties, properties);
451 dialog = PSPPIRE_VAL_LABS_DIALOG (obj);
453 content_area = GTK_CONTAINER (PSPPIRE_DIALOG (dialog)->box);
454 gtk_container_add (GTK_CONTAINER (content_area),
455 get_widget_assert (xml, "val-labs-dialog"));
457 dialog->value_entry = get_widget_assert (xml,"value_entry");
458 dialog->label_entry = get_widget_assert (xml,"label_entry");
460 dialog->add_button = get_widget_assert (xml, "val_labs_add");
461 dialog->remove_button = get_widget_assert (xml, "val_labs_remove");
462 dialog->change_button = get_widget_assert (xml, "val_labs_change");
464 dialog->treeview = get_widget_assert (xml,"treeview1");
466 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (dialog->treeview), FALSE);
468 renderer = gtk_cell_renderer_text_new ();
470 column = gtk_tree_view_column_new_with_attributes ("Title",
476 gtk_tree_view_append_column (GTK_TREE_VIEW (dialog->treeview), column);
478 dialog->change_handler_id =
479 g_signal_connect (dialog->label_entry,
481 G_CALLBACK (on_label_entry_change), dialog);
483 dialog->value_handler_id =
484 g_signal_connect (dialog->value_entry,
486 G_CALLBACK (on_value_entry_change), dialog);
488 g_signal_connect (dialog->change_button,
490 G_CALLBACK (on_change), dialog);
493 g_signal_connect (dialog->treeview, "cursor-changed",
494 G_CALLBACK (on_select_row), dialog);
496 g_signal_connect (dialog->remove_button, "clicked",
497 G_CALLBACK (on_remove), dialog);
499 g_signal_connect (dialog->add_button, "clicked",
500 G_CALLBACK (on_add), dialog);
504 g_object_unref (xml);
510 /* Populate the components of the dialog box, from the 'labs' member
513 repopulate_dialog (PsppireValLabsDialog *dialog)
515 const struct val_lab **labels;
521 GtkListStore *list_store = gtk_list_store_new (2,
525 g_signal_handler_block (GTK_ENTRY (dialog->label_entry),
526 dialog->change_handler_id);
527 g_signal_handler_block (GTK_ENTRY (dialog->value_entry),
528 dialog->value_handler_id);
530 gtk_entry_set_text (GTK_ENTRY (dialog->value_entry), "");
531 gtk_entry_set_text (GTK_ENTRY (dialog->label_entry), "");
533 g_signal_handler_unblock (GTK_ENTRY (dialog->value_entry),
534 dialog->value_handler_id);
535 g_signal_handler_unblock (GTK_ENTRY (dialog->label_entry),
536 dialog->change_handler_id);
538 labels = val_labs_sorted (dialog->labs);
539 n_labels = val_labs_count (dialog->labs);
540 for (i = 0; i < n_labels; i++)
542 const struct val_lab *vl = labels[i];
545 value_to_text__ (vl->value, &dialog->format, dialog->encoding);
547 gchar *const text = g_strdup_printf (_("%s = `%s'"), vstr,
548 val_lab_get_escaped_label (vl));
550 gtk_list_store_append (list_store, &iter);
551 gtk_list_store_set (list_store, &iter,
561 gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->treeview),
562 GTK_TREE_MODEL (list_store));
564 g_object_unref (list_store);
569 psppire_val_labs_dialog_set_variable (PsppireValLabsDialog *dialog,
570 const struct variable *var)
572 val_labs_destroy (dialog->labs);
575 g_free (dialog->encoding);
576 dialog->encoding = NULL;
580 dialog->labs = val_labs_clone (var_get_value_labels (var));
581 dialog->encoding = g_strdup (var_get_encoding (var));
582 dialog->format = *var_get_print_format (var);
585 dialog->format = F_8_0;
587 if (dialog->labs == NULL)
588 dialog->labs = val_labs_create (var_get_width (var));
590 repopulate_dialog (dialog);
593 const struct val_labs *
594 psppire_val_labs_dialog_get_value_labels (const PsppireValLabsDialog *dialog)