5b226eb6b807b191362659eec72422caad7c8e1c
[pspp-builds.git] / src / ui / gui / val-labs-dialog.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2005, 2009  Free Software Foundation
3
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.
8
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.
13
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/>. */
16
17
18 /*  This module describes the behaviour of the Value Labels dialog box,
19     used for input of the value labels in the variable sheet */
20
21 #include <config.h>
22
23 #include <string.h>
24
25 #include "helper.h"
26 #include "val-labs-dialog.h"
27 #include <data/value-labels.h>
28 #include <data/format.h>
29 #include "psppire-var-sheet.h"
30 #include "psppire-var-store.h"
31 #include <libpspp/i18n.h>
32
33 struct val_labs_dialog
34 {
35   GtkWidget *window;
36
37   PsppireVarStore *var_store;
38
39   /* The variable to be updated */
40   struct variable *pv;
41
42   /* Local copy of labels */
43   struct val_labs *labs;
44
45   /* Actions */
46   GtkWidget *add_button;
47   GtkWidget *remove_button;
48   GtkWidget *change_button;
49
50   /* Entry Boxes */
51   GtkWidget *value_entry;
52   GtkWidget *label_entry;
53
54   /* Signal handler ids */
55   gint change_handler_id;
56   gint value_handler_id;
57
58   GtkWidget *treeview;
59 };
60
61
62 /* This callback occurs when the text in the label entry box
63    is changed */
64 static void
65 on_label_entry_change (GtkEntry *entry, gpointer data)
66 {
67   union value v;
68   const gchar *text ;
69   struct val_labs_dialog *dialog = data;
70   g_assert (dialog->labs);
71
72   text = gtk_entry_get_text (GTK_ENTRY (dialog->value_entry));
73
74   text_to_value (text,
75                  dialog->var_store->dict,
76                  dialog->pv,
77                  &v);
78
79   if (val_labs_find (dialog->labs, &v))
80     {
81       gtk_widget_set_sensitive (dialog->change_button, TRUE);
82       gtk_widget_set_sensitive (dialog->add_button, FALSE);
83     }
84   else
85     {
86       gtk_widget_set_sensitive (dialog->change_button, FALSE);
87       gtk_widget_set_sensitive (dialog->add_button, TRUE);
88     }
89
90   value_destroy (&v, var_get_width (dialog->pv));
91 }
92
93
94 /* Set the TREEVIEW list cursor to the item which has the value VAL */
95 static void
96 select_treeview_from_value (GtkTreeView *treeview, union value *val)
97 {
98   GtkTreePath *path ;
99
100   /*
101     We do this with a linear search through the model --- hardly
102     efficient, but the list is short ... */
103   GtkTreeIter iter;
104
105   GtkTreeModel * model  = gtk_tree_view_get_model (treeview);
106
107   gboolean success;
108   for (success = gtk_tree_model_get_iter_first (model, &iter);
109        success;
110        success = gtk_tree_model_iter_next (model, &iter))
111     {
112       union value v;
113       GValue gvalue = {0};
114
115       gtk_tree_model_get_value (model, &iter, 1, &gvalue);
116
117       v.f = g_value_get_double (&gvalue);
118
119       if ( 0 == memcmp (&v, val, sizeof (union value)))
120         {
121           break;
122         }
123     }
124
125   path = gtk_tree_model_get_path (model, &iter);
126   if ( path )
127     {
128       gtk_tree_view_set_cursor (treeview, path, 0, 0);
129       gtk_tree_path_free (path);
130     }
131
132 }
133
134
135 /* This callback occurs when the text in the value entry box is
136    changed */
137 static void
138 on_value_entry_change (GtkEntry *entry, gpointer data)
139 {
140   const char *s;
141
142   struct val_labs_dialog *dialog = data;
143
144   const gchar *text = gtk_entry_get_text (GTK_ENTRY (dialog->value_entry));
145
146   union value v;
147   text_to_value (text,
148                  dialog->var_store->dict,
149                  dialog->pv,
150                  &v);
151
152
153   g_signal_handler_block (GTK_ENTRY (dialog->label_entry),
154                          dialog->change_handler_id);
155
156   gtk_entry_set_text (GTK_ENTRY (dialog->label_entry),"");
157
158
159   if ( (s = val_labs_find (dialog->labs, &v)) )
160     {
161       gtk_entry_set_text (GTK_ENTRY (dialog->label_entry), s);
162       gtk_widget_set_sensitive (dialog->add_button, FALSE);
163       gtk_widget_set_sensitive (dialog->remove_button, TRUE);
164       select_treeview_from_value (GTK_TREE_VIEW (dialog->treeview), &v);
165     }
166   else
167     {
168       gtk_widget_set_sensitive (dialog->remove_button, FALSE);
169       gtk_widget_set_sensitive (dialog->add_button, TRUE);
170     }
171
172   g_signal_handler_unblock (GTK_ENTRY (dialog->label_entry),
173                          dialog->change_handler_id);
174
175   value_destroy (&v, var_get_width (dialog->pv));
176 }
177
178
179 /* Callback for when the Value Labels dialog is closed using
180    the OK button.*/
181 static gint
182 val_labs_ok (GtkWidget *w, gpointer data)
183 {
184   struct val_labs_dialog *dialog = data;
185
186   var_set_value_labels (dialog->pv, dialog->labs);
187
188   val_labs_destroy (dialog->labs);
189
190   dialog->labs = 0;
191
192   gtk_widget_hide (dialog->window);
193
194   return FALSE;
195 }
196
197 /* Callback for when the Value Labels dialog is closed using
198    the Cancel button.*/
199 static void
200 val_labs_cancel (struct val_labs_dialog *dialog)
201 {
202   val_labs_destroy (dialog->labs);
203
204   dialog->labs = 0;
205
206   gtk_widget_hide (dialog->window);
207 }
208
209
210 /* Callback for when the Value Labels dialog is closed using
211    the Cancel button.*/
212 static void
213 on_cancel (GtkWidget *w, gpointer data)
214 {
215   struct val_labs_dialog *dialog = data;
216
217   val_labs_cancel (dialog);
218 }
219
220
221 /* Callback for when the Value Labels dialog is closed using
222    the window delete button.*/
223 static gint
224 on_delete (GtkWidget *w, GdkEvent *e, gpointer data)
225 {
226   struct val_labs_dialog *dialog = data;
227
228   val_labs_cancel (dialog);
229
230   return TRUE;
231 }
232
233
234 /* Return the value-label pair currently selected in the dialog box  */
235 static void
236 get_selected_tuple (struct val_labs_dialog *dialog,
237                     union value *valuep, const char **label)
238 {
239   GtkTreeView *treeview = GTK_TREE_VIEW (dialog->treeview);
240
241   GtkTreeIter iter ;
242   GValue the_value = {0};
243   union value value;
244
245   GtkTreeSelection* sel =  gtk_tree_view_get_selection (treeview);
246
247   GtkTreeModel * model  = gtk_tree_view_get_model (treeview);
248
249   gtk_tree_selection_get_selected (sel, &model, &iter);
250
251   gtk_tree_model_get_value (model, &iter, 1, &the_value);
252
253   value.f = g_value_get_double (&the_value);
254   g_value_unset (&the_value);
255
256   if (valuep != NULL)
257     *valuep = value;
258   if (label != NULL)
259     *label = val_labs_find (dialog->labs, &value);
260 }
261
262
263 static void repopulate_dialog (struct val_labs_dialog *dialog);
264
265 /* Callback which occurs when the "Change" button is clicked */
266 static void
267 on_change (GtkWidget *w, gpointer data)
268 {
269   struct val_labs_dialog *dialog = data;
270
271   const gchar *val_text = gtk_entry_get_text (GTK_ENTRY (dialog->value_entry));
272
273   union value v;
274
275   text_to_value (val_text,
276                  dialog->var_store->dict,
277                  dialog->pv,
278                  &v);
279
280   val_labs_replace (dialog->labs, &v,
281                     gtk_entry_get_text (GTK_ENTRY (dialog->label_entry)));
282
283   gtk_widget_set_sensitive (dialog->change_button, FALSE);
284
285   repopulate_dialog (dialog);
286   gtk_widget_grab_focus (dialog->value_entry);
287
288   value_destroy (&v, var_get_width (dialog->pv));
289 }
290
291 /* Callback which occurs when the "Add" button is clicked */
292 static void
293 on_add (GtkWidget *w, gpointer data)
294 {
295   struct val_labs_dialog *dialog = data;
296
297   union value v;
298
299   const gchar *text = gtk_entry_get_text (GTK_ENTRY (dialog->value_entry));
300
301   text_to_value (text,
302                  dialog->var_store->dict,
303                  dialog->pv,
304                  &v);
305
306   if (val_labs_add (dialog->labs, &v,
307                     gtk_entry_get_text
308                     ( GTK_ENTRY (dialog->label_entry)) ) )
309     {
310       gtk_widget_set_sensitive (dialog->add_button, FALSE);
311
312       repopulate_dialog (dialog);
313       gtk_widget_grab_focus (dialog->value_entry);
314     }
315
316   value_destroy (&v, var_get_width (dialog->pv));
317 }
318
319 /* Callback which occurs when the "Remove" button is clicked */
320 static void
321 on_remove (GtkWidget *w, gpointer data)
322 {
323   struct val_labs_dialog *dialog = data;
324
325   union value value;
326   const struct val_lab *vl;
327
328   get_selected_tuple (dialog, &value, NULL);
329   vl = val_labs_lookup (dialog->labs, &value);
330   if (vl != NULL)
331     val_labs_remove (dialog->labs, vl);
332
333   repopulate_dialog (dialog);
334   gtk_widget_grab_focus (dialog->value_entry);
335
336   gtk_widget_set_sensitive (dialog->remove_button, FALSE);
337 }
338
339
340
341 /* Callback which occurs when a line item is selected in the list of
342    value--label pairs.*/
343 static void
344 on_select_row (GtkTreeView *treeview, gpointer data)
345 {
346   struct val_labs_dialog *dialog = data;
347
348   union value value;
349   const char *label = NULL;
350
351   gchar *text;
352
353   get_selected_tuple (dialog, &value, &label);
354   text = value_to_text (value, dialog->var_store->dict, *var_get_write_format (dialog->pv));
355
356   g_signal_handler_block (GTK_ENTRY (dialog->value_entry),
357                          dialog->value_handler_id);
358
359   gtk_entry_set_text (GTK_ENTRY (dialog->value_entry), text);
360
361   g_signal_handler_unblock (GTK_ENTRY (dialog->value_entry),
362                          dialog->value_handler_id);
363   g_free (text);
364
365   g_signal_handler_block (GTK_ENTRY (dialog->label_entry),
366                          dialog->change_handler_id);
367
368
369   gtk_entry_set_text (GTK_ENTRY (dialog->label_entry),
370                       label);
371
372   g_signal_handler_unblock (GTK_ENTRY (dialog->label_entry),
373                          dialog->change_handler_id);
374
375   gtk_widget_set_sensitive (dialog->remove_button, TRUE);
376   gtk_widget_set_sensitive (dialog->change_button, FALSE);
377 }
378
379
380 /* Create a new dialog box
381    (there should  normally be only one)*/
382 struct val_labs_dialog *
383 val_labs_dialog_create (GtkWindow *toplevel, PsppireVarStore *var_store)
384 {
385   GtkTreeViewColumn *column;
386
387   GtkCellRenderer *renderer ;
388
389   GtkBuilder *xml = builder_new ("var-sheet-dialogs.ui");
390
391   struct val_labs_dialog *dialog = g_malloc (sizeof (*dialog));
392
393   dialog->var_store = var_store;
394   dialog->window = get_widget_assert (xml,"val_labs_dialog");
395   dialog->value_entry = get_widget_assert (xml,"value_entry");
396   dialog->label_entry = get_widget_assert (xml,"label_entry");
397
398   gtk_window_set_transient_for
399     (GTK_WINDOW (dialog->window), toplevel);
400
401   dialog->add_button = get_widget_assert (xml, "val_labs_add");
402   dialog->remove_button = get_widget_assert (xml, "val_labs_remove");
403   dialog->change_button = get_widget_assert (xml, "val_labs_change");
404
405   dialog->treeview = get_widget_assert (xml,"treeview1");
406
407   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (dialog->treeview), FALSE);
408
409   renderer = gtk_cell_renderer_text_new ();
410
411   column = gtk_tree_view_column_new_with_attributes ("Title",
412                                                      renderer,
413                                                      "text",
414                                                      0,
415                                                      NULL);
416
417   gtk_tree_view_append_column (GTK_TREE_VIEW (dialog->treeview), column);
418
419   g_signal_connect (get_widget_assert (xml, "val_labs_cancel"),
420                    "clicked",
421                    G_CALLBACK (on_cancel), dialog);
422
423   g_signal_connect (dialog->window, "delete-event",
424                     G_CALLBACK (on_delete), dialog);
425
426   g_signal_connect (get_widget_assert (xml, "val_labs_ok"),
427                    "clicked",
428                    G_CALLBACK (val_labs_ok), dialog);
429
430   dialog->change_handler_id =
431     g_signal_connect (dialog->label_entry,
432                      "changed",
433                      G_CALLBACK (on_label_entry_change), dialog);
434
435   dialog->value_handler_id  =
436     g_signal_connect (dialog->value_entry,
437                      "changed",
438                      G_CALLBACK (on_value_entry_change), dialog);
439
440   g_signal_connect (dialog->change_button,
441                    "clicked",
442                    G_CALLBACK (on_change), dialog);
443
444
445   g_signal_connect (dialog->treeview, "cursor-changed",
446                    G_CALLBACK (on_select_row), dialog);
447
448   g_signal_connect (dialog->remove_button, "clicked",
449                    G_CALLBACK (on_remove), dialog);
450
451   g_signal_connect (dialog->add_button, "clicked",
452                    G_CALLBACK (on_add), dialog);
453
454   dialog->labs = 0;
455
456   g_object_unref (xml);
457
458   return dialog;
459 }
460
461
462 void
463 val_labs_dialog_set_target_variable (struct val_labs_dialog *dialog,
464                                      struct variable *var)
465 {
466   dialog->pv = var;
467 }
468
469
470
471 /* Populate the components of the dialog box, from the 'labs' member
472    variable */
473 static void
474 repopulate_dialog (struct val_labs_dialog *dialog)
475 {
476   const struct val_lab **labels;
477   size_t n_labels;
478   size_t i;
479
480   GtkTreeIter iter;
481
482   GtkListStore *list_store = gtk_list_store_new (2,
483                                                  G_TYPE_STRING,
484                                                  G_TYPE_DOUBLE);
485
486   g_signal_handler_block (GTK_ENTRY (dialog->label_entry),
487                          dialog->change_handler_id);
488   g_signal_handler_block (GTK_ENTRY (dialog->value_entry),
489                          dialog->value_handler_id);
490
491   gtk_entry_set_text (GTK_ENTRY (dialog->value_entry), "");
492   gtk_entry_set_text (GTK_ENTRY (dialog->label_entry), "");
493
494   g_signal_handler_unblock (GTK_ENTRY (dialog->value_entry),
495                          dialog->value_handler_id);
496   g_signal_handler_unblock (GTK_ENTRY (dialog->label_entry),
497                            dialog->change_handler_id);
498
499   labels = val_labs_sorted (dialog->labs);
500   n_labels = val_labs_count (dialog->labs);
501   for (i = 0; i < n_labels; i++)
502     {
503       const struct val_lab *vl = labels[i];
504
505       gchar *const vstr  =
506         value_to_text (vl->value, dialog->var_store->dict,
507                       *var_get_write_format (dialog->pv));
508
509       gchar *const text = g_strdup_printf ("%s = \"%s\"",
510                                            vstr, val_lab_get_label (vl));
511
512       gtk_list_store_append (list_store, &iter);
513       gtk_list_store_set (list_store, &iter,
514                           0, text,
515                           1, vl->value.f,
516                           -1);
517
518       g_free (text);
519       g_free (vstr);
520     }
521   free (labels);
522
523   gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->treeview),
524                           GTK_TREE_MODEL (list_store));
525
526   g_object_unref (list_store);
527
528 }
529
530 /* Initialise and display the dialog box */
531 void
532 val_labs_dialog_show (struct val_labs_dialog *dialog)
533 {
534   const struct val_labs *value_labels;
535
536   g_assert (!dialog->labs);
537
538   value_labels = var_get_value_labels (dialog->pv);
539
540   if (value_labels)
541     dialog->labs = val_labs_clone ( value_labels );
542   else
543     dialog->labs = val_labs_create ( var_get_width (dialog->pv));
544
545   gtk_widget_set_sensitive (dialog->remove_button, FALSE);
546   gtk_widget_set_sensitive (dialog->change_button, FALSE);
547   gtk_widget_set_sensitive (dialog->add_button, FALSE);
548
549   gtk_widget_grab_focus (dialog->value_entry);
550
551   repopulate_dialog (dialog);
552   gtk_widget_show (dialog->window);
553 }
554