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