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", NULL);
380 content_area = GTK_CONTAINER (PSPPIRE_DIALOG (dialog));
381 xml = builder_new ("missing-val-dialog.ui");
382 gtk_container_add (GTK_CONTAINER (content_area),
383 get_widget_assert (xml, "missing-values-dialog"));
385 dialog->mv[0] = get_widget_assert (xml, "mv0");
386 dialog->mv[1] = get_widget_assert (xml, "mv1");
387 dialog->mv[2] = get_widget_assert (xml, "mv2");
389 dialog->low = get_widget_assert (xml, "mv-low");
390 dialog->high = get_widget_assert (xml, "mv-high");
391 dialog->discrete = get_widget_assert (xml, "mv-discrete");
394 dialog->button_none =
395 GTK_TOGGLE_BUTTON (get_widget_assert (xml, "no_missing"));
397 dialog->button_discrete =
398 GTK_TOGGLE_BUTTON (get_widget_assert (xml, "discrete_missing"));
400 dialog->button_range =
401 GTK_TOGGLE_BUTTON (get_widget_assert (xml, "range_missing"));
403 psppire_dialog_set_accept_predicate (PSPPIRE_DIALOG (dialog),
404 missing_val_dialog_acceptable,
407 g_signal_connect (dialog->button_discrete, "toggled",
408 G_CALLBACK (discrete), dialog);
410 g_signal_connect (dialog->button_range, "toggled",
411 G_CALLBACK (range), dialog);
413 g_object_unref (xml);
419 psppire_missing_val_dialog_set_variable (PsppireMissingValDialog *dialog,
420 const struct variable *var)
422 enum val_type var_type;
425 mv_destroy (&dialog->mvl);
426 g_free (dialog->encoding);
430 const struct missing_values *vmv = var_get_missing_values (var);
431 if (mv_is_empty(vmv))
432 mv_init (&dialog->mvl, var_get_width(var));
434 mv_copy (&dialog->mvl, vmv);
435 dialog->encoding = g_strdup (var_get_encoding (var));
436 dialog->format = *var_get_print_format (var);
440 mv_init (&dialog->mvl, 0);
441 dialog->encoding = NULL;
442 dialog->format = F_8_0;
445 /* Blank all entry boxes and make them insensitive */
446 gtk_entry_set_text (GTK_ENTRY (dialog->low), "");
447 gtk_entry_set_text (GTK_ENTRY (dialog->high), "");
448 gtk_entry_set_text (GTK_ENTRY (dialog->discrete), "");
449 gtk_widget_set_sensitive (dialog->low, FALSE);
450 gtk_widget_set_sensitive (dialog->high, FALSE);
451 gtk_widget_set_sensitive (dialog->discrete, FALSE);
453 var_type = val_type_from_width (fmt_var_width (&dialog->format));
454 gtk_widget_set_sensitive (GTK_WIDGET (dialog->button_range),
455 var_type == VAL_NUMERIC);
460 for (i = 0 ; i < 3 ; ++i)
462 gtk_entry_set_text (GTK_ENTRY (dialog->mv[i]), "");
463 gtk_widget_set_sensitive (dialog->mv[i], FALSE);
466 if (mv_has_range (&dialog->mvl))
468 union value low, high;
471 mv_get_range (&dialog->mvl, &low.f, &high.f);
474 low_text = value_to_text__ (low, &dialog->format, dialog->encoding);
475 high_text = value_to_text__ (high, &dialog->format, dialog->encoding);
477 gtk_entry_set_text (GTK_ENTRY (dialog->low), low_text);
478 gtk_entry_set_text (GTK_ENTRY (dialog->high), high_text);
482 if (mv_has_value (&dialog->mvl))
485 text = value_to_text__ (*mv_get_value (&dialog->mvl, 0),
486 &dialog->format, dialog->encoding);
487 gtk_entry_set_text (GTK_ENTRY (dialog->discrete), text);
491 gtk_toggle_button_set_active (dialog->button_range, TRUE);
492 gtk_widget_set_sensitive (dialog->low, TRUE);
493 gtk_widget_set_sensitive (dialog->high, TRUE);
494 gtk_widget_set_sensitive (dialog->discrete, TRUE);
497 else if (mv_has_value (&dialog->mvl))
499 const int n = mv_n_values (&dialog->mvl);
501 for (i = 0 ; i < 3 ; ++i)
507 text = value_to_text__ (*mv_get_value (&dialog->mvl, i),
508 &dialog->format, dialog->encoding);
509 gtk_entry_set_text (GTK_ENTRY (dialog->mv[i]), text);
512 gtk_widget_set_sensitive (dialog->mv[i], TRUE);
514 gtk_toggle_button_set_active (dialog->button_discrete, TRUE);
516 else if (mv_is_empty (&dialog->mvl))
518 gtk_toggle_button_set_active (dialog->button_none, TRUE);
522 const struct missing_values *
523 psppire_missing_val_dialog_get_missing_values (
524 const PsppireMissingValDialog *dialog)