Ensure that windows opens the right file for output.
[pspp] / src / ui / gui / missing-val-dialog.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2005, 2006  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 /*  This module describes the behaviour of the Missing Values dialog box,
18     used for input of the missing values in the variable sheet */
19
20 #include <config.h>
21 #include <gettext.h>
22 #define _(msgid) gettext (msgid)
23 #define N_(msgid) msgid
24
25
26 #include "helper.h"
27 #include <data/format.h>
28 #include "missing-val-dialog.h"
29 #include <data/missing-values.h>
30 #include <data/variable.h>
31 #include <data/data-in.h>
32
33
34 #include <gtk/gtk.h>
35 #include <glade/glade.h>
36
37 #include <string.h>
38
39
40 /* A simple (sub) dialog box for displaying user input errors */
41 static void
42 err_dialog (const gchar *msg, GtkWindow *window)
43 {
44   GtkWidget *hbox ;
45   GtkWidget *label = gtk_label_new (msg);
46
47   GtkWidget *dialog =
48     gtk_dialog_new_with_buttons ("PSPP",
49                                  window,
50                                  GTK_DIALOG_MODAL |
51                                  GTK_DIALOG_DESTROY_WITH_PARENT |
52                                  GTK_DIALOG_NO_SEPARATOR,
53                                  GTK_STOCK_OK,
54                                  GTK_RESPONSE_ACCEPT,
55                                  NULL);
56
57
58   GtkWidget *icon = gtk_image_new_from_stock (GTK_STOCK_DIALOG_ERROR,
59                                              GTK_ICON_SIZE_DIALOG);
60
61   g_signal_connect_swapped (dialog,
62                             "response",
63                             G_CALLBACK (gtk_widget_destroy),
64                             dialog);
65
66   hbox = gtk_hbox_new (FALSE, 10);
67
68   gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox),
69                      hbox);
70
71   gtk_box_pack_start (GTK_BOX (hbox), icon, TRUE, FALSE, 10);
72   gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 10);
73
74   gtk_widget_show_all (dialog);
75 }
76
77
78 /* Callback which occurs when the OK button is clicked */
79 static void
80 missing_val_dialog_accept (GtkWidget *w, gpointer data)
81 {
82   struct missing_val_dialog *dialog = data;
83
84   const struct fmt_spec *write_spec = var_get_write_format (dialog->pv);
85
86   if ( gtk_toggle_button_get_active (dialog->button_discrete))
87     {
88       gint nvals = 0;
89       gint badvals = 0;
90       gint i;
91       mv_clear(&dialog->mvl);
92       for(i = 0 ; i < 3 ; ++i )
93         {
94           gchar *text =
95             g_strdup (gtk_entry_get_text (GTK_ENTRY (dialog->mv[i])));
96
97           union value v;
98           if ( !text || strlen (g_strstrip (text)) == 0 )
99             {
100               g_free (text);
101               continue;
102             }
103
104           if ( text_to_value (text, &v, *write_spec))
105             {
106               nvals++;
107               mv_add_value (&dialog->mvl, &v);
108             }
109           else
110               badvals++;
111           g_free (text);
112         }
113       if ( nvals == 0 || badvals > 0 )
114         {
115           err_dialog (_("Incorrect value for variable type"),
116                      GTK_WINDOW (dialog->window));
117           return ;
118         }
119     }
120
121   if (gtk_toggle_button_get_active (dialog->button_range))
122     {
123       gchar *discrete_text ;
124
125       union value low_val ;
126       union value high_val;
127       const gchar *low_text = gtk_entry_get_text (GTK_ENTRY (dialog->low));
128       const gchar *high_text = gtk_entry_get_text (GTK_ENTRY (dialog->high));
129
130       if ( text_to_value (low_text, &low_val, *write_spec)
131            &&
132            text_to_value (high_text, &high_val, *write_spec) )
133         {
134           if ( low_val.f > high_val.f )
135             {
136               err_dialog (_("Incorrect range specification"),
137                           GTK_WINDOW (dialog->window));
138               return ;
139             }
140         }
141       else
142         {
143           err_dialog (_("Incorrect range specification"),
144                       GTK_WINDOW (dialog->window));
145           return;
146         }
147
148       discrete_text =
149         g_strdup (gtk_entry_get_text (GTK_ENTRY (dialog->discrete)));
150
151       mv_clear (&dialog->mvl);
152       mv_add_range (&dialog->mvl, low_val.f, high_val.f);
153
154       if ( discrete_text && strlen (g_strstrip (discrete_text)) > 0 )
155         {
156           union value discrete_val;
157           if ( !text_to_value (discrete_text, &discrete_val,
158                               *write_spec))
159             {
160               err_dialog (_("Incorrect value for variable type"),
161                          GTK_WINDOW (dialog->window) );
162               g_free (discrete_text);
163               return;
164             }
165           mv_add_value (&dialog->mvl, &discrete_val);
166         }
167       g_free (discrete_text);
168     }
169
170
171   if (gtk_toggle_button_get_active (dialog->button_none))
172     mv_clear (&dialog->mvl);
173
174   var_set_missing_values (dialog->pv, &dialog->mvl);
175
176   gtk_widget_hide (dialog->window);
177 }
178
179
180 /* Callback which occurs when the 'discrete' radiobutton is toggled */
181 static void
182 discrete (GtkToggleButton *button, gpointer data)
183 {
184   gint i;
185   struct missing_val_dialog *dialog = data;
186
187   for (i = 0 ; i < 3 ; ++i )
188     {
189       gtk_widget_set_sensitive (dialog->mv[i],
190                                gtk_toggle_button_get_active (button));
191     }
192 }
193
194 /* Callback which occurs when the 'range' radiobutton is toggled */
195 static void
196 range (GtkToggleButton *button, gpointer data)
197 {
198   struct missing_val_dialog *dialog = data;
199
200   const gboolean active = gtk_toggle_button_get_active (button);
201
202   gtk_widget_set_sensitive (dialog->low, active);
203   gtk_widget_set_sensitive (dialog->high, active);
204   gtk_widget_set_sensitive (dialog->discrete, active);
205 }
206
207
208
209 /* Callback for when the Missing Value dialog is closed using
210    the window delete button.*/
211 static gint
212 on_delete (GtkWidget *w, GdkEvent *e, gpointer data)
213 {
214   struct missing_val_dialog *dialog = data;
215
216   gtk_widget_hide (dialog->window);
217
218   return TRUE;
219 }
220
221
222 /* Creates the dialog structure from the xml */
223 struct missing_val_dialog *
224 missing_val_dialog_create (GladeXML *xml)
225 {
226   struct missing_val_dialog *dialog = g_malloc (sizeof (*dialog));
227
228   connect_help (xml);
229
230   dialog->window = get_widget_assert (xml, "missing_values_dialog");
231
232   gtk_window_set_transient_for
233     (GTK_WINDOW (dialog->window),
234      GTK_WINDOW (get_widget_assert (xml, "data_editor")));
235
236
237   g_signal_connect_swapped (get_widget_assert (xml, "missing_val_cancel"),
238                    "clicked", G_CALLBACK (gtk_widget_hide), dialog->window);
239
240   g_signal_connect (get_widget_assert (xml, "missing_val_ok"),
241                    "clicked", G_CALLBACK (missing_val_dialog_accept), dialog);
242
243   g_signal_connect (dialog->window, "delete-event",
244                     G_CALLBACK (on_delete), dialog);
245
246   dialog->mv[0] = get_widget_assert (xml, "mv0");
247   dialog->mv[1] = get_widget_assert (xml, "mv1");
248   dialog->mv[2] = get_widget_assert (xml, "mv2");
249
250   dialog->low = get_widget_assert (xml, "mv-low");
251   dialog->high = get_widget_assert (xml, "mv-high");
252   dialog->discrete = get_widget_assert (xml, "mv-discrete");
253
254
255   dialog->button_none     =
256     GTK_TOGGLE_BUTTON (get_widget_assert (xml, "no_missing"));
257
258   dialog->button_discrete =
259     GTK_TOGGLE_BUTTON (get_widget_assert (xml, "discrete_missing"));
260
261   dialog->button_range    =
262     GTK_TOGGLE_BUTTON (get_widget_assert (xml, "range_missing"));
263
264
265   g_signal_connect (G_OBJECT (dialog->button_discrete), "toggled",
266                    G_CALLBACK (discrete), dialog);
267
268   g_signal_connect (G_OBJECT (dialog->button_range), "toggled",
269                    G_CALLBACK (range), dialog);
270
271   return dialog;
272 }
273
274 /* Shows the dialog box and sets default values */
275 void
276 missing_val_dialog_show (struct missing_val_dialog *dialog)
277 {
278   const struct fmt_spec *write_spec ;
279
280   gint i;
281   g_return_if_fail (dialog);
282   g_return_if_fail (dialog->pv);
283
284   mv_copy (&dialog->mvl, var_get_missing_values (dialog->pv));
285
286   write_spec = var_get_write_format (dialog->pv);
287
288   /* Blank all entry boxes and make them insensitive */
289   gtk_entry_set_text (GTK_ENTRY (dialog->low), "");
290   gtk_entry_set_text (GTK_ENTRY (dialog->high), "");
291   gtk_entry_set_text (GTK_ENTRY (dialog->discrete), "");
292   gtk_widget_set_sensitive (dialog->low, FALSE);
293   gtk_widget_set_sensitive (dialog->high, FALSE);
294   gtk_widget_set_sensitive (dialog->discrete, FALSE);
295
296   gtk_widget_set_sensitive (GTK_WIDGET (dialog->button_range),
297                            var_is_numeric (dialog->pv));
298
299
300   for (i = 0 ; i < 3 ; ++i )
301     {
302       gtk_entry_set_text (GTK_ENTRY (dialog->mv[i]), "");
303       gtk_widget_set_sensitive (dialog->mv[i], FALSE);
304     }
305
306   if ( mv_has_range (&dialog->mvl))
307     {
308       union value low, high;
309       gchar *low_text;
310       gchar *high_text;
311       mv_get_range (&dialog->mvl, &low.f, &high.f);
312
313       low_text = value_to_text (low, *write_spec);
314       high_text = value_to_text (high, *write_spec);
315
316       gtk_entry_set_text (GTK_ENTRY (dialog->low), low_text);
317       gtk_entry_set_text (GTK_ENTRY (dialog->high), high_text);
318       g_free (low_text);
319       g_free (high_text);
320
321       if ( mv_has_value (&dialog->mvl))
322         {
323           gchar *text;
324           union value value;
325           mv_get_value (&dialog->mvl, &value, 0);
326           text = value_to_text (value, *write_spec);
327           gtk_entry_set_text (GTK_ENTRY (dialog->discrete), text);
328           g_free (text);
329         }
330
331       gtk_toggle_button_set_active (dialog->button_range, TRUE);
332       gtk_widget_set_sensitive (dialog->low, TRUE);
333       gtk_widget_set_sensitive (dialog->high, TRUE);
334       gtk_widget_set_sensitive (dialog->discrete, TRUE);
335
336     }
337   else if ( mv_has_value (&dialog->mvl))
338     {
339       const int n = mv_n_values (&dialog->mvl);
340
341       for (i = 0 ; i < 3 ; ++i )
342         {
343           if ( i < n)
344             {
345               gchar *text ;
346               union value value;
347
348               mv_get_value (&dialog->mvl, &value, i);
349               text = value_to_text (value, *write_spec);
350               gtk_entry_set_text (GTK_ENTRY (dialog->mv[i]), text);
351               g_free (text);
352             }
353           gtk_widget_set_sensitive (dialog->mv[i], TRUE);
354         }
355       gtk_toggle_button_set_active (dialog->button_discrete, TRUE);
356     }
357   else if ( mv_is_empty (&dialog->mvl))
358     {
359       gtk_toggle_button_set_active (dialog->button_none, TRUE);
360     }
361
362   gtk_widget_show (dialog->window);
363 }