1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2005, 2009, 2010, 2011, 2012, 2015 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,
150 psppire_val_labs_dialog_run (GtkWindow *parent_window,
151 const struct variable *var)
153 PsppireValLabsDialog *dialog;
154 struct val_labs *labs;
156 dialog = psppire_val_labs_dialog_new (var);
157 gtk_window_set_transient_for (GTK_WINDOW (dialog), parent_window);
158 gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
159 gtk_widget_show (GTK_WIDGET (dialog));
161 labs = (psppire_dialog_run (PSPPIRE_DIALOG (dialog)) == GTK_RESPONSE_OK
162 ? val_labs_clone (psppire_val_labs_dialog_get_value_labels (dialog))
165 gtk_widget_destroy (GTK_WIDGET (dialog));
170 /* This callback occurs when the text in the label entry box
173 on_label_entry_change (GtkEntry *entry, gpointer data)
177 PsppireValLabsDialog *dialog = data;
178 g_assert (dialog->labs);
180 text = gtk_entry_get_text (GTK_ENTRY (dialog->value_entry));
182 text_to_value__ (text, &dialog->format, dialog->encoding, &v);
184 if (val_labs_find (dialog->labs, &v))
186 gtk_widget_set_sensitive (dialog->change_button, TRUE);
187 gtk_widget_set_sensitive (dialog->add_button, FALSE);
191 gtk_widget_set_sensitive (dialog->change_button, FALSE);
192 gtk_widget_set_sensitive (dialog->add_button, TRUE);
195 value_destroy (&v, val_labs_get_width (dialog->labs));
199 /* Set the TREEVIEW list cursor to the item which has the value VAL */
201 select_treeview_from_value (GtkTreeView *treeview, union value *val)
206 We do this with a linear search through the model --- hardly
207 efficient, but the list is short ... */
210 GtkTreeModel * model = gtk_tree_view_get_model (treeview);
213 for (success = gtk_tree_model_get_iter_first (model, &iter);
215 success = gtk_tree_model_iter_next (model, &iter))
220 gtk_tree_model_get_value (model, &iter, 1, &gvalue);
222 v.f = g_value_get_double (&gvalue);
224 if ( 0 == memcmp (&v, val, sizeof (union value)))
230 path = gtk_tree_model_get_path (model, &iter);
233 gtk_tree_view_set_cursor (treeview, path, 0, 0);
234 gtk_tree_path_free (path);
240 /* This callback occurs when the text in the value entry box is
243 on_value_entry_change (GtkEntry *entry, gpointer data)
247 PsppireValLabsDialog *dialog = data;
249 const gchar *text = gtk_entry_get_text (GTK_ENTRY (dialog->value_entry));
252 text_to_value__ (text, &dialog->format, dialog->encoding, &v);
254 g_signal_handler_block (GTK_ENTRY (dialog->label_entry),
255 dialog->change_handler_id);
257 gtk_entry_set_text (GTK_ENTRY (dialog->label_entry),"");
260 if ( (s = val_labs_find (dialog->labs, &v)) )
262 gtk_entry_set_text (GTK_ENTRY (dialog->label_entry), s);
263 gtk_widget_set_sensitive (dialog->add_button, FALSE);
264 gtk_widget_set_sensitive (dialog->remove_button, TRUE);
265 select_treeview_from_value (GTK_TREE_VIEW (dialog->treeview), &v);
269 gtk_widget_set_sensitive (dialog->remove_button, FALSE);
270 gtk_widget_set_sensitive (dialog->add_button, TRUE);
273 g_signal_handler_unblock (GTK_ENTRY (dialog->label_entry),
274 dialog->change_handler_id);
276 value_destroy (&v, val_labs_get_width (dialog->labs));
280 /* Return the value-label pair currently selected in the dialog box */
282 get_selected_tuple (PsppireValLabsDialog *dialog,
283 union value *valuep, const char **label)
285 GtkTreeView *treeview = GTK_TREE_VIEW (dialog->treeview);
288 GValue the_value = {0};
291 GtkTreeSelection* sel = gtk_tree_view_get_selection (treeview);
293 GtkTreeModel * model = gtk_tree_view_get_model (treeview);
295 if (! gtk_tree_selection_get_selected (sel, &model, &iter))
298 gtk_tree_model_get_value (model, &iter, 1, &the_value);
300 value.f = g_value_get_double (&the_value);
301 g_value_unset (&the_value);
307 struct val_lab *vl = val_labs_lookup (dialog->labs, &value);
309 *label = val_lab_get_escaped_label (vl);
316 static void repopulate_dialog (PsppireValLabsDialog *dialog);
318 /* Callback which occurs when the "Change" button is clicked */
320 on_change (GtkWidget *w, gpointer data)
322 PsppireValLabsDialog *dialog = data;
324 const gchar *val_text = gtk_entry_get_text (GTK_ENTRY (dialog->value_entry));
328 text_to_value__ (val_text, &dialog->format, dialog->encoding, &v);
330 val_labs_replace (dialog->labs, &v,
331 gtk_entry_get_text (GTK_ENTRY (dialog->label_entry)));
333 gtk_widget_set_sensitive (dialog->change_button, FALSE);
335 repopulate_dialog (dialog);
336 gtk_widget_grab_focus (dialog->value_entry);
338 value_destroy (&v, val_labs_get_width (dialog->labs));
341 /* Callback which occurs when the "Add" button is clicked */
343 on_add (GtkWidget *w, gpointer data)
345 PsppireValLabsDialog *dialog = data;
349 const gchar *text = gtk_entry_get_text (GTK_ENTRY (dialog->value_entry));
351 text_to_value__ (text, &dialog->format, dialog->encoding, &v);
353 if (val_labs_add (dialog->labs, &v,
355 ( GTK_ENTRY (dialog->label_entry)) ) )
357 gtk_widget_set_sensitive (dialog->add_button, FALSE);
359 repopulate_dialog (dialog);
360 gtk_widget_grab_focus (dialog->value_entry);
363 value_destroy (&v, val_labs_get_width (dialog->labs));
366 /* Callback which occurs when the "Remove" button is clicked */
368 on_remove (GtkWidget *w, gpointer data)
370 PsppireValLabsDialog *dialog = data;
375 if (! get_selected_tuple (dialog, &value, NULL))
378 vl = val_labs_lookup (dialog->labs, &value);
380 val_labs_remove (dialog->labs, vl);
382 repopulate_dialog (dialog);
383 gtk_widget_grab_focus (dialog->value_entry);
385 gtk_widget_set_sensitive (dialog->remove_button, FALSE);
390 /* Callback which occurs when a line item is selected in the list of
391 value--label pairs.*/
393 on_select_row (GtkTreeView *treeview, gpointer data)
395 PsppireValLabsDialog *dialog = data;
398 const char *label = NULL;
402 if (! get_selected_tuple (dialog, &value, &label))
405 text = value_to_text__ (value, &dialog->format, dialog->encoding);
407 g_signal_handler_block (GTK_ENTRY (dialog->value_entry),
408 dialog->value_handler_id);
410 gtk_entry_set_text (GTK_ENTRY (dialog->value_entry), text);
412 g_signal_handler_unblock (GTK_ENTRY (dialog->value_entry),
413 dialog->value_handler_id);
416 g_signal_handler_block (GTK_ENTRY (dialog->label_entry),
417 dialog->change_handler_id);
420 gtk_entry_set_text (GTK_ENTRY (dialog->label_entry),
423 g_signal_handler_unblock (GTK_ENTRY (dialog->label_entry),
424 dialog->change_handler_id);
426 gtk_widget_set_sensitive (dialog->remove_button, TRUE);
427 gtk_widget_set_sensitive (dialog->change_button, FALSE);
431 /* Create a new dialog box
432 (there should normally be only one)*/
434 psppire_val_labs_dialog_constructor (GType type,
436 GObjectConstructParam *properties)
438 PsppireValLabsDialog *dialog;
439 GtkTreeViewColumn *column;
441 GtkCellRenderer *renderer ;
443 GtkBuilder *xml = builder_new ("val-labs-dialog.ui");
445 GtkContainer *content_area;
448 obj = G_OBJECT_CLASS (psppire_val_labs_dialog_parent_class)->constructor (
449 type, n_properties, properties);
450 dialog = PSPPIRE_VAL_LABS_DIALOG (obj);
452 content_area = GTK_CONTAINER (PSPPIRE_DIALOG (dialog));
453 gtk_container_add (GTK_CONTAINER (content_area),
454 get_widget_assert (xml, "val-labs-dialog"));
456 dialog->value_entry = get_widget_assert (xml,"value_entry");
457 dialog->label_entry = get_widget_assert (xml,"label_entry");
459 dialog->add_button = get_widget_assert (xml, "val_labs_add");
460 dialog->remove_button = get_widget_assert (xml, "val_labs_remove");
461 dialog->change_button = get_widget_assert (xml, "val_labs_change");
463 dialog->treeview = get_widget_assert (xml,"treeview1");
465 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (dialog->treeview), FALSE);
467 renderer = gtk_cell_renderer_text_new ();
469 column = gtk_tree_view_column_new_with_attributes ("Title",
475 gtk_tree_view_append_column (GTK_TREE_VIEW (dialog->treeview), column);
477 dialog->change_handler_id =
478 g_signal_connect (dialog->label_entry,
480 G_CALLBACK (on_label_entry_change), dialog);
482 dialog->value_handler_id =
483 g_signal_connect (dialog->value_entry,
485 G_CALLBACK (on_value_entry_change), dialog);
487 g_signal_connect (dialog->change_button,
489 G_CALLBACK (on_change), dialog);
492 g_signal_connect (dialog->treeview, "cursor-changed",
493 G_CALLBACK (on_select_row), dialog);
495 g_signal_connect (dialog->remove_button, "clicked",
496 G_CALLBACK (on_remove), dialog);
498 g_signal_connect (dialog->add_button, "clicked",
499 G_CALLBACK (on_add), dialog);
503 g_object_unref (xml);
509 /* Populate the components of the dialog box, from the 'labs' member
512 repopulate_dialog (PsppireValLabsDialog *dialog)
514 const struct val_lab **labels;
520 GtkListStore *list_store = gtk_list_store_new (2,
524 g_signal_handler_block (GTK_ENTRY (dialog->label_entry),
525 dialog->change_handler_id);
526 g_signal_handler_block (GTK_ENTRY (dialog->value_entry),
527 dialog->value_handler_id);
529 gtk_entry_set_text (GTK_ENTRY (dialog->value_entry), "");
530 gtk_entry_set_text (GTK_ENTRY (dialog->label_entry), "");
532 g_signal_handler_unblock (GTK_ENTRY (dialog->value_entry),
533 dialog->value_handler_id);
534 g_signal_handler_unblock (GTK_ENTRY (dialog->label_entry),
535 dialog->change_handler_id);
537 labels = val_labs_sorted (dialog->labs);
538 n_labels = val_labs_count (dialog->labs);
539 for (i = 0; i < n_labels; i++)
541 const struct val_lab *vl = labels[i];
544 value_to_text__ (vl->value, &dialog->format, dialog->encoding);
546 gchar *const text = g_strdup_printf (_("%s = `%s'"), vstr,
547 val_lab_get_escaped_label (vl));
549 gtk_list_store_append (list_store, &iter);
550 gtk_list_store_set (list_store, &iter,
560 gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->treeview),
561 GTK_TREE_MODEL (list_store));
563 g_object_unref (list_store);
568 psppire_val_labs_dialog_set_variable (PsppireValLabsDialog *dialog,
569 const struct variable *var)
571 val_labs_destroy (dialog->labs);
574 g_free (dialog->encoding);
575 dialog->encoding = NULL;
579 dialog->labs = val_labs_clone (var_get_value_labels (var));
580 dialog->encoding = g_strdup (var_get_encoding (var));
581 dialog->format = *var_get_print_format (var);
584 dialog->format = F_8_0;
586 if (dialog->labs == NULL)
587 dialog->labs = val_labs_create (var_get_width (var));
589 repopulate_dialog (dialog);
592 const struct val_labs *
593 psppire_val_labs_dialog_get_value_labels (const PsppireValLabsDialog *dialog)