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