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 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);
314 static void repopulate_dialog (PsppireValLabsDialog *dialog);
316 /* Callback which occurs when the "Change" button is clicked */
318 on_change (GtkWidget *w, gpointer data)
320 PsppireValLabsDialog *dialog = data;
322 const gchar *val_text = gtk_entry_get_text (GTK_ENTRY (dialog->value_entry));
326 text_to_value__ (val_text, &dialog->format, dialog->encoding, &v);
328 val_labs_replace (dialog->labs, &v,
329 gtk_entry_get_text (GTK_ENTRY (dialog->label_entry)));
331 gtk_widget_set_sensitive (dialog->change_button, FALSE);
333 repopulate_dialog (dialog);
334 gtk_widget_grab_focus (dialog->value_entry);
336 value_destroy (&v, val_labs_get_width (dialog->labs));
339 /* Callback which occurs when the "Add" button is clicked */
341 on_add (GtkWidget *w, gpointer data)
343 PsppireValLabsDialog *dialog = data;
347 const gchar *text = gtk_entry_get_text (GTK_ENTRY (dialog->value_entry));
349 text_to_value__ (text, &dialog->format, dialog->encoding, &v);
351 if (val_labs_add (dialog->labs, &v,
353 ( GTK_ENTRY (dialog->label_entry)) ) )
355 gtk_widget_set_sensitive (dialog->add_button, FALSE);
357 repopulate_dialog (dialog);
358 gtk_widget_grab_focus (dialog->value_entry);
361 value_destroy (&v, val_labs_get_width (dialog->labs));
364 /* Callback which occurs when the "Remove" button is clicked */
366 on_remove (GtkWidget *w, gpointer data)
368 PsppireValLabsDialog *dialog = data;
373 get_selected_tuple (dialog, &value, NULL);
374 vl = val_labs_lookup (dialog->labs, &value);
376 val_labs_remove (dialog->labs, vl);
378 repopulate_dialog (dialog);
379 gtk_widget_grab_focus (dialog->value_entry);
381 gtk_widget_set_sensitive (dialog->remove_button, FALSE);
386 /* Callback which occurs when a line item is selected in the list of
387 value--label pairs.*/
389 on_select_row (GtkTreeView *treeview, gpointer data)
391 PsppireValLabsDialog *dialog = data;
394 const char *label = NULL;
398 get_selected_tuple (dialog, &value, &label);
399 text = value_to_text__ (value, &dialog->format, dialog->encoding);
401 g_signal_handler_block (GTK_ENTRY (dialog->value_entry),
402 dialog->value_handler_id);
404 gtk_entry_set_text (GTK_ENTRY (dialog->value_entry), text);
406 g_signal_handler_unblock (GTK_ENTRY (dialog->value_entry),
407 dialog->value_handler_id);
410 g_signal_handler_block (GTK_ENTRY (dialog->label_entry),
411 dialog->change_handler_id);
414 gtk_entry_set_text (GTK_ENTRY (dialog->label_entry),
417 g_signal_handler_unblock (GTK_ENTRY (dialog->label_entry),
418 dialog->change_handler_id);
420 gtk_widget_set_sensitive (dialog->remove_button, TRUE);
421 gtk_widget_set_sensitive (dialog->change_button, FALSE);
425 /* Create a new dialog box
426 (there should normally be only one)*/
428 psppire_val_labs_dialog_constructor (GType type,
430 GObjectConstructParam *properties)
432 PsppireValLabsDialog *dialog;
433 GtkTreeViewColumn *column;
435 GtkCellRenderer *renderer ;
437 GtkBuilder *xml = builder_new ("val-labs-dialog.ui");
439 GtkContainer *content_area;
442 obj = G_OBJECT_CLASS (psppire_val_labs_dialog_parent_class)->constructor (
443 type, n_properties, properties);
444 dialog = PSPPIRE_VAL_LABS_DIALOG (obj);
446 content_area = GTK_CONTAINER (PSPPIRE_DIALOG (dialog)->box);
447 gtk_container_add (GTK_CONTAINER (content_area),
448 get_widget_assert (xml, "val-labs-dialog"));
450 dialog->value_entry = get_widget_assert (xml,"value_entry");
451 dialog->label_entry = get_widget_assert (xml,"label_entry");
453 dialog->add_button = get_widget_assert (xml, "val_labs_add");
454 dialog->remove_button = get_widget_assert (xml, "val_labs_remove");
455 dialog->change_button = get_widget_assert (xml, "val_labs_change");
457 dialog->treeview = get_widget_assert (xml,"treeview1");
459 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (dialog->treeview), FALSE);
461 renderer = gtk_cell_renderer_text_new ();
463 column = gtk_tree_view_column_new_with_attributes ("Title",
469 gtk_tree_view_append_column (GTK_TREE_VIEW (dialog->treeview), column);
471 dialog->change_handler_id =
472 g_signal_connect (dialog->label_entry,
474 G_CALLBACK (on_label_entry_change), dialog);
476 dialog->value_handler_id =
477 g_signal_connect (dialog->value_entry,
479 G_CALLBACK (on_value_entry_change), dialog);
481 g_signal_connect (dialog->change_button,
483 G_CALLBACK (on_change), dialog);
486 g_signal_connect (dialog->treeview, "cursor-changed",
487 G_CALLBACK (on_select_row), dialog);
489 g_signal_connect (dialog->remove_button, "clicked",
490 G_CALLBACK (on_remove), dialog);
492 g_signal_connect (dialog->add_button, "clicked",
493 G_CALLBACK (on_add), dialog);
497 g_object_unref (xml);
503 /* Populate the components of the dialog box, from the 'labs' member
506 repopulate_dialog (PsppireValLabsDialog *dialog)
508 const struct val_lab **labels;
514 GtkListStore *list_store = gtk_list_store_new (2,
518 g_signal_handler_block (GTK_ENTRY (dialog->label_entry),
519 dialog->change_handler_id);
520 g_signal_handler_block (GTK_ENTRY (dialog->value_entry),
521 dialog->value_handler_id);
523 gtk_entry_set_text (GTK_ENTRY (dialog->value_entry), "");
524 gtk_entry_set_text (GTK_ENTRY (dialog->label_entry), "");
526 g_signal_handler_unblock (GTK_ENTRY (dialog->value_entry),
527 dialog->value_handler_id);
528 g_signal_handler_unblock (GTK_ENTRY (dialog->label_entry),
529 dialog->change_handler_id);
531 labels = val_labs_sorted (dialog->labs);
532 n_labels = val_labs_count (dialog->labs);
533 for (i = 0; i < n_labels; i++)
535 const struct val_lab *vl = labels[i];
538 value_to_text__ (vl->value, &dialog->format, dialog->encoding);
540 gchar *const text = g_strdup_printf (_("%s = `%s'"), vstr,
541 val_lab_get_escaped_label (vl));
543 gtk_list_store_append (list_store, &iter);
544 gtk_list_store_set (list_store, &iter,
554 gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->treeview),
555 GTK_TREE_MODEL (list_store));
557 g_object_unref (list_store);
562 psppire_val_labs_dialog_set_variable (PsppireValLabsDialog *dialog,
563 const struct variable *var)
565 val_labs_destroy (dialog->labs);
568 g_free (dialog->encoding);
569 dialog->encoding = NULL;
573 dialog->labs = val_labs_clone (var_get_value_labels (var));
574 dialog->encoding = g_strdup (var_get_encoding (var));
575 dialog->format = *var_get_print_format (var);
578 dialog->format = F_8_0;
580 if (dialog->labs == NULL)
581 dialog->labs = val_labs_create (var_get_width (var));
583 repopulate_dialog (dialog);
586 const struct val_labs *
587 psppire_val_labs_dialog_get_value_labels (const PsppireValLabsDialog *dialog)