1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2005, 2006, 2009, 2011, 2012, 2015, 2016,
3 2020 Free Software Foundation
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 3 of the License, or
8 (at your option) any later version.
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.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>. */
18 /* This module describes the behaviour of the Missing Values dialog box,
19 used for input of the missing values in the variable sheet */
23 #include "ui/gui/missing-val-dialog.h"
25 #include "builder-wrapper.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>
38 #define _(msgid) gettext (msgid)
39 #define N_(msgid) msgid
41 static GObject *psppire_missing_val_dialog_constructor (
42 GType type, guint, GObjectConstructParam *);
43 static void psppire_missing_val_dialog_finalize (GObject *);
45 G_DEFINE_TYPE (PsppireMissingValDialog,
46 psppire_missing_val_dialog,
56 psppire_missing_val_dialog_set_property (GObject *object,
61 PsppireMissingValDialog *obj = PSPPIRE_MISSING_VAL_DIALOG (object);
66 psppire_missing_val_dialog_set_variable (obj,
67 g_value_get_pointer (value));
69 case PROP_MISSING_VALUES:
71 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
77 psppire_missing_val_dialog_get_property (GObject *object,
82 PsppireMissingValDialog *obj = PSPPIRE_MISSING_VAL_DIALOG (object);
86 case PROP_MISSING_VALUES:
87 g_value_set_pointer (value, &obj->mvl);
91 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
97 psppire_missing_val_dialog_class_init (PsppireMissingValDialogClass *class)
99 GObjectClass *gobject_class;
100 gobject_class = G_OBJECT_CLASS (class);
102 gobject_class->constructor = psppire_missing_val_dialog_constructor;
103 gobject_class->finalize = psppire_missing_val_dialog_finalize;
104 gobject_class->set_property = psppire_missing_val_dialog_set_property;
105 gobject_class->get_property = psppire_missing_val_dialog_get_property;
107 g_object_class_install_property (
108 gobject_class, PROP_VARIABLE,
109 g_param_spec_pointer ("variable",
111 "Variable whose missing values are to be edited. "
112 "The variable's print format and encoding are also "
116 g_object_class_install_property (
117 gobject_class, PROP_MISSING_VALUES,
118 g_param_spec_pointer ("missing-values",
120 "Edited missing values.",
125 psppire_missing_val_dialog_init (PsppireMissingValDialog *dialog)
127 /* We do all of our work on widgets in the constructor function, because that
128 runs after the construction properties have been set. Otherwise
129 PsppireDialog's "orientation" property hasn't been set and therefore we
130 have no box to populate. */
131 mv_init (&dialog->mvl, 0);
132 dialog->encoding = NULL;
136 psppire_missing_val_dialog_finalize (GObject *obj)
138 PsppireMissingValDialog *dialog = PSPPIRE_MISSING_VAL_DIALOG (obj);
140 mv_destroy (&dialog->mvl);
141 g_free (dialog->encoding);
143 G_OBJECT_CLASS (psppire_missing_val_dialog_parent_class)->finalize (obj);
146 PsppireMissingValDialog *
147 psppire_missing_val_dialog_new (const struct variable *var)
149 return PSPPIRE_MISSING_VAL_DIALOG (
150 g_object_new (PSPPIRE_TYPE_MISSING_VAL_DIALOG,
156 psppire_missing_val_dialog_run (GtkWindow *parent_window,
157 const struct variable *var,
158 struct missing_values *mv)
160 PsppireMissingValDialog *dialog;
162 dialog = psppire_missing_val_dialog_new (var);
163 gtk_window_set_transient_for (GTK_WINDOW (dialog), parent_window);
164 gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
165 gtk_widget_show (GTK_WIDGET (dialog));
167 gint result = psppire_dialog_run (PSPPIRE_DIALOG (dialog));
168 if (result == GTK_RESPONSE_OK)
169 mv_copy (mv, psppire_missing_val_dialog_get_missing_values (dialog));
171 mv_copy (mv, var_get_missing_values (var));
173 gtk_widget_destroy (GTK_WIDGET (dialog));
178 /* A simple (sub) dialog box for displaying user input errors */
180 err_dialog (const gchar *msg, GtkWindow *window)
183 gtk_message_dialog_new (window,
184 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
189 gtk_dialog_run (GTK_DIALOG (dialog));
190 gtk_widget_destroy (dialog);
193 /* Interpret text, display error dialog
194 If parsing is o.k., the value is initialized and it is the responsibility of
195 the caller to destroy the variable. */
197 try_missing_value(const PsppireMissingValDialog *dialog, const gchar *text, union value *vp)
199 const int var_width = fmt_var_width (&dialog->format);
200 char *error_txt = NULL;
202 value_init(vp, var_width);
203 error_txt = data_in (ss_cstr(text), "UTF-8", dialog->format.type,
204 vp, var_width, dialog->encoding);
207 err_dialog (error_txt, GTK_WINDOW (dialog));
213 if (mv_is_acceptable (vp, var_width))
217 err_dialog (_("The maximum length of a missing value"
218 " for a string variable is 8 in UTF-8."),
219 GTK_WINDOW (dialog));
224 value_destroy (vp, var_width);
228 /* Acceptability predicate for PsppireMissingValDialog.
230 This function is also the only place that dialog->mvl gets updated. */
232 missing_val_dialog_acceptable (gpointer data)
234 PsppireMissingValDialog *dialog = data;
235 int var_width = fmt_var_width (&dialog->format);
237 if (gtk_toggle_button_get_active (dialog->button_discrete))
242 mv_clear(&dialog->mvl);
243 for(i = 0 ; i < 3 ; ++i)
246 g_strdup (gtk_entry_get_text (GTK_ENTRY (dialog->mv[i])));
249 if (!text || strlen (g_strstrip (text)) == 0)
255 if (!try_missing_value (dialog, text, &v))
258 gtk_widget_grab_focus (dialog->mv[i]);
261 mv_add_value (&dialog->mvl, &v);
264 value_destroy (&v, var_width);
268 err_dialog (_("At least one value must be specified"),
269 GTK_WINDOW (dialog));
270 gtk_widget_grab_focus (dialog->mv[0]);
275 if (gtk_toggle_button_get_active (dialog->button_range))
277 gchar *discrete_text;
278 union value low_val ;
279 union value high_val;
280 const gchar *low_text = gtk_entry_get_text (GTK_ENTRY (dialog->low));
281 const gchar *high_text = gtk_entry_get_text (GTK_ENTRY (dialog->high));
283 assert (var_width == 0); /* Ranges are only for numeric variables */
285 if (!try_missing_value(dialog, low_text, &low_val))
287 gtk_widget_grab_focus (dialog->low);
290 if (!try_missing_value (dialog, high_text, &high_val))
292 gtk_widget_grab_focus (dialog->high);
293 value_destroy (&low_val, var_width);
296 if (low_val.f > high_val.f)
298 err_dialog (_("Incorrect range specification"),
299 GTK_WINDOW (dialog));
300 value_destroy (&low_val, var_width);
301 value_destroy (&high_val, var_width);
302 gtk_widget_grab_focus (dialog->low);
305 mv_clear (&dialog->mvl);
306 mv_add_range (&dialog->mvl, low_val.f, high_val.f);
307 value_destroy (&low_val, var_width);
308 value_destroy (&high_val, var_width);
310 discrete_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (dialog->discrete)));
312 if (discrete_text && strlen (g_strstrip (discrete_text)) > 0)
314 union value discrete_val;
315 if (!try_missing_value (dialog, discrete_text, &discrete_val))
317 g_free (discrete_text);
318 gtk_widget_grab_focus (dialog->discrete);
321 mv_add_value (&dialog->mvl, &discrete_val);
322 value_destroy (&discrete_val, var_width);
324 g_free (discrete_text);
327 if (gtk_toggle_button_get_active (dialog->button_none))
328 mv_clear (&dialog->mvl);
334 /* Callback which occurs when the 'discrete' radiobutton is toggled */
336 discrete (GtkToggleButton *button, gpointer data)
339 PsppireMissingValDialog *dialog = data;
341 for (i = 0 ; i < 3 ; ++i)
343 gtk_widget_set_sensitive (dialog->mv[i],
344 gtk_toggle_button_get_active (button));
348 /* Callback which occurs when the 'range' radiobutton is toggled */
350 range (GtkToggleButton *button, gpointer data)
352 PsppireMissingValDialog *dialog = data;
354 const gboolean active = gtk_toggle_button_get_active (button);
356 gtk_widget_set_sensitive (dialog->low, active);
357 gtk_widget_set_sensitive (dialog->high, active);
358 gtk_widget_set_sensitive (dialog->discrete, active);
363 /* Shows the dialog box and sets default values */
365 psppire_missing_val_dialog_constructor (GType type,
367 GObjectConstructParam *properties)
369 PsppireMissingValDialog *dialog;
370 GtkContainer *content_area;
374 obj = G_OBJECT_CLASS (psppire_missing_val_dialog_parent_class)->constructor (
375 type, n_properties, properties);
376 dialog = PSPPIRE_MISSING_VAL_DIALOG (obj);
378 g_object_set (dialog, "help_page", "Missing-Observations",
379 "title", _("Missing Values"), NULL);
381 content_area = GTK_CONTAINER (PSPPIRE_DIALOG (dialog));
382 xml = builder_new ("missing-val-dialog.ui");
383 gtk_container_add (GTK_CONTAINER (content_area),
384 get_widget_assert (xml, "missing-values-dialog"));
386 dialog->mv[0] = get_widget_assert (xml, "mv0");
387 dialog->mv[1] = get_widget_assert (xml, "mv1");
388 dialog->mv[2] = get_widget_assert (xml, "mv2");
390 dialog->low = get_widget_assert (xml, "mv-low");
391 dialog->high = get_widget_assert (xml, "mv-high");
392 dialog->discrete = get_widget_assert (xml, "mv-discrete");
395 dialog->button_none =
396 GTK_TOGGLE_BUTTON (get_widget_assert (xml, "no_missing"));
398 dialog->button_discrete =
399 GTK_TOGGLE_BUTTON (get_widget_assert (xml, "discrete_missing"));
401 dialog->button_range =
402 GTK_TOGGLE_BUTTON (get_widget_assert (xml, "range_missing"));
404 psppire_dialog_set_accept_predicate (PSPPIRE_DIALOG (dialog),
405 missing_val_dialog_acceptable,
408 g_signal_connect (dialog->button_discrete, "toggled",
409 G_CALLBACK (discrete), dialog);
411 g_signal_connect (dialog->button_range, "toggled",
412 G_CALLBACK (range), dialog);
414 g_object_unref (xml);
420 psppire_missing_val_dialog_set_variable (PsppireMissingValDialog *dialog,
421 const struct variable *var)
423 enum val_type var_type;
426 mv_destroy (&dialog->mvl);
427 g_free (dialog->encoding);
431 const struct missing_values *vmv = var_get_missing_values (var);
432 if (mv_is_empty(vmv))
433 mv_init (&dialog->mvl, var_get_width(var));
435 mv_copy (&dialog->mvl, vmv);
436 dialog->encoding = g_strdup (var_get_encoding (var));
437 dialog->format = *var_get_print_format (var);
441 mv_init (&dialog->mvl, 0);
442 dialog->encoding = NULL;
443 dialog->format = F_8_0;
446 /* Blank all entry boxes and make them insensitive */
447 gtk_entry_set_text (GTK_ENTRY (dialog->low), "");
448 gtk_entry_set_text (GTK_ENTRY (dialog->high), "");
449 gtk_entry_set_text (GTK_ENTRY (dialog->discrete), "");
450 gtk_widget_set_sensitive (dialog->low, FALSE);
451 gtk_widget_set_sensitive (dialog->high, FALSE);
452 gtk_widget_set_sensitive (dialog->discrete, FALSE);
454 var_type = val_type_from_width (fmt_var_width (&dialog->format));
455 gtk_widget_set_sensitive (GTK_WIDGET (dialog->button_range),
456 var_type == VAL_NUMERIC);
461 for (i = 0 ; i < 3 ; ++i)
463 gtk_entry_set_text (GTK_ENTRY (dialog->mv[i]), "");
464 gtk_widget_set_sensitive (dialog->mv[i], FALSE);
467 if (mv_has_range (&dialog->mvl))
469 union value low, high;
472 mv_get_range (&dialog->mvl, &low.f, &high.f);
475 low_text = value_to_text__ (low, &dialog->format, dialog->encoding);
476 high_text = value_to_text__ (high, &dialog->format, dialog->encoding);
478 gtk_entry_set_text (GTK_ENTRY (dialog->low), low_text);
479 gtk_entry_set_text (GTK_ENTRY (dialog->high), high_text);
483 if (mv_has_value (&dialog->mvl))
486 text = value_to_text__ (*mv_get_value (&dialog->mvl, 0),
487 &dialog->format, dialog->encoding);
488 gtk_entry_set_text (GTK_ENTRY (dialog->discrete), text);
492 gtk_toggle_button_set_active (dialog->button_range, TRUE);
493 gtk_widget_set_sensitive (dialog->low, TRUE);
494 gtk_widget_set_sensitive (dialog->high, TRUE);
495 gtk_widget_set_sensitive (dialog->discrete, TRUE);
498 else if (mv_has_value (&dialog->mvl))
500 const int n = mv_n_values (&dialog->mvl);
502 for (i = 0 ; i < 3 ; ++i)
508 text = value_to_text__ (*mv_get_value (&dialog->mvl, i),
509 &dialog->format, dialog->encoding);
510 gtk_entry_set_text (GTK_ENTRY (dialog->mv[i]), text);
513 gtk_widget_set_sensitive (dialog->mv[i], TRUE);
515 gtk_toggle_button_set_active (dialog->button_discrete, TRUE);
517 else if (mv_is_empty (&dialog->mvl))
519 gtk_toggle_button_set_active (dialog->button_none, TRUE);
523 const struct missing_values *
524 psppire_missing_val_dialog_get_missing_values (
525 const PsppireMissingValDialog *dialog)