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 gint result = psppire_dialog_run (PSPPIRE_DIALOG (dialog));
167 if (result == GTK_RESPONSE_OK)
168 mv_copy (mv, psppire_missing_val_dialog_get_missing_values (dialog));
170 mv_copy (mv, var_get_missing_values (var));
172 gtk_widget_destroy (GTK_WIDGET (dialog));
177 /* A simple (sub) dialog box for displaying user input errors */
179 err_dialog (const gchar *msg, GtkWindow *window)
182 gtk_message_dialog_new (window,
183 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
188 gtk_dialog_run (GTK_DIALOG (dialog));
189 gtk_widget_destroy (dialog);
192 /* Interpret text, display error dialog
193 If parsing is o.k., the value is initialized and it is the responsibility of
194 the caller to destroy the variable. */
196 try_missing_value(const PsppireMissingValDialog *dialog, const gchar *text, union value *vp)
198 const int var_width = fmt_var_width (&dialog->format);
199 char *error_txt = NULL;
201 value_init(vp, var_width);
202 error_txt = data_in (ss_cstr(text), "UTF-8", dialog->format.type,
203 vp, var_width, dialog->encoding);
206 err_dialog (error_txt, GTK_WINDOW (dialog));
212 if (mv_is_acceptable (vp, var_width))
216 err_dialog (_("The maximum length of a missing value"
217 " for a string variable is 8 in UTF-8."),
218 GTK_WINDOW (dialog));
223 value_destroy (vp, var_width);
227 /* Acceptability predicate for PsppireMissingValDialog.
229 This function is also the only place that dialog->mvl gets updated. */
231 missing_val_dialog_acceptable (gpointer data)
233 PsppireMissingValDialog *dialog = data;
234 int var_width = fmt_var_width (&dialog->format);
236 if (gtk_toggle_button_get_active (dialog->button_discrete))
241 mv_clear(&dialog->mvl);
242 for(i = 0 ; i < 3 ; ++i)
245 g_strdup (gtk_entry_get_text (GTK_ENTRY (dialog->mv[i])));
248 if (!text || strlen (g_strstrip (text)) == 0)
254 if (!try_missing_value (dialog, text, &v))
257 gtk_widget_grab_focus (dialog->mv[i]);
260 mv_add_value (&dialog->mvl, &v);
263 value_destroy (&v, var_width);
267 err_dialog (_("At least one value must be specified"),
268 GTK_WINDOW (dialog));
269 gtk_widget_grab_focus (dialog->mv[0]);
274 if (gtk_toggle_button_get_active (dialog->button_range))
276 gchar *discrete_text;
277 union value low_val ;
278 union value high_val;
279 const gchar *low_text = gtk_entry_get_text (GTK_ENTRY (dialog->low));
280 const gchar *high_text = gtk_entry_get_text (GTK_ENTRY (dialog->high));
282 assert (var_width == 0); /* Ranges are only for numeric variables */
284 if (!try_missing_value(dialog, low_text, &low_val))
286 gtk_widget_grab_focus (dialog->low);
289 if (!try_missing_value (dialog, high_text, &high_val))
291 gtk_widget_grab_focus (dialog->high);
292 value_destroy (&low_val, var_width);
295 if (low_val.f > high_val.f)
297 err_dialog (_("Incorrect range specification"),
298 GTK_WINDOW (dialog));
299 value_destroy (&low_val, var_width);
300 value_destroy (&high_val, var_width);
301 gtk_widget_grab_focus (dialog->low);
304 mv_clear (&dialog->mvl);
305 mv_add_range (&dialog->mvl, low_val.f, high_val.f);
306 value_destroy (&low_val, var_width);
307 value_destroy (&high_val, var_width);
309 discrete_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (dialog->discrete)));
311 if (discrete_text && strlen (g_strstrip (discrete_text)) > 0)
313 union value discrete_val;
314 if (!try_missing_value (dialog, discrete_text, &discrete_val))
316 g_free (discrete_text);
317 gtk_widget_grab_focus (dialog->discrete);
320 mv_add_value (&dialog->mvl, &discrete_val);
321 value_destroy (&discrete_val, var_width);
323 g_free (discrete_text);
326 if (gtk_toggle_button_get_active (dialog->button_none))
327 mv_clear (&dialog->mvl);
333 /* Callback which occurs when the 'discrete' radiobutton is toggled */
335 discrete (GtkToggleButton *button, gpointer data)
338 PsppireMissingValDialog *dialog = data;
340 for (i = 0 ; i < 3 ; ++i)
342 gtk_widget_set_sensitive (dialog->mv[i],
343 gtk_toggle_button_get_active (button));
347 /* Callback which occurs when the 'range' radiobutton is toggled */
349 range (GtkToggleButton *button, gpointer data)
351 PsppireMissingValDialog *dialog = data;
353 const gboolean active = gtk_toggle_button_get_active (button);
355 gtk_widget_set_sensitive (dialog->low, active);
356 gtk_widget_set_sensitive (dialog->high, active);
357 gtk_widget_set_sensitive (dialog->discrete, active);
362 /* Shows the dialog box and sets default values */
364 psppire_missing_val_dialog_constructor (GType type,
366 GObjectConstructParam *properties)
368 PsppireMissingValDialog *dialog;
369 GtkContainer *content_area;
373 obj = G_OBJECT_CLASS (psppire_missing_val_dialog_parent_class)->constructor (
374 type, n_properties, properties);
375 dialog = PSPPIRE_MISSING_VAL_DIALOG (obj);
377 content_area = GTK_CONTAINER (PSPPIRE_DIALOG (dialog));
378 xml = builder_new ("missing-val-dialog.ui");
379 gtk_container_add (GTK_CONTAINER (content_area),
380 get_widget_assert (xml, "missing-values-dialog"));
382 dialog->mv[0] = get_widget_assert (xml, "mv0");
383 dialog->mv[1] = get_widget_assert (xml, "mv1");
384 dialog->mv[2] = get_widget_assert (xml, "mv2");
386 dialog->low = get_widget_assert (xml, "mv-low");
387 dialog->high = get_widget_assert (xml, "mv-high");
388 dialog->discrete = get_widget_assert (xml, "mv-discrete");
391 dialog->button_none =
392 GTK_TOGGLE_BUTTON (get_widget_assert (xml, "no_missing"));
394 dialog->button_discrete =
395 GTK_TOGGLE_BUTTON (get_widget_assert (xml, "discrete_missing"));
397 dialog->button_range =
398 GTK_TOGGLE_BUTTON (get_widget_assert (xml, "range_missing"));
400 psppire_dialog_set_accept_predicate (PSPPIRE_DIALOG (dialog),
401 missing_val_dialog_acceptable,
404 g_signal_connect (dialog->button_discrete, "toggled",
405 G_CALLBACK (discrete), dialog);
407 g_signal_connect (dialog->button_range, "toggled",
408 G_CALLBACK (range), dialog);
410 g_object_unref (xml);
416 psppire_missing_val_dialog_set_variable (PsppireMissingValDialog *dialog,
417 const struct variable *var)
419 enum val_type var_type;
422 mv_destroy (&dialog->mvl);
423 g_free (dialog->encoding);
427 const struct missing_values *vmv = var_get_missing_values (var);
428 if (mv_is_empty(vmv))
429 mv_init (&dialog->mvl, var_get_width(var));
431 mv_copy (&dialog->mvl, vmv);
432 dialog->encoding = g_strdup (var_get_encoding (var));
433 dialog->format = *var_get_print_format (var);
437 mv_init (&dialog->mvl, 0);
438 dialog->encoding = NULL;
439 dialog->format = F_8_0;
442 /* Blank all entry boxes and make them insensitive */
443 gtk_entry_set_text (GTK_ENTRY (dialog->low), "");
444 gtk_entry_set_text (GTK_ENTRY (dialog->high), "");
445 gtk_entry_set_text (GTK_ENTRY (dialog->discrete), "");
446 gtk_widget_set_sensitive (dialog->low, FALSE);
447 gtk_widget_set_sensitive (dialog->high, FALSE);
448 gtk_widget_set_sensitive (dialog->discrete, FALSE);
450 var_type = val_type_from_width (fmt_var_width (&dialog->format));
451 gtk_widget_set_sensitive (GTK_WIDGET (dialog->button_range),
452 var_type == VAL_NUMERIC);
457 for (i = 0 ; i < 3 ; ++i)
459 gtk_entry_set_text (GTK_ENTRY (dialog->mv[i]), "");
460 gtk_widget_set_sensitive (dialog->mv[i], FALSE);
463 if (mv_has_range (&dialog->mvl))
465 union value low, high;
468 mv_get_range (&dialog->mvl, &low.f, &high.f);
471 low_text = value_to_text__ (low, &dialog->format, dialog->encoding);
472 high_text = value_to_text__ (high, &dialog->format, dialog->encoding);
474 gtk_entry_set_text (GTK_ENTRY (dialog->low), low_text);
475 gtk_entry_set_text (GTK_ENTRY (dialog->high), high_text);
479 if (mv_has_value (&dialog->mvl))
482 text = value_to_text__ (*mv_get_value (&dialog->mvl, 0),
483 &dialog->format, dialog->encoding);
484 gtk_entry_set_text (GTK_ENTRY (dialog->discrete), text);
488 gtk_toggle_button_set_active (dialog->button_range, TRUE);
489 gtk_widget_set_sensitive (dialog->low, TRUE);
490 gtk_widget_set_sensitive (dialog->high, TRUE);
491 gtk_widget_set_sensitive (dialog->discrete, TRUE);
494 else if (mv_has_value (&dialog->mvl))
496 const int n = mv_n_values (&dialog->mvl);
498 for (i = 0 ; i < 3 ; ++i)
504 text = value_to_text__ (*mv_get_value (&dialog->mvl, i),
505 &dialog->format, dialog->encoding);
506 gtk_entry_set_text (GTK_ENTRY (dialog->mv[i]), text);
509 gtk_widget_set_sensitive (dialog->mv[i], TRUE);
511 gtk_toggle_button_set_active (dialog->button_discrete, TRUE);
513 else if (mv_is_empty (&dialog->mvl))
515 gtk_toggle_button_set_active (dialog->button_none, TRUE);
519 const struct missing_values *
520 psppire_missing_val_dialog_get_missing_values (
521 const PsppireMissingValDialog *dialog)