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