Ensure that windows opens the right file for output.
[pspp] / 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
274   return FALSE;
275 }
276
277 /* Callback which occurs when the "Add" button is clicked */
278 static gint
279 on_add (GtkWidget *w, gpointer data)
280 {
281   struct val_labs_dialog *dialog = data;
282
283   union value v;
284
285   const gchar *text = gtk_entry_get_text (GTK_ENTRY (dialog->value_entry));
286
287   text_to_value (text, &v,
288                 *var_get_write_format (dialog->pv));
289
290
291   if ( ! val_labs_add (dialog->labs, v,
292                        gtk_entry_get_text
293                        ( GTK_ENTRY (dialog->label_entry)) ) )
294     return FALSE;
295
296   gtk_widget_set_sensitive (dialog->add_button, FALSE);
297
298   repopulate_dialog (dialog);
299
300   return FALSE;
301 }
302
303 /* Callback which occurs when the "Remove" button is clicked */
304 static gint
305 on_remove (GtkWidget *w, gpointer data)
306 {
307   struct val_labs_dialog *dialog = data;
308
309   struct val_lab *vl = get_selected_tuple (dialog);
310
311   val_labs_remove (dialog->labs, vl->value);
312
313   repopulate_dialog (dialog);
314
315   gtk_widget_set_sensitive (dialog->remove_button, FALSE);
316
317   return FALSE;
318 }
319
320
321
322 /* Callback which occurs when a line item is selected in the list of
323    value--label pairs.*/
324 static void
325 on_select_row                  (GtkTreeView *treeview,
326                                 gpointer data)
327 {
328   gchar *labeltext;
329   struct val_labs_dialog *dialog = data;
330
331   struct val_lab * vl  = get_selected_tuple (dialog);
332
333   gchar *const text = value_to_text (vl->value,
334                                     *var_get_write_format (dialog->pv));
335
336   g_signal_handler_block (GTK_ENTRY (dialog->value_entry),
337                          dialog->value_handler_id);
338
339   gtk_entry_set_text (GTK_ENTRY (dialog->value_entry), text);
340
341   g_signal_handler_unblock (GTK_ENTRY (dialog->value_entry),
342                          dialog->value_handler_id);
343   g_free (text);
344
345   g_signal_handler_block (GTK_ENTRY (dialog->label_entry),
346                          dialog->change_handler_id);
347
348   labeltext = pspp_locale_to_utf8 (vl->label, -1, 0);
349   gtk_entry_set_text (GTK_ENTRY (dialog->label_entry),
350                      labeltext);
351   g_free (labeltext);
352
353   g_signal_handler_unblock (GTK_ENTRY (dialog->label_entry),
354                          dialog->change_handler_id);
355
356   gtk_widget_set_sensitive (dialog->remove_button, TRUE);
357   gtk_widget_set_sensitive (dialog->change_button, FALSE);
358 }
359
360
361 /* Create a new dialog box
362    (there should  normally be only one)*/
363 struct val_labs_dialog *
364 val_labs_dialog_create (GladeXML *xml)
365 {
366   GtkTreeViewColumn *column;
367
368   GtkCellRenderer *renderer ;
369
370   struct val_labs_dialog *dialog = g_malloc (sizeof (*dialog));
371
372   connect_help (xml);
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 (get_widget_assert (xml, "val_labs_cancel"),
401                    "clicked",
402                    GTK_SIGNAL_FUNC (on_cancel), dialog);
403
404   g_signal_connect (dialog->window, "delete-event",
405                     GTK_SIGNAL_FUNC (on_delete), dialog);
406
407   g_signal_connect (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 (dialog->label_entry,
413                      "changed",
414                      GTK_SIGNAL_FUNC (on_label_entry_change), dialog);
415
416   dialog->value_handler_id  =
417     g_signal_connect (dialog->value_entry,
418                      "changed",
419                      GTK_SIGNAL_FUNC (on_value_entry_change), dialog);
420
421   g_signal_connect (dialog->change_button,
422                    "clicked",
423                    GTK_SIGNAL_FUNC (on_change), dialog);
424
425
426   g_signal_connect (dialog->treeview, "cursor-changed",
427                    GTK_SIGNAL_FUNC (on_select_row), dialog);
428
429   g_signal_connect (dialog->remove_button, "clicked",
430                    GTK_SIGNAL_FUNC (on_remove), dialog);
431
432   g_signal_connect (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_clone ( 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