1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2011, 2014 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/>. */
21 #include "dialog-common.h"
22 #include "psppire-val-chooser.h"
24 #include "libpspp/str.h"
27 #include "ui/syntax-gen.h"
30 #define _(msgid) gettext (msgid)
31 #define N_(msgid) msgid
33 static void psppire_val_chooser_base_finalize (PsppireValChooserClass *, gpointer);
34 static void psppire_val_chooser_base_init (PsppireValChooserClass *class);
35 static void psppire_val_chooser_class_init (PsppireValChooserClass *class);
36 static void psppire_val_chooser_init (PsppireValChooser *vc);
38 static void psppire_val_chooser_realize (GtkWidget *w);
43 psppire_val_chooser_get_type (void)
45 static GType psppire_val_chooser_type = 0;
47 if (!psppire_val_chooser_type)
49 static const GTypeInfo psppire_val_chooser_info =
51 sizeof (PsppireValChooserClass),
52 (GBaseInitFunc) psppire_val_chooser_base_init,
53 (GBaseFinalizeFunc) psppire_val_chooser_base_finalize,
54 (GClassInitFunc)psppire_val_chooser_class_init,
55 (GClassFinalizeFunc) NULL,
57 sizeof (PsppireValChooser),
59 (GInstanceInitFunc) psppire_val_chooser_init,
62 psppire_val_chooser_type =
63 g_type_register_static (GTK_TYPE_FRAME, "PsppireValChooser",
64 &psppire_val_chooser_info, 0);
67 return psppire_val_chooser_type;
72 psppire_val_chooser_finalize (GObject *object)
98 psppire_val_chooser_set_property (GObject *object,
103 PsppireValChooser *vr = PSPPIRE_VAL_CHOOSER (object);
109 gboolean x = g_value_get_boolean (value);
110 gtk_widget_set_visible (GTK_WIDGET (vr->rw[VC_ELSE].rb), x);
111 gtk_widget_set_visible (GTK_WIDGET (vr->rw[VC_ELSE].label), x);
115 vr->input_var_is_string = g_value_get_boolean (value);
116 gtk_widget_set_sensitive (GTK_WIDGET (vr->rw[VC_SYSMIS].rb), !vr->input_var_is_string);
117 gtk_widget_set_sensitive (GTK_WIDGET (vr->rw[VC_MISSING].rb), !vr->input_var_is_string);
118 gtk_widget_set_sensitive (GTK_WIDGET (vr->rw[VC_RANGE].rb), !vr->input_var_is_string);
119 gtk_widget_set_sensitive (GTK_WIDGET (vr->rw[VC_LOW_UP].rb), !vr->input_var_is_string);
120 gtk_widget_set_sensitive (GTK_WIDGET (vr->rw[VC_HIGH_DOWN].rb), !vr->input_var_is_string);
123 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
130 psppire_val_chooser_get_property (GObject *object,
135 PsppireValChooser *vr = PSPPIRE_VAL_CHOOSER (object);
142 gtk_widget_get_visible (GTK_WIDGET (vr->rw[VC_ELSE].rb));
143 g_value_set_boolean (value, x);
147 g_value_set_boolean (value, vr->input_var_is_string);
149 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
155 static GObjectClass * parent_class = NULL;
158 psppire_val_chooser_class_init (PsppireValChooserClass *class)
160 GObjectClass *object_class = G_OBJECT_CLASS (class);
161 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
163 GParamSpec *is_string_spec =
164 g_param_spec_boolean ("is-string",
166 "Should the value range be a string value",
170 GParamSpec *show_else_spec =
171 g_param_spec_boolean ("show-else",
173 "Should the \"All other values\" item be visible",
178 parent_class = g_type_class_peek_parent (class);
180 object_class->set_property = psppire_val_chooser_set_property;
181 object_class->get_property = psppire_val_chooser_get_property;
183 widget_class->realize = psppire_val_chooser_realize;
185 g_object_class_install_property (object_class,
189 g_object_class_install_property (object_class,
196 psppire_val_chooser_base_init (PsppireValChooserClass *class)
198 GObjectClass *object_class = G_OBJECT_CLASS (class);
200 object_class->finalize = psppire_val_chooser_finalize;
206 psppire_val_chooser_base_finalize (PsppireValChooserClass *class,
213 /* Set the focus of B to follow the sensitivity of A */
215 focus_follows_sensitivity (GtkWidget *a, GParamSpec *pspec, GtkWidget *b)
217 gboolean sens = gtk_widget_get_sensitive (a);
219 g_object_set (b, "has-focus", sens, NULL);
224 typedef GtkWidget *filler_f (struct layout *, struct range_widgets *);
225 typedef void set_f (PsppireValChooser *, struct old_value *, const struct range_widgets *);
236 static void simple_set (PsppireValChooser *vr, struct old_value *ov, const struct range_widgets *rw)
238 const gchar *text = gtk_entry_get_text (rw->e1);
240 if (vr->input_var_is_string)
242 ov->type = OV_STRING;
243 ov->v.s = g_strdup (text);
247 ov->type = OV_NUMERIC;
248 ov->v.v = g_strtod (text, 0);
252 static void lo_up_set (PsppireValChooser *vr, struct old_value *ov, const struct range_widgets *rw)
254 const gchar *text = gtk_entry_get_text (rw->e1);
256 ov->type = OV_LOW_UP;
257 ov->v.range[1] = g_strtod (text, 0);
261 static void hi_down_set (PsppireValChooser *vr, struct old_value *ov, const struct range_widgets *rw)
263 const gchar *text = gtk_entry_get_text (rw->e1);
265 ov->type = OV_HIGH_DOWN;
266 ov->v.range[0] = g_strtod (text, 0);
269 static void missing_set (PsppireValChooser *vr, struct old_value *ov, const struct range_widgets *l)
271 ov->type = OV_MISSING;
275 static void sysmis_set (PsppireValChooser *vr, struct old_value *ov, const struct range_widgets *l)
277 ov->type = OV_SYSMIS;
280 static void else_set (PsppireValChooser *vr, struct old_value *ov, const struct range_widgets *l)
286 static void range_set (PsppireValChooser *vr, struct old_value *ov, const struct range_widgets *rw)
288 const gchar *text = gtk_entry_get_text (rw->e1);
291 ov->v.range[0] = g_strtod (text, 0);
293 text = gtk_entry_get_text (rw->e2);
294 ov->v.range[1] = g_strtod (text, 0);
297 static GtkWidget * range_entry (struct layout *l, struct range_widgets *rw)
299 GtkWidget *vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 3);
300 GtkWidget *entrylo = gtk_entry_new ();
301 GtkWidget *label = gtk_label_new (_("through"));
302 GtkWidget *entryhi = gtk_entry_new ();
304 rw->e1 = GTK_ENTRY (entrylo);
305 rw->e2 = GTK_ENTRY (entryhi);
307 g_object_set (G_OBJECT (label),
308 "valign", GTK_ALIGN_CENTER,
309 "halign", GTK_ALIGN_START,
313 g_signal_connect (vbox, "notify::sensitive", G_CALLBACK (focus_follows_sensitivity), entrylo);
315 gtk_box_pack_start (GTK_BOX (vbox), entrylo, TRUE, TRUE, 0);
316 gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
317 gtk_box_pack_start (GTK_BOX (vbox), entryhi, TRUE, TRUE, 0);
321 static GtkWidget * simple_entry (struct layout *l, struct range_widgets *rw)
323 GtkWidget *entry = gtk_entry_new ();
325 rw->e1 = GTK_ENTRY (entry);
327 g_signal_connect (entry, "notify::sensitive", G_CALLBACK (focus_follows_sensitivity), entry);
332 static struct layout range_opt[n_VAL_CHOOSER_BUTTONS]=
334 {N_("_Value:"), simple_entry, simple_set },
335 {N_("_System Missing"), NULL, sysmis_set },
336 {N_("System _or User Missing"), NULL, missing_set},
337 {N_("_Range:"), range_entry, range_set },
338 {N_("Range, _LOWEST thru value"), simple_entry, lo_up_set },
339 {N_("Range, value thru _HIGHEST"), simple_entry, hi_down_set},
340 {N_("_All other values"), NULL, else_set }
344 psppire_val_chooser_init (PsppireValChooser *vr)
347 GtkWidget *grid = gtk_grid_new ();
348 GSList *group = NULL;
351 g_object_set (G_OBJECT (grid),
356 vr->input_var_is_string = FALSE;
358 for (i = 0; i < n_VAL_CHOOSER_BUTTONS; ++i)
360 struct layout *l = &range_opt[i];
361 vr->rw[i].label = GTK_LABEL (gtk_label_new (gettext (l->label)));
362 gtk_label_set_use_underline (vr->rw[i].label, TRUE);
363 vr->rw[i].rb = GTK_TOGGLE_BUTTON (gtk_radio_button_new (group));
364 gtk_label_set_mnemonic_widget (vr->rw[i].label, GTK_WIDGET (vr->rw[i].rb));
366 g_object_set (G_OBJECT (vr->rw[i].label),
367 "valign", GTK_ALIGN_CENTER,
368 "halign", GTK_ALIGN_START,
371 group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (vr->rw[i].rb));
373 /* Attach the buttons */
374 gtk_grid_attach (GTK_GRID (grid), GTK_WIDGET (vr->rw[i].rb),
377 gtk_widget_set_hexpand (GTK_WIDGET (vr->rw[i].rb), FALSE);
379 /* Attach the labels */
380 gtk_grid_attach (GTK_GRID (grid), GTK_WIDGET (vr->rw[i].label),
383 gtk_widget_set_hexpand (GTK_WIDGET (vr->rw[i].label), TRUE);
390 GtkWidget *fill = l->fill (l, &vr->rw[i]);
392 gtk_widget_set_sensitive (fill, FALSE);
394 gtk_grid_attach (GTK_GRID (grid), fill, 1, row, 1, 1);
396 gtk_widget_set_hexpand (fill, TRUE);
400 g_signal_connect (vr->rw[i].rb, "toggled", G_CALLBACK (set_sensitivity_from_toggle), fill);
404 gtk_frame_set_shadow_type (GTK_FRAME (vr), GTK_SHADOW_ETCHED_IN);
406 gtk_container_add (GTK_CONTAINER (vr), grid);
408 gtk_widget_show_all (grid);
413 psppire_val_chooser_new (void)
415 return GTK_WIDGET (g_object_new (psppire_val_chooser_get_type (), NULL));
421 psppire_val_chooser_realize (GtkWidget *w)
423 PsppireValChooser *vr = PSPPIRE_VAL_CHOOSER (w);
425 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(vr->rw[0].rb), TRUE);
426 gtk_toggle_button_toggled (GTK_TOGGLE_BUTTON (vr->rw[0].rb));
428 /* Chain up to the parent class */
429 GTK_WIDGET_CLASS (parent_class)->realize (w);
435 /* A boxed type representing a value, or a range of values which may
436 potentially be replaced by something */
439 static struct old_value *
440 old_value_copy (struct old_value *ov)
442 struct old_value *copy = g_memdup (ov, sizeof (*copy));
444 if (ov->type == OV_STRING)
445 copy->v.s = g_strdup (ov->v.s);
452 old_value_free (struct old_value *ov)
454 if (ov->type == OV_STRING)
460 old_value_to_string (const GValue *src, GValue *dest)
462 const struct old_value *ov = g_value_get_boxed (src);
468 gchar *text = g_strdup_printf ("%.*g", DBL_DIG + 1, ov->v.v);
469 g_value_set_string (dest, text);
474 g_value_set_string (dest, ov->v.s);
477 g_value_set_string (dest, "MISSING");
480 g_value_set_string (dest, "SYSMIS");
483 g_value_set_string (dest, "ELSE");
488 char en_dash[6] = {0,0,0,0,0,0};
490 g_unichar_to_utf8 (0x2013, en_dash);
492 text = g_strdup_printf ("%.*g %s %.*g",
493 DBL_DIG + 1, ov->v.range[0],
495 DBL_DIG + 1, ov->v.range[1]);
496 g_value_set_string (dest, text);
503 char en_dash[6] = {0,0,0,0,0,0};
505 g_unichar_to_utf8 (0x2013, en_dash);
507 text = g_strdup_printf ("LOWEST %s %.*g",
509 DBL_DIG + 1, ov->v.range[1]);
511 g_value_set_string (dest, text);
518 char en_dash[6] = {0,0,0,0,0,0};
520 g_unichar_to_utf8 (0x2013, en_dash);
522 text = g_strdup_printf ("%.*g %s HIGHEST",
523 DBL_DIG + 1, ov->v.range[0],
526 g_value_set_string (dest, text);
531 g_warning ("Invalid type in old recode value");
532 g_value_set_string (dest, "???");
538 old_value_get_type (void)
544 t = g_boxed_type_register_static ("psppire-recode-old-values",
545 (GBoxedCopyFunc) old_value_copy,
546 (GBoxedFreeFunc) old_value_free);
548 g_value_register_transform_func (t, G_TYPE_STRING,
549 old_value_to_string);
557 /* Generate a syntax fragment for NV and append it to STR */
559 old_value_append_syntax (struct string *str, const struct old_value *ov)
564 ds_put_c_format (str, "%.*g", DBL_DIG + 1, ov->v.v);
568 struct string ds = DS_EMPTY_INITIALIZER;
569 syntax_gen_string (&ds, ss_cstr (ov->v.s));
570 ds_put_cstr (str, ds_cstr (&ds));
575 ds_put_cstr (str, "MISSING");
578 ds_put_cstr (str, "SYSMIS");
581 ds_put_cstr (str, "ELSE");
584 ds_put_c_format (str, "%.*g THRU %.*g",
585 DBL_DIG + 1, ov->v.range[0],
586 DBL_DIG + 1, ov->v.range[1]);
589 ds_put_c_format (str, "LOWEST THRU %.*g",
590 DBL_DIG + 1, ov->v.range[1]);
593 ds_put_c_format (str, "%.*g THRU HIGHEST",
594 DBL_DIG + 1, ov->v.range[0]);
597 g_warning ("Invalid type in old recode value");
598 ds_put_cstr (str, "???");
605 /* Set OV according to the current state of VR */
607 psppire_val_chooser_get_status (PsppireValChooser *vr, struct old_value *ov)
611 for (i = 0; i < n_VAL_CHOOSER_BUTTONS; ++i)
613 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (vr->rw[i].rb)))
615 range_opt[i].set (vr, ov, &vr->rw[i]);
621 /* This might need to be changed to something less naive.
622 In particular, what happends with dates, etc?
625 num_to_string (gdouble x)
627 return g_strdup_printf ("%.*g", DBL_DIG + 1, x);
631 /* Set VR according to the value of OV */
633 psppire_val_chooser_set_status (PsppireValChooser *vr, const struct old_value *ov)
639 for (i = 0; i < n_VAL_CHOOSER_BUTTONS; ++i)
642 gtk_entry_set_text (vr->rw[i].e1, "");
645 gtk_entry_set_text (vr->rw[i].e2, "");
651 gtk_toggle_button_set_active (vr->rw[0].rb, TRUE);
652 gtk_entry_set_text (vr->rw[0].e1, ov->v.s);
658 gtk_toggle_button_set_active (vr->rw[0].rb, TRUE);
660 str = num_to_string (ov->v.v);
662 gtk_entry_set_text (vr->rw[0].e1, str);
668 gtk_toggle_button_set_active (vr->rw[VC_SYSMIS].rb, TRUE);
672 gtk_toggle_button_set_active (vr->rw[VC_MISSING].rb, TRUE);
677 gchar *str = num_to_string (ov->v.range[0]);
678 gtk_toggle_button_set_active (vr->rw[VC_RANGE].rb, TRUE);
679 gtk_entry_set_text (vr->rw[VC_RANGE].e1, str);
683 str = num_to_string (ov->v.range[1]);
684 gtk_entry_set_text (vr->rw[VC_RANGE].e2, str);
691 gchar *str = num_to_string (ov->v.range[1]);
693 gtk_toggle_button_set_active (vr->rw[VC_LOW_UP].rb, TRUE);
695 gtk_entry_set_text (vr->rw[VC_LOW_UP].e1, str);
704 gchar *str = num_to_string (ov->v.range[0]);
706 gtk_toggle_button_set_active (vr->rw[VC_HIGH_DOWN].rb, TRUE);
708 gtk_entry_set_text (vr->rw[VC_HIGH_DOWN].e1, str);
715 gtk_toggle_button_set_active (vr->rw[VC_ELSE].rb, TRUE);
719 g_warning ("Unknown old value type");