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