1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2005, 2006, 2009, 2011, 2012, 2015, 2016 Free Software Foundation
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.
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.
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/>. */
17 /* This module describes the behaviour of the Missing Values dialog box,
18 used for input of the missing values in the variable sheet */
22 #include "ui/gui/missing-val-dialog.h"
24 #include "builder-wrapper.h"
26 #include <data/format.h>
27 #include "missing-val-dialog.h"
28 #include <data/missing-values.h>
29 #include <data/variable.h>
30 #include <data/data-in.h>
37 #define _(msgid) gettext (msgid)
38 #define N_(msgid) msgid
40 static GObject *psppire_missing_val_dialog_constructor (
41 GType type, guint, GObjectConstructParam *);
42 static void psppire_missing_val_dialog_finalize (GObject *);
44 G_DEFINE_TYPE (PsppireMissingValDialog,
45 psppire_missing_val_dialog,
55 psppire_missing_val_dialog_set_property (GObject *object,
60 PsppireMissingValDialog *obj = PSPPIRE_MISSING_VAL_DIALOG (object);
65 psppire_missing_val_dialog_set_variable (obj,
66 g_value_get_pointer (value));
68 case PROP_MISSING_VALUES:
70 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
76 psppire_missing_val_dialog_get_property (GObject *object,
81 PsppireMissingValDialog *obj = PSPPIRE_MISSING_VAL_DIALOG (object);
85 case PROP_MISSING_VALUES:
86 g_value_set_pointer (value, &obj->mvl);
90 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
96 psppire_missing_val_dialog_class_init (PsppireMissingValDialogClass *class)
98 GObjectClass *gobject_class;
99 gobject_class = G_OBJECT_CLASS (class);
101 gobject_class->constructor = psppire_missing_val_dialog_constructor;
102 gobject_class->finalize = psppire_missing_val_dialog_finalize;
103 gobject_class->set_property = psppire_missing_val_dialog_set_property;
104 gobject_class->get_property = psppire_missing_val_dialog_get_property;
106 g_object_class_install_property (
107 gobject_class, PROP_VARIABLE,
108 g_param_spec_pointer ("variable",
110 "Variable whose missing values are to be edited. "
111 "The variable's print format and encoding are also "
115 g_object_class_install_property (
116 gobject_class, PROP_MISSING_VALUES,
117 g_param_spec_pointer ("missing-values",
119 "Edited missing values.",
124 psppire_missing_val_dialog_init (PsppireMissingValDialog *dialog)
126 /* We do all of our work on widgets in the constructor function, because that
127 runs after the construction properties have been set. Otherwise
128 PsppireDialog's "orientation" property hasn't been set and therefore we
129 have no box to populate. */
130 mv_init (&dialog->mvl, 0);
131 dialog->encoding = NULL;
135 psppire_missing_val_dialog_finalize (GObject *obj)
137 PsppireMissingValDialog *dialog = PSPPIRE_MISSING_VAL_DIALOG (obj);
139 mv_destroy (&dialog->mvl);
140 g_free (dialog->encoding);
142 G_OBJECT_CLASS (psppire_missing_val_dialog_parent_class)->finalize (obj);
145 PsppireMissingValDialog *
146 psppire_missing_val_dialog_new (const struct variable *var)
148 return PSPPIRE_MISSING_VAL_DIALOG (
149 g_object_new (PSPPIRE_TYPE_MISSING_VAL_DIALOG,
155 psppire_missing_val_dialog_run (GtkWindow *parent_window,
156 const struct variable *var,
157 struct missing_values *mv)
159 PsppireMissingValDialog *dialog;
161 dialog = psppire_missing_val_dialog_new (var);
162 gtk_window_set_transient_for (GTK_WINDOW (dialog), parent_window);
163 gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
164 gtk_widget_show (GTK_WIDGET (dialog));
166 if (psppire_dialog_run (PSPPIRE_DIALOG (dialog)) == GTK_RESPONSE_OK)
167 mv_copy (mv, psppire_missing_val_dialog_get_missing_values (dialog));
169 mv_copy (mv, var_get_missing_values (var));
171 gtk_widget_destroy (GTK_WIDGET (dialog));
175 /* A simple (sub) dialog box for displaying user input errors */
177 err_dialog (const gchar *msg, GtkWindow *window)
180 gtk_message_dialog_new (window,
181 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
186 gtk_dialog_run (GTK_DIALOG (dialog));
187 gtk_widget_destroy (dialog);
190 /* Interpret text, display error dialog
191 If parsing is o.k., the value is initialized and it is the responsibility of
192 the caller to destroy the variable. */
194 try_missing_value(const PsppireMissingValDialog *dialog, const gchar *text, union value *vp)
196 const int var_width = fmt_var_width (&dialog->format);
197 char *error_txt = NULL;
199 value_init(vp, var_width);
200 error_txt = data_in (ss_cstr(text), "UTF-8", dialog->format.type,
201 vp, var_width, dialog->encoding);
204 err_dialog (error_txt, GTK_WINDOW (dialog));
210 if (mv_is_acceptable (vp, var_width))
214 err_dialog (_("The maximum length of a missing value"
215 " for a string variable is 8 in UTF-8."),
216 GTK_WINDOW (dialog));
221 value_destroy (vp, var_width);
225 /* Acceptability predicate for PsppireMissingValDialog.
227 This function is also the only place that dialog->mvl gets updated. */
229 missing_val_dialog_acceptable (gpointer data)
231 PsppireMissingValDialog *dialog = data;
232 int var_width = fmt_var_width (&dialog->format);
234 if ( gtk_toggle_button_get_active (dialog->button_discrete))
239 mv_clear(&dialog->mvl);
240 for(i = 0 ; i < 3 ; ++i )
243 g_strdup (gtk_entry_get_text (GTK_ENTRY (dialog->mv[i])));
246 if ( !text || strlen (g_strstrip (text)) == 0 )
252 if (!try_missing_value (dialog, text, &v))
255 gtk_widget_grab_focus (dialog->mv[i]);
258 mv_add_value (&dialog->mvl, &v);
261 value_destroy (&v, var_width);
265 err_dialog (_("At least one value must be specified"),
266 GTK_WINDOW (dialog));
267 gtk_widget_grab_focus (dialog->mv[0]);
272 if (gtk_toggle_button_get_active (dialog->button_range))
274 gchar *discrete_text;
275 union value low_val ;
276 union value high_val;
277 const gchar *low_text = gtk_entry_get_text (GTK_ENTRY (dialog->low));
278 const gchar *high_text = gtk_entry_get_text (GTK_ENTRY (dialog->high));
280 assert (var_width == 0); /* Ranges are only for numeric variables */
282 if (!try_missing_value(dialog, low_text, &low_val))
284 gtk_widget_grab_focus (dialog->low);
287 if (!try_missing_value (dialog, high_text, &high_val))
289 gtk_widget_grab_focus (dialog->high);
290 value_destroy (&low_val, var_width);
293 if (low_val.f > high_val.f)
295 err_dialog (_("Incorrect range specification"),
296 GTK_WINDOW (dialog));
297 value_destroy (&low_val, var_width);
298 value_destroy (&high_val, var_width);
299 gtk_widget_grab_focus (dialog->low);
302 mv_clear (&dialog->mvl);
303 mv_add_range (&dialog->mvl, low_val.f, high_val.f);
304 value_destroy (&low_val, var_width);
305 value_destroy (&high_val, var_width);
307 discrete_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (dialog->discrete)));
309 if ( discrete_text && strlen (g_strstrip (discrete_text)) > 0 )
311 union value discrete_val;
312 if (!try_missing_value (dialog, discrete_text, &discrete_val))
314 g_free (discrete_text);
315 gtk_widget_grab_focus (dialog->discrete);
318 mv_add_value (&dialog->mvl, &discrete_val);
319 value_destroy (&discrete_val, var_width);
321 g_free (discrete_text);
324 if (gtk_toggle_button_get_active (dialog->button_none))
325 mv_clear (&dialog->mvl);
331 /* Callback which occurs when the 'discrete' radiobutton is toggled */
333 discrete (GtkToggleButton *button, gpointer data)
336 PsppireMissingValDialog *dialog = data;
338 for (i = 0 ; i < 3 ; ++i )
340 gtk_widget_set_sensitive (dialog->mv[i],
341 gtk_toggle_button_get_active (button));
345 /* Callback which occurs when the 'range' radiobutton is toggled */
347 range (GtkToggleButton *button, gpointer data)
349 PsppireMissingValDialog *dialog = data;
351 const gboolean active = gtk_toggle_button_get_active (button);
353 gtk_widget_set_sensitive (dialog->low, active);
354 gtk_widget_set_sensitive (dialog->high, active);
355 gtk_widget_set_sensitive (dialog->discrete, active);
360 /* Shows the dialog box and sets default values */
362 psppire_missing_val_dialog_constructor (GType type,
364 GObjectConstructParam *properties)
366 PsppireMissingValDialog *dialog;
367 GtkContainer *content_area;
371 obj = G_OBJECT_CLASS (psppire_missing_val_dialog_parent_class)->constructor (
372 type, n_properties, properties);
373 dialog = PSPPIRE_MISSING_VAL_DIALOG (obj);
375 content_area = GTK_CONTAINER (PSPPIRE_DIALOG (dialog));
376 xml = builder_new ("missing-val-dialog.ui");
377 gtk_container_add (GTK_CONTAINER (content_area),
378 get_widget_assert (xml, "missing-values-dialog"));
380 dialog->mv[0] = get_widget_assert (xml, "mv0");
381 dialog->mv[1] = get_widget_assert (xml, "mv1");
382 dialog->mv[2] = get_widget_assert (xml, "mv2");
384 dialog->low = get_widget_assert (xml, "mv-low");
385 dialog->high = get_widget_assert (xml, "mv-high");
386 dialog->discrete = get_widget_assert (xml, "mv-discrete");
389 dialog->button_none =
390 GTK_TOGGLE_BUTTON (get_widget_assert (xml, "no_missing"));
392 dialog->button_discrete =
393 GTK_TOGGLE_BUTTON (get_widget_assert (xml, "discrete_missing"));
395 dialog->button_range =
396 GTK_TOGGLE_BUTTON (get_widget_assert (xml, "range_missing"));
398 psppire_dialog_set_accept_predicate (PSPPIRE_DIALOG (dialog),
399 missing_val_dialog_acceptable,
402 g_signal_connect (dialog->button_discrete, "toggled",
403 G_CALLBACK (discrete), dialog);
405 g_signal_connect (dialog->button_range, "toggled",
406 G_CALLBACK (range), dialog);
408 g_object_unref (xml);
414 psppire_missing_val_dialog_set_variable (PsppireMissingValDialog *dialog,
415 const struct variable *var)
417 enum val_type var_type;
420 mv_destroy (&dialog->mvl);
421 g_free (dialog->encoding);
425 const struct missing_values *vmv = var_get_missing_values (var);
426 if (mv_is_empty(vmv))
427 mv_init (&dialog->mvl, var_get_width(var));
429 mv_copy (&dialog->mvl, vmv);
430 dialog->encoding = g_strdup (var_get_encoding (var));
431 dialog->format = *var_get_print_format (var);
435 mv_init (&dialog->mvl, 0);
436 dialog->encoding = NULL;
437 dialog->format = F_8_0;
440 /* Blank all entry boxes and make them insensitive */
441 gtk_entry_set_text (GTK_ENTRY (dialog->low), "");
442 gtk_entry_set_text (GTK_ENTRY (dialog->high), "");
443 gtk_entry_set_text (GTK_ENTRY (dialog->discrete), "");
444 gtk_widget_set_sensitive (dialog->low, FALSE);
445 gtk_widget_set_sensitive (dialog->high, FALSE);
446 gtk_widget_set_sensitive (dialog->discrete, FALSE);
448 var_type = val_type_from_width (fmt_var_width (&dialog->format));
449 gtk_widget_set_sensitive (GTK_WIDGET (dialog->button_range),
450 var_type == VAL_NUMERIC);
455 for (i = 0 ; i < 3 ; ++i )
457 gtk_entry_set_text (GTK_ENTRY (dialog->mv[i]), "");
458 gtk_widget_set_sensitive (dialog->mv[i], FALSE);
461 if ( mv_has_range (&dialog->mvl))
463 union value low, high;
466 mv_get_range (&dialog->mvl, &low.f, &high.f);
469 low_text = value_to_text__ (low, &dialog->format, dialog->encoding);
470 high_text = value_to_text__ (high, &dialog->format, dialog->encoding);
472 gtk_entry_set_text (GTK_ENTRY (dialog->low), low_text);
473 gtk_entry_set_text (GTK_ENTRY (dialog->high), high_text);
477 if ( mv_has_value (&dialog->mvl))
480 text = value_to_text__ (*mv_get_value (&dialog->mvl, 0),
481 &dialog->format, dialog->encoding);
482 gtk_entry_set_text (GTK_ENTRY (dialog->discrete), text);
486 gtk_toggle_button_set_active (dialog->button_range, TRUE);
487 gtk_widget_set_sensitive (dialog->low, TRUE);
488 gtk_widget_set_sensitive (dialog->high, TRUE);
489 gtk_widget_set_sensitive (dialog->discrete, TRUE);
492 else if ( mv_has_value (&dialog->mvl))
494 const int n = mv_n_values (&dialog->mvl);
496 for (i = 0 ; i < 3 ; ++i )
502 text = value_to_text__ (*mv_get_value (&dialog->mvl, i),
503 &dialog->format, dialog->encoding);
504 gtk_entry_set_text (GTK_ENTRY (dialog->mv[i]), text);
507 gtk_widget_set_sensitive (dialog->mv[i], TRUE);
509 gtk_toggle_button_set_active (dialog->button_discrete, TRUE);
511 else if ( mv_is_empty (&dialog->mvl))
513 gtk_toggle_button_set_active (dialog->button_none, TRUE);
517 const struct missing_values *
518 psppire_missing_val_dialog_get_missing_values (
519 const PsppireMissingValDialog *dialog)