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