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