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