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);
41 psppire_val_chooser_get_type (void)
43 static GType psppire_val_chooser_type = 0;
45 if (!psppire_val_chooser_type)
47 static const GTypeInfo psppire_val_chooser_info =
49 sizeof (PsppireValChooserClass),
50 (GBaseInitFunc) (void (*)(void)) psppire_val_chooser_base_init,
51 (GBaseFinalizeFunc) (void (*)(void)) psppire_val_chooser_base_finalize,
52 (GClassInitFunc) (void (*)(void)) psppire_val_chooser_class_init,
53 (GClassFinalizeFunc) NULL,
55 sizeof (PsppireValChooser),
57 (GInstanceInitFunc) (void (*)(void)) psppire_val_chooser_init,
58 NULL /* value_table */
61 psppire_val_chooser_type =
62 g_type_register_static (GTK_TYPE_FRAME, "PsppireValChooser",
63 &psppire_val_chooser_info, 0);
66 return psppire_val_chooser_type;
71 psppire_val_chooser_finalize (GObject *object)
97 psppire_val_chooser_set_property (GObject *object,
102 PsppireValChooser *vr = PSPPIRE_VAL_CHOOSER (object);
108 gboolean x = g_value_get_boolean (value);
109 gtk_widget_set_visible (GTK_WIDGET (vr->rw[VC_ELSE].rb), x);
110 gtk_widget_set_visible (GTK_WIDGET (vr->rw[VC_ELSE].label), x);
114 vr->input_var_is_string = g_value_get_boolean (value);
115 gtk_widget_set_sensitive (GTK_WIDGET (vr->rw[VC_SYSMIS].rb), !vr->input_var_is_string);
116 gtk_widget_set_sensitive (GTK_WIDGET (vr->rw[VC_MISSING].rb), !vr->input_var_is_string);
117 gtk_widget_set_sensitive (GTK_WIDGET (vr->rw[VC_RANGE].rb), !vr->input_var_is_string);
118 gtk_widget_set_sensitive (GTK_WIDGET (vr->rw[VC_LOW_UP].rb), !vr->input_var_is_string);
119 gtk_widget_set_sensitive (GTK_WIDGET (vr->rw[VC_HIGH_DOWN].rb), !vr->input_var_is_string);
122 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
129 psppire_val_chooser_get_property (GObject *object,
134 PsppireValChooser *vr = PSPPIRE_VAL_CHOOSER (object);
141 gtk_widget_get_visible (GTK_WIDGET (vr->rw[VC_ELSE].rb));
142 g_value_set_boolean (value, x);
146 g_value_set_boolean (value, vr->input_var_is_string);
148 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
154 static GObjectClass * parent_class = NULL;
157 psppire_val_chooser_class_init (PsppireValChooserClass *class)
159 GObjectClass *object_class = G_OBJECT_CLASS (class);
160 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
162 GParamSpec *is_string_spec =
163 g_param_spec_boolean ("is-string",
165 "Should the value range be a string value",
169 GParamSpec *show_else_spec =
170 g_param_spec_boolean ("show-else",
172 "Should the \"All other values\" item be visible",
177 parent_class = g_type_class_peek_parent (class);
179 object_class->set_property = psppire_val_chooser_set_property;
180 object_class->get_property = psppire_val_chooser_get_property;
182 widget_class->realize = psppire_val_chooser_realize;
184 g_object_class_install_property (object_class,
188 g_object_class_install_property (object_class,
195 psppire_val_chooser_base_init (PsppireValChooserClass *class)
197 GObjectClass *object_class = G_OBJECT_CLASS (class);
199 object_class->finalize = psppire_val_chooser_finalize;
205 psppire_val_chooser_base_finalize (PsppireValChooserClass *class,
212 /* Set the focus of B to follow the sensitivity of A */
214 focus_follows_sensitivity (GtkWidget *a, GParamSpec *pspec, GtkWidget *b)
216 gboolean sens = gtk_widget_get_sensitive (a);
218 g_object_set (b, "has-focus", sens, NULL);
223 typedef GtkWidget *filler_f (struct layout *, struct range_widgets *);
224 typedef void set_f (PsppireValChooser *, struct old_value *, const struct range_widgets *);
235 static void simple_set (PsppireValChooser *vr, struct old_value *ov, const struct range_widgets *rw)
237 const gchar *text = gtk_entry_get_text (rw->e1);
239 if (vr->input_var_is_string)
241 ov->type = OV_STRING;
242 ov->v.s = g_strdup (text);
246 ov->type = OV_NUMERIC;
247 ov->v.v = g_strtod (text, 0);
251 static void lo_up_set (PsppireValChooser *vr, struct old_value *ov, const struct range_widgets *rw)
253 const gchar *text = gtk_entry_get_text (rw->e1);
255 ov->type = OV_LOW_UP;
256 ov->v.range[1] = g_strtod (text, 0);
260 static void hi_down_set (PsppireValChooser *vr, struct old_value *ov, const struct range_widgets *rw)
262 const gchar *text = gtk_entry_get_text (rw->e1);
264 ov->type = OV_HIGH_DOWN;
265 ov->v.range[0] = g_strtod (text, 0);
268 static void missing_set (PsppireValChooser *vr, struct old_value *ov, const struct range_widgets *l)
270 ov->type = OV_MISSING;
274 static void sysmis_set (PsppireValChooser *vr, struct old_value *ov, const struct range_widgets *l)
276 ov->type = OV_SYSMIS;
279 static void else_set (PsppireValChooser *vr, struct old_value *ov, const struct range_widgets *l)
285 static void range_set (PsppireValChooser *vr, struct old_value *ov, const struct range_widgets *rw)
287 const gchar *text = gtk_entry_get_text (rw->e1);
290 ov->v.range[0] = g_strtod (text, 0);
292 text = gtk_entry_get_text (rw->e2);
293 ov->v.range[1] = g_strtod (text, 0);
296 static GtkWidget * range_entry (struct layout *l, struct range_widgets *rw)
298 GtkWidget *vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 3);
299 GtkWidget *entrylo = gtk_entry_new ();
300 GtkWidget *label = gtk_label_new (_("through"));
301 GtkWidget *entryhi = gtk_entry_new ();
303 rw->e1 = GTK_ENTRY (entrylo);
304 rw->e2 = GTK_ENTRY (entryhi);
306 g_object_set (G_OBJECT (label),
307 "valign", GTK_ALIGN_CENTER,
308 "halign", GTK_ALIGN_START,
312 g_signal_connect (vbox, "notify::sensitive", G_CALLBACK (focus_follows_sensitivity), entrylo);
314 gtk_box_pack_start (GTK_BOX (vbox), entrylo, TRUE, TRUE, 0);
315 gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
316 gtk_box_pack_start (GTK_BOX (vbox), entryhi, TRUE, TRUE, 0);
320 static GtkWidget * simple_entry (struct layout *l, struct range_widgets *rw)
322 GtkWidget *entry = gtk_entry_new ();
324 rw->e1 = GTK_ENTRY (entry);
326 g_signal_connect (entry, "notify::sensitive", G_CALLBACK (focus_follows_sensitivity), entry);
331 static struct layout range_opt[n_VAL_CHOOSER_BUTTONS]=
333 {N_("_Value:"), simple_entry, simple_set },
334 {N_("_System Missing"), NULL, sysmis_set },
335 {N_("System _or User Missing"), NULL, missing_set},
336 {N_("_Range:"), range_entry, range_set },
337 {N_("Range, _LOWEST thru value"), simple_entry, lo_up_set },
338 {N_("Range, value thru _HIGHEST"), simple_entry, hi_down_set},
339 {N_("_All other values"), NULL, else_set }
343 psppire_val_chooser_init (PsppireValChooser *vr)
346 GtkWidget *grid = gtk_grid_new ();
347 GSList *group = NULL;
350 g_object_set (G_OBJECT (grid),
355 vr->input_var_is_string = FALSE;
357 for (i = 0; i < n_VAL_CHOOSER_BUTTONS; ++i)
359 struct layout *l = &range_opt[i];
360 vr->rw[i].label = GTK_LABEL (gtk_label_new (gettext (l->label)));
361 gtk_label_set_use_underline (vr->rw[i].label, TRUE);
362 vr->rw[i].rb = GTK_TOGGLE_BUTTON (gtk_radio_button_new (group));
363 gtk_label_set_mnemonic_widget (vr->rw[i].label, GTK_WIDGET (vr->rw[i].rb));
365 g_object_set (G_OBJECT (vr->rw[i].label),
366 "valign", GTK_ALIGN_CENTER,
367 "halign", GTK_ALIGN_START,
370 group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (vr->rw[i].rb));
372 /* Attach the buttons */
373 gtk_grid_attach (GTK_GRID (grid), GTK_WIDGET (vr->rw[i].rb),
376 gtk_widget_set_hexpand (GTK_WIDGET (vr->rw[i].rb), FALSE);
378 /* Attach the labels */
379 gtk_grid_attach (GTK_GRID (grid), GTK_WIDGET (vr->rw[i].label),
382 gtk_widget_set_hexpand (GTK_WIDGET (vr->rw[i].label), TRUE);
389 GtkWidget *fill = l->fill (l, &vr->rw[i]);
391 gtk_widget_set_sensitive (fill, FALSE);
393 gtk_grid_attach (GTK_GRID (grid), fill, 1, row, 1, 1);
395 gtk_widget_set_hexpand (fill, TRUE);
399 g_signal_connect (vr->rw[i].rb, "toggled", G_CALLBACK (set_sensitivity_from_toggle), fill);
403 gtk_frame_set_shadow_type (GTK_FRAME (vr), GTK_SHADOW_ETCHED_IN);
405 gtk_container_add (GTK_CONTAINER (vr), grid);
407 gtk_widget_show_all (grid);
412 psppire_val_chooser_new (void)
414 return GTK_WIDGET (g_object_new (psppire_val_chooser_get_type (), NULL));
420 psppire_val_chooser_realize (GtkWidget *w)
422 PsppireValChooser *vr = PSPPIRE_VAL_CHOOSER (w);
424 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(vr->rw[0].rb), TRUE);
425 gtk_toggle_button_toggled (GTK_TOGGLE_BUTTON (vr->rw[0].rb));
427 /* Chain up to the parent class */
428 GTK_WIDGET_CLASS (parent_class)->realize (w);
434 /* A boxed type representing a value, or a range of values which may
435 potentially be replaced by something */
438 static struct old_value *
439 old_value_copy (struct old_value *ov)
441 struct old_value *copy = g_memdup (ov, sizeof (*copy));
443 if (ov->type == OV_STRING)
444 copy->v.s = g_strdup (ov->v.s);
451 old_value_free (struct old_value *ov)
453 if (ov->type == OV_STRING)
459 old_value_to_string (const GValue *src, GValue *dest)
461 const struct old_value *ov = g_value_get_boxed (src);
467 gchar *text = g_strdup_printf ("%.*g", DBL_DIG + 1, ov->v.v);
468 g_value_set_string (dest, text);
473 g_value_set_string (dest, ov->v.s);
476 g_value_set_string (dest, "MISSING");
479 g_value_set_string (dest, "SYSMIS");
482 g_value_set_string (dest, "ELSE");
487 char en_dash[6] = {0,0,0,0,0,0};
489 g_unichar_to_utf8 (0x2013, en_dash);
491 text = g_strdup_printf ("%.*g %s %.*g",
492 DBL_DIG + 1, ov->v.range[0],
494 DBL_DIG + 1, ov->v.range[1]);
495 g_value_set_string (dest, text);
502 char en_dash[6] = {0,0,0,0,0,0};
504 g_unichar_to_utf8 (0x2013, en_dash);
506 text = g_strdup_printf ("LOWEST %s %.*g",
508 DBL_DIG + 1, ov->v.range[1]);
510 g_value_set_string (dest, text);
517 char en_dash[6] = {0,0,0,0,0,0};
519 g_unichar_to_utf8 (0x2013, en_dash);
521 text = g_strdup_printf ("%.*g %s HIGHEST",
522 DBL_DIG + 1, ov->v.range[0],
525 g_value_set_string (dest, text);
530 g_warning ("Invalid type in old recode value");
531 g_value_set_string (dest, "???");
537 old_value_get_type (void)
543 t = g_boxed_type_register_static ("psppire-recode-old-values",
544 (GBoxedCopyFunc) old_value_copy,
545 (GBoxedFreeFunc) old_value_free);
547 g_value_register_transform_func (t, G_TYPE_STRING,
548 old_value_to_string);
556 /* Generate a syntax fragment for NV and append it to STR */
558 old_value_append_syntax (struct string *str, const struct old_value *ov)
563 ds_put_c_format (str, "%.*g", DBL_DIG + 1, ov->v.v);
567 struct string ds = DS_EMPTY_INITIALIZER;
568 syntax_gen_string (&ds, ss_cstr (ov->v.s));
569 ds_put_cstr (str, ds_cstr (&ds));
574 ds_put_cstr (str, "MISSING");
577 ds_put_cstr (str, "SYSMIS");
580 ds_put_cstr (str, "ELSE");
583 ds_put_c_format (str, "%.*g THRU %.*g",
584 DBL_DIG + 1, ov->v.range[0],
585 DBL_DIG + 1, ov->v.range[1]);
588 ds_put_c_format (str, "LOWEST THRU %.*g",
589 DBL_DIG + 1, ov->v.range[1]);
592 ds_put_c_format (str, "%.*g THRU HIGHEST",
593 DBL_DIG + 1, ov->v.range[0]);
596 g_warning ("Invalid type in old recode value");
597 ds_put_cstr (str, "???");
604 /* Set OV according to the current state of VR */
606 psppire_val_chooser_get_status (PsppireValChooser *vr, struct old_value *ov)
610 for (i = 0; i < n_VAL_CHOOSER_BUTTONS; ++i)
612 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (vr->rw[i].rb)))
614 range_opt[i].set (vr, ov, &vr->rw[i]);
620 /* This might need to be changed to something less naive.
621 In particular, what happends with dates, etc?
624 num_to_string (gdouble x)
626 return g_strdup_printf ("%.*g", DBL_DIG + 1, x);
630 /* Set VR according to the value of OV */
632 psppire_val_chooser_set_status (PsppireValChooser *vr, const struct old_value *ov)
638 for (i = 0; i < n_VAL_CHOOSER_BUTTONS; ++i)
641 gtk_entry_set_text (vr->rw[i].e1, "");
644 gtk_entry_set_text (vr->rw[i].e2, "");
650 gtk_toggle_button_set_active (vr->rw[0].rb, TRUE);
651 gtk_entry_set_text (vr->rw[0].e1, ov->v.s);
657 gtk_toggle_button_set_active (vr->rw[0].rb, TRUE);
659 str = num_to_string (ov->v.v);
661 gtk_entry_set_text (vr->rw[0].e1, str);
667 gtk_toggle_button_set_active (vr->rw[VC_SYSMIS].rb, TRUE);
671 gtk_toggle_button_set_active (vr->rw[VC_MISSING].rb, TRUE);
676 gchar *str = num_to_string (ov->v.range[0]);
677 gtk_toggle_button_set_active (vr->rw[VC_RANGE].rb, TRUE);
678 gtk_entry_set_text (vr->rw[VC_RANGE].e1, str);
682 str = num_to_string (ov->v.range[1]);
683 gtk_entry_set_text (vr->rw[VC_RANGE].e2, str);
690 gchar *str = num_to_string (ov->v.range[1]);
692 gtk_toggle_button_set_active (vr->rw[VC_LOW_UP].rb, TRUE);
694 gtk_entry_set_text (vr->rw[VC_LOW_UP].e1, str);
703 gchar *str = num_to_string (ov->v.range[0]);
705 gtk_toggle_button_set_active (vr->rw[VC_HIGH_DOWN].rb, TRUE);
707 gtk_entry_set_text (vr->rw[VC_HIGH_DOWN].e1, str);
714 gtk_toggle_button_set_active (vr->rw[VC_ELSE].rb, TRUE);
718 g_warning ("Unknown old value type");