Fixed bug in value labels dialog box
[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   dialog->window = get_widget_assert (xml,"val_labs_dialog");
375   dialog->value_entry = get_widget_assert (xml,"value_entry");
376   dialog->label_entry = get_widget_assert (xml,"label_entry");
377
378   gtk_window_set_transient_for
379     (GTK_WINDOW (dialog->window),
380      GTK_WINDOW (get_widget_assert (xml, "data_editor")));
381
382   dialog->add_button = get_widget_assert (xml, "val_labs_add");
383   dialog->remove_button = get_widget_assert (xml, "val_labs_remove");
384   dialog->change_button = get_widget_assert (xml, "val_labs_change");
385
386   dialog->treeview = get_widget_assert (xml,"treeview1");
387
388   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (dialog->treeview), FALSE);
389
390   renderer = gtk_cell_renderer_text_new ();
391
392   column = gtk_tree_view_column_new_with_attributes ("Title",
393                                                      renderer,
394                                                      "text",
395                                                      0,
396                                                      NULL);
397
398   gtk_tree_view_append_column (GTK_TREE_VIEW (dialog->treeview), column);
399
400   g_signal_connect (GTK_OBJECT (get_widget_assert (xml, "val_labs_cancel")),
401                    "clicked",
402                    GTK_SIGNAL_FUNC (on_cancel), dialog);
403
404   g_signal_connect (GTK_OBJECT (dialog->window), "delete-event",
405                     GTK_SIGNAL_FUNC (on_delete), dialog);
406
407   g_signal_connect (GTK_OBJECT (get_widget_assert (xml, "val_labs_ok")),
408                    "clicked",
409                    GTK_SIGNAL_FUNC (val_labs_ok), dialog);
410
411   dialog->change_handler_id =
412     g_signal_connect (GTK_OBJECT (dialog->label_entry),
413                      "changed",
414                      GTK_SIGNAL_FUNC (on_label_entry_change), dialog);
415
416   dialog->value_handler_id  =
417     g_signal_connect (GTK_OBJECT (dialog->value_entry),
418                      "changed",
419                      GTK_SIGNAL_FUNC (on_value_entry_change), dialog);
420
421   g_signal_connect (GTK_OBJECT (dialog->change_button),
422                    "clicked",
423                    GTK_SIGNAL_FUNC (on_change), dialog);
424
425
426   g_signal_connect (GTK_OBJECT (dialog->treeview), "cursor-changed",
427                    GTK_SIGNAL_FUNC (on_select_row), dialog);
428
429   g_signal_connect (GTK_OBJECT (dialog->remove_button), "clicked",
430                    GTK_SIGNAL_FUNC (on_remove), dialog);
431
432   g_signal_connect (GTK_OBJECT (dialog->add_button), "clicked",
433                    GTK_SIGNAL_FUNC (on_add), dialog);
434
435   dialog->labs = 0;
436
437   return dialog;
438 }
439
440
441 void
442 val_labs_dialog_set_target_variable (struct val_labs_dialog *dialog,
443                                      struct variable *var)
444 {
445   dialog->pv = var;
446 }
447
448
449
450 /* Populate the components of the dialog box, from the 'labs' member
451    variable */
452 static void
453 repopulate_dialog (struct val_labs_dialog *dialog)
454 {
455   struct val_labs_iterator *vli = 0;
456   struct val_lab *vl;
457
458   GtkTreeIter iter;
459
460   GtkListStore *list_store = gtk_list_store_new (2,
461                                                  G_TYPE_STRING,
462                                                  G_TYPE_DOUBLE);
463
464   g_signal_handler_block (GTK_ENTRY (dialog->label_entry),
465                          dialog->change_handler_id);
466   g_signal_handler_block (GTK_ENTRY (dialog->value_entry),
467                          dialog->value_handler_id);
468
469   gtk_entry_set_text (GTK_ENTRY (dialog->value_entry), "");
470   gtk_entry_set_text (GTK_ENTRY (dialog->label_entry), "");
471
472   g_signal_handler_unblock (GTK_ENTRY (dialog->value_entry),
473                          dialog->value_handler_id);
474   g_signal_handler_unblock (GTK_ENTRY (dialog->label_entry),
475                            dialog->change_handler_id);
476
477
478   for (vl = val_labs_first_sorted (dialog->labs, &vli);
479       vl;
480       vl = val_labs_next (dialog->labs, &vli))
481     {
482
483       gchar *const vstr  =
484         value_to_text (vl->value,
485                       *var_get_write_format (dialog->pv));
486
487       gchar *labeltext =
488         pspp_locale_to_utf8 (vl->label, -1, 0);
489
490       gchar *const text = g_strdup_printf ("%s = \"%s\"",
491                                           vstr, labeltext);
492
493
494       gtk_list_store_append (list_store, &iter);
495       gtk_list_store_set (list_store, &iter,
496                           0, text,
497                           1, vl->value.f,
498                           -1);
499
500       g_free (labeltext);
501       g_free (text);
502       g_free (vstr);
503     }
504
505   gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->treeview),
506                           GTK_TREE_MODEL (list_store));
507
508   g_object_unref (list_store);
509
510 }
511
512 /* Initialise and display the dialog box */
513 void
514 val_labs_dialog_show (struct val_labs_dialog *dialog)
515 {
516   const struct val_labs *value_labels;
517
518   g_assert (!dialog->labs);
519
520   value_labels = var_get_value_labels (dialog->pv);
521
522   if (value_labels)
523     dialog->labs = val_labs_copy ( value_labels );
524   else
525     dialog->labs = val_labs_create ( var_get_width (dialog->pv));
526
527   gtk_widget_set_sensitive (dialog->remove_button, FALSE);
528   gtk_widget_set_sensitive (dialog->change_button, FALSE);
529   gtk_widget_set_sensitive (dialog->add_button, FALSE);
530
531   repopulate_dialog (dialog);
532   gtk_widget_show (dialog->window);
533 }
534