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 settings_get_fmt_settings (), vp, var_width,
208 err_dialog (error_txt, GTK_WINDOW (dialog));
214 if (mv_is_acceptable (vp, var_width))
218 err_dialog (_("The maximum length of a missing value"
219 " for a string variable is 8 in UTF-8."),
220 GTK_WINDOW (dialog));
225 value_destroy (vp, var_width);
229 /* Acceptability predicate for PsppireMissingValDialog.
231 This function is also the only place that dialog->mvl gets updated. */
233 missing_val_dialog_acceptable (gpointer data)
235 PsppireMissingValDialog *dialog = data;
236 int var_width = fmt_var_width (&dialog->format);
238 if (gtk_toggle_button_get_active (dialog->button_discrete))
243 mv_clear(&dialog->mvl);
244 for(i = 0 ; i < 3 ; ++i)
247 g_strdup (gtk_entry_get_text (GTK_ENTRY (dialog->mv[i])));
250 if (!text || strlen (g_strstrip (text)) == 0)
256 if (!try_missing_value (dialog, text, &v))
259 gtk_widget_grab_focus (dialog->mv[i]);
262 mv_add_value (&dialog->mvl, &v);
265 value_destroy (&v, var_width);
269 err_dialog (_("At least one value must be specified"),
270 GTK_WINDOW (dialog));
271 gtk_widget_grab_focus (dialog->mv[0]);
276 if (gtk_toggle_button_get_active (dialog->button_range))
278 gchar *discrete_text;
279 union value low_val ;
280 union value high_val;
281 const gchar *low_text = gtk_entry_get_text (GTK_ENTRY (dialog->low));
282 const gchar *high_text = gtk_entry_get_text (GTK_ENTRY (dialog->high));
284 assert (var_width == 0); /* Ranges are only for numeric variables */
286 if (!try_missing_value(dialog, low_text, &low_val))
288 gtk_widget_grab_focus (dialog->low);
291 if (!try_missing_value (dialog, high_text, &high_val))
293 gtk_widget_grab_focus (dialog->high);
294 value_destroy (&low_val, var_width);
297 if (low_val.f > high_val.f)
299 err_dialog (_("Incorrect range specification"),
300 GTK_WINDOW (dialog));
301 value_destroy (&low_val, var_width);
302 value_destroy (&high_val, var_width);
303 gtk_widget_grab_focus (dialog->low);
306 mv_clear (&dialog->mvl);
307 mv_add_range (&dialog->mvl, low_val.f, high_val.f);
308 value_destroy (&low_val, var_width);
309 value_destroy (&high_val, var_width);
311 discrete_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (dialog->discrete)));
313 if (discrete_text && strlen (g_strstrip (discrete_text)) > 0)
315 union value discrete_val;
316 if (!try_missing_value (dialog, discrete_text, &discrete_val))
318 g_free (discrete_text);
319 gtk_widget_grab_focus (dialog->discrete);
322 mv_add_value (&dialog->mvl, &discrete_val);
323 value_destroy (&discrete_val, var_width);
325 g_free (discrete_text);
328 if (gtk_toggle_button_get_active (dialog->button_none))
329 mv_clear (&dialog->mvl);
335 /* Callback which occurs when the 'discrete' radiobutton is toggled */
337 discrete (GtkToggleButton *button, gpointer data)
340 PsppireMissingValDialog *dialog = data;
342 for (i = 0 ; i < 3 ; ++i)
344 gtk_widget_set_sensitive (dialog->mv[i],
345 gtk_toggle_button_get_active (button));
349 /* Callback which occurs when the 'range' radiobutton is toggled */
351 range (GtkToggleButton *button, gpointer data)
353 PsppireMissingValDialog *dialog = data;
355 const gboolean active = gtk_toggle_button_get_active (button);
357 gtk_widget_set_sensitive (dialog->low, active);
358 gtk_widget_set_sensitive (dialog->high, active);
359 gtk_widget_set_sensitive (dialog->discrete, active);
364 /* Shows the dialog box and sets default values */
366 psppire_missing_val_dialog_constructor (GType type,
368 GObjectConstructParam *properties)
370 PsppireMissingValDialog *dialog;
371 GtkContainer *content_area;
375 obj = G_OBJECT_CLASS (psppire_missing_val_dialog_parent_class)->constructor (
376 type, n_properties, properties);
377 dialog = PSPPIRE_MISSING_VAL_DIALOG (obj);
379 g_object_set (dialog, "help-page", "Missing-Observations",
380 "title", _("Missing Values"), NULL);
382 content_area = GTK_CONTAINER (PSPPIRE_DIALOG (dialog));
383 xml = builder_new ("missing-val-dialog.ui");
384 gtk_container_add (GTK_CONTAINER (content_area),
385 get_widget_assert (xml, "missing-values-dialog"));
387 dialog->mv[0] = get_widget_assert (xml, "mv0");
388 dialog->mv[1] = get_widget_assert (xml, "mv1");
389 dialog->mv[2] = get_widget_assert (xml, "mv2");
391 dialog->low = get_widget_assert (xml, "mv-low");
392 dialog->high = get_widget_assert (xml, "mv-high");
393 dialog->discrete = get_widget_assert (xml, "mv-discrete");
396 dialog->button_none =
397 GTK_TOGGLE_BUTTON (get_widget_assert (xml, "no_missing"));
399 dialog->button_discrete =
400 GTK_TOGGLE_BUTTON (get_widget_assert (xml, "discrete_missing"));
402 dialog->button_range =
403 GTK_TOGGLE_BUTTON (get_widget_assert (xml, "range_missing"));
405 psppire_dialog_set_accept_predicate (PSPPIRE_DIALOG (dialog),
406 missing_val_dialog_acceptable,
409 g_signal_connect (dialog->button_discrete, "toggled",
410 G_CALLBACK (discrete), dialog);
412 g_signal_connect (dialog->button_range, "toggled",
413 G_CALLBACK (range), dialog);
415 g_object_unref (xml);
421 psppire_missing_val_dialog_set_variable (PsppireMissingValDialog *dialog,
422 const struct variable *var)
424 enum val_type var_type;
427 mv_destroy (&dialog->mvl);
428 g_free (dialog->encoding);
432 const struct missing_values *vmv = var_get_missing_values (var);
433 if (mv_is_empty(vmv))
434 mv_init (&dialog->mvl, var_get_width(var));
436 mv_copy (&dialog->mvl, vmv);
437 dialog->encoding = g_strdup (var_get_encoding (var));
438 dialog->format = *var_get_print_format (var);
442 mv_init (&dialog->mvl, 0);
443 dialog->encoding = NULL;
444 dialog->format = F_8_0;
447 /* Blank all entry boxes and make them insensitive */
448 gtk_entry_set_text (GTK_ENTRY (dialog->low), "");
449 gtk_entry_set_text (GTK_ENTRY (dialog->high), "");
450 gtk_entry_set_text (GTK_ENTRY (dialog->discrete), "");
451 gtk_widget_set_sensitive (dialog->low, FALSE);
452 gtk_widget_set_sensitive (dialog->high, FALSE);
453 gtk_widget_set_sensitive (dialog->discrete, FALSE);
455 var_type = val_type_from_width (fmt_var_width (&dialog->format));
456 gtk_widget_set_sensitive (GTK_WIDGET (dialog->button_range),
457 var_type == VAL_NUMERIC);
462 for (i = 0 ; i < 3 ; ++i)
464 gtk_entry_set_text (GTK_ENTRY (dialog->mv[i]), "");
465 gtk_widget_set_sensitive (dialog->mv[i], FALSE);
468 if (mv_has_range (&dialog->mvl))
470 union value low, high;
473 mv_get_range (&dialog->mvl, &low.f, &high.f);
476 low_text = value_to_text__ (low, &dialog->format, dialog->encoding);
477 high_text = value_to_text__ (high, &dialog->format, dialog->encoding);
479 gtk_entry_set_text (GTK_ENTRY (dialog->low), low_text);
480 gtk_entry_set_text (GTK_ENTRY (dialog->high), high_text);
484 if (mv_has_value (&dialog->mvl))
487 text = value_to_text__ (*mv_get_value (&dialog->mvl, 0),
488 &dialog->format, dialog->encoding);
489 gtk_entry_set_text (GTK_ENTRY (dialog->discrete), text);
493 gtk_toggle_button_set_active (dialog->button_range, TRUE);
494 gtk_widget_set_sensitive (dialog->low, TRUE);
495 gtk_widget_set_sensitive (dialog->high, TRUE);
496 gtk_widget_set_sensitive (dialog->discrete, TRUE);
499 else if (mv_has_value (&dialog->mvl))
501 const int n = mv_n_values (&dialog->mvl);
503 for (i = 0 ; i < 3 ; ++i)
509 text = value_to_text__ (*mv_get_value (&dialog->mvl, i),
510 &dialog->format, dialog->encoding);
511 gtk_entry_set_text (GTK_ENTRY (dialog->mv[i]), text);
514 gtk_widget_set_sensitive (dialog->mv[i], TRUE);
516 gtk_toggle_button_set_active (dialog->button_discrete, TRUE);
518 else if (mv_is_empty (&dialog->mvl))
520 gtk_toggle_button_set_active (dialog->button_none, TRUE);
524 const struct missing_values *
525 psppire_missing_val_dialog_get_missing_values (
526 const PsppireMissingValDialog *dialog)