1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2011 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/>. */
20 #include "psppire-val-chooser.h"
22 #include "libpspp/str.h"
25 #include "ui/syntax-gen.h"
28 #define _(msgid) gettext (msgid)
29 #define N_(msgid) msgid
31 static void psppire_val_chooser_base_finalize (PsppireValChooserClass *, gpointer);
32 static void psppire_val_chooser_base_init (PsppireValChooserClass *class);
33 static void psppire_val_chooser_class_init (PsppireValChooserClass *class);
34 static void psppire_val_chooser_init (PsppireValChooser *vc);
36 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) psppire_val_chooser_base_init,
51 (GBaseFinalizeFunc) psppire_val_chooser_base_finalize,
52 (GClassInitFunc)psppire_val_chooser_class_init,
53 (GClassFinalizeFunc) NULL,
55 sizeof (PsppireValChooser),
57 (GInstanceInitFunc) psppire_val_chooser_init,
60 psppire_val_chooser_type =
61 g_type_register_static (GTK_TYPE_FRAME, "PsppireValChooser",
62 &psppire_val_chooser_info, 0);
65 return psppire_val_chooser_type;
70 psppire_val_chooser_finalize (GObject *object)
84 psppire_val_chooser_set_property (GObject *object,
89 PsppireValChooser *vr = PSPPIRE_VAL_CHOOSER (object);
94 vr->input_var_is_string = g_value_get_boolean (value);
96 gtk_widget_set_sensitive (GTK_WIDGET (vr->rw[1].rb), !vr->input_var_is_string);
97 gtk_widget_set_sensitive (GTK_WIDGET (vr->rw[2].rb), !vr->input_var_is_string);
98 gtk_widget_set_sensitive (GTK_WIDGET (vr->rw[3].rb), !vr->input_var_is_string);
99 gtk_widget_set_sensitive (GTK_WIDGET (vr->rw[4].rb), !vr->input_var_is_string);
100 gtk_widget_set_sensitive (GTK_WIDGET (vr->rw[5].rb), !vr->input_var_is_string);
103 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
110 psppire_val_chooser_get_property (GObject *object,
115 PsppireValChooser *vr = PSPPIRE_VAL_CHOOSER (object);
120 g_value_set_boolean (value, vr->input_var_is_string);
122 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
128 static GObjectClass * parent_class = NULL;
131 psppire_val_chooser_class_init (PsppireValChooserClass *class)
133 GObjectClass *object_class = G_OBJECT_CLASS (class);
134 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
136 GParamSpec *is_string_spec =
137 g_param_spec_boolean ("is-string",
139 "Should the value range be a string value",
143 parent_class = g_type_class_peek_parent (class);
145 object_class->set_property = psppire_val_chooser_set_property;
146 object_class->get_property = psppire_val_chooser_get_property;
148 widget_class->realize = psppire_val_chooser_realize;
150 g_object_class_install_property (object_class,
157 psppire_val_chooser_base_init (PsppireValChooserClass *class)
159 GObjectClass *object_class = G_OBJECT_CLASS (class);
161 object_class->finalize = psppire_val_chooser_finalize;
167 psppire_val_chooser_base_finalize (PsppireValChooserClass *class,
174 /* Set the focus of B to follow the sensitivity of A */
176 focus_follows_sensitivity (GtkWidget *a, GParamSpec *pspec, GtkWidget *b)
178 gboolean sens = gtk_widget_get_sensitive (a);
180 g_object_set (b, "has-focus", sens, NULL);
185 typedef GtkWidget *filler_f (struct layout *, struct range_widgets *);
186 typedef void set_f (PsppireValChooser *, struct old_value *, const struct range_widgets *);
197 static void simple_set (PsppireValChooser *vr, struct old_value *ov, const struct range_widgets *rw)
199 const gchar *text = gtk_entry_get_text (rw->e1);
201 if ( vr->input_var_is_string)
203 ov->type = OV_STRING;
204 ov->v.s = g_strdup (text);
208 ov->type = OV_NUMERIC;
209 ov->v.v = g_strtod (text, 0);
213 static void lo_up_set (PsppireValChooser *vr, struct old_value *ov, const struct range_widgets *rw)
215 const gchar *text = gtk_entry_get_text (rw->e1);
217 ov->type = OV_LOW_UP;
218 ov->v.range[1] = g_strtod (text, 0);
222 static void hi_down_set (PsppireValChooser *vr, struct old_value *ov, const struct range_widgets *rw)
224 const gchar *text = gtk_entry_get_text (rw->e1);
226 ov->type = OV_HIGH_DOWN;
227 ov->v.range[0] = g_strtod (text, 0);
230 static void missing_set (PsppireValChooser *vr, struct old_value *ov, const struct range_widgets *l)
232 ov->type = OV_MISSING;
236 static void sysmis_set (PsppireValChooser *vr, struct old_value *ov, const struct range_widgets *l)
238 ov->type = OV_SYSMIS;
241 static void else_set (PsppireValChooser *vr, struct old_value *ov, const struct range_widgets *l)
247 static void range_set (PsppireValChooser *vr, struct old_value *ov, const struct range_widgets *rw)
249 const gchar *text = gtk_entry_get_text (rw->e1);
252 ov->v.range[0] = g_strtod (text, 0);
254 text = gtk_entry_get_text (rw->e2);
255 ov->v.range[1] = g_strtod (text, 0);
258 static GtkWidget * range_entry (struct layout *l, struct range_widgets *rw)
260 GtkWidget *vbox = gtk_vbox_new (3, FALSE);
261 GtkWidget *entrylo = gtk_entry_new ();
262 GtkWidget *label = gtk_label_new (_("through"));
263 GtkWidget *entryhi = gtk_entry_new ();
265 rw->e1 = GTK_ENTRY (entrylo);
266 rw->e2 = GTK_ENTRY (entryhi);
268 gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
270 g_signal_connect (vbox, "notify::sensitive", G_CALLBACK (focus_follows_sensitivity), entrylo);
272 gtk_box_pack_start (GTK_BOX (vbox), entrylo, TRUE, TRUE, 0);
273 gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
274 gtk_box_pack_start (GTK_BOX (vbox), entryhi, TRUE, TRUE, 0);
278 static GtkWidget * simple_entry (struct layout *l, struct range_widgets *rw)
280 GtkWidget *entry = gtk_entry_new ();
282 rw->e1 = GTK_ENTRY (entry);
284 g_signal_connect (entry, "notify::sensitive", G_CALLBACK (focus_follows_sensitivity), entry);
289 static struct layout range_opt[n_VAL_CHOOSER_BUTTONS]=
291 {N_("Value:"), simple_entry, simple_set },
292 {N_("System Missing"), NULL, sysmis_set },
293 {N_("System or User Missing"), NULL, missing_set},
294 {N_("Range:"), range_entry, range_set },
295 {N_("Range, LOWEST thru value"), simple_entry, lo_up_set },
296 {N_("Range, value thru HIGHEST"), simple_entry, hi_down_set},
297 {N_("All other values"), NULL, else_set }
301 set_sensitivity_from_toggle (GtkToggleButton *togglebutton, GtkWidget *w)
303 gboolean active = gtk_toggle_button_get_active (togglebutton);
305 gtk_widget_set_sensitive (w, active);
309 psppire_val_chooser_init (PsppireValChooser *vr)
312 GtkWidget *aln = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
313 GtkWidget *table = gtk_table_new (11, 2, FALSE);
314 GSList *group = NULL;
317 gtk_alignment_set_padding (GTK_ALIGNMENT (aln), 0, 0, 5, 5);
319 vr->input_var_is_string = FALSE;
321 for (i = 0; i < n_VAL_CHOOSER_BUTTONS; ++i)
323 struct layout *l = &range_opt[i];
324 GtkWidget *label = gtk_label_new (gettext (l->label));
325 vr->rw[i].rb = GTK_TOGGLE_BUTTON (gtk_radio_button_new (group));
327 gtk_widget_set_sensitive (label, FALSE);
328 g_signal_connect (vr->rw[i].rb, "toggled", G_CALLBACK (set_sensitivity_from_toggle), label);
330 gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
332 group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (vr->rw[i].rb));
334 gtk_table_attach_defaults (GTK_TABLE (table), GTK_WIDGET (vr->rw[i].rb), 0, 1,
338 gtk_table_attach_defaults (GTK_TABLE (table), label, 1, 2,
344 GtkWidget *fill = l->fill (l, &vr->rw[i]);
346 gtk_widget_set_sensitive (fill, FALSE);
348 gtk_table_attach_defaults (GTK_TABLE (table), fill, 1, 2,
352 g_signal_connect (vr->rw[i].rb, "toggled", G_CALLBACK (set_sensitivity_from_toggle), fill);
356 gtk_frame_set_shadow_type (GTK_FRAME (vr), GTK_SHADOW_ETCHED_IN);
358 gtk_container_add (GTK_CONTAINER (aln), table);
359 gtk_container_add (GTK_CONTAINER (vr), aln);
361 gtk_widget_show_all (aln);
366 psppire_val_chooser_new (void)
368 return GTK_WIDGET (g_object_new (psppire_val_chooser_get_type (), NULL));
374 psppire_val_chooser_realize (GtkWidget *w)
376 PsppireValChooser *vr = PSPPIRE_VAL_CHOOSER (w);
378 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(vr->rw[0].rb), TRUE);
379 gtk_toggle_button_toggled (GTK_TOGGLE_BUTTON (vr->rw[0].rb));
381 /* Chain up to the parent class */
382 GTK_WIDGET_CLASS (parent_class)->realize (w);
388 /* A boxed type representing a value, or a range of values which may
389 potentially be replaced by something */
392 static struct old_value *
393 old_value_copy (struct old_value *ov)
395 struct old_value *copy = g_memdup (ov, sizeof (*copy));
397 if ( ov->type == OV_STRING )
398 copy->v.s = g_strdup (ov->v.s);
405 old_value_free (struct old_value *ov)
407 if (ov->type == OV_STRING)
413 old_value_to_string (const GValue *src, GValue *dest)
415 const struct old_value *ov = g_value_get_boxed (src);
421 gchar *text = g_strdup_printf ("%g", ov->v.v);
422 g_value_set_string (dest, text);
427 g_value_set_string (dest, ov->v.s);
430 g_value_set_string (dest, "MISSING");
433 g_value_set_string (dest, "SYSMIS");
436 g_value_set_string (dest, "ELSE");
441 char en_dash[6] = {0,0,0,0,0,0};
443 g_unichar_to_utf8 (0x2013, en_dash);
445 text = g_strdup_printf ("%g %s %g",
449 g_value_set_string (dest, text);
456 char en_dash[6] = {0,0,0,0,0,0};
458 g_unichar_to_utf8 (0x2013, en_dash);
460 text = g_strdup_printf ("LOWEST %s %g",
464 g_value_set_string (dest, text);
471 char en_dash[6] = {0,0,0,0,0,0};
473 g_unichar_to_utf8 (0x2013, en_dash);
475 text = g_strdup_printf ("%g %s HIGHEST",
479 g_value_set_string (dest, text);
484 g_warning ("Invalid type in old recode value");
485 g_value_set_string (dest, "???");
491 old_value_get_type (void)
497 t = g_boxed_type_register_static ("psppire-recode-old-values",
498 (GBoxedCopyFunc) old_value_copy,
499 (GBoxedFreeFunc) old_value_free);
501 g_value_register_transform_func (t, G_TYPE_STRING,
502 old_value_to_string);
510 /* Generate a syntax fragment for NV and append it to STR */
512 old_value_append_syntax (GString *str, const struct old_value *ov)
517 g_string_append_printf (str, "%g", ov->v.v);
521 struct string ds = DS_EMPTY_INITIALIZER;
522 syntax_gen_string (&ds, ss_cstr (ov->v.s));
523 g_string_append (str, ds_cstr (&ds));
528 g_string_append (str, "MISSING");
531 g_string_append (str, "SYSMIS");
534 g_string_append (str, "ELSE");
537 g_string_append_printf (str, "%g THRU %g",
542 g_string_append_printf (str, "LOWEST THRU %g",
546 g_string_append_printf (str, "%g THRU HIGHEST",
550 g_warning ("Invalid type in old recode value");
551 g_string_append (str, "???");
558 /* Set OV according to the current state of VR */
560 psppire_val_chooser_get_status (PsppireValChooser *vr, struct old_value *ov)
564 for (i = 0; i < n_VAL_CHOOSER_BUTTONS; ++i)
566 if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (vr->rw[i].rb)))
572 range_opt[i].set (vr, ov, &vr->rw[i]);
575 /* This might need to be changed to something less naive.
576 In particular, what happends with dates, etc?
579 num_to_string (gdouble x)
581 return g_strdup_printf ("%g", x);
585 /* Set VR according to the value of OV */
587 psppire_val_chooser_set_status (PsppireValChooser *vr, const struct old_value *ov)
593 for (i = 0; i < n_VAL_CHOOSER_BUTTONS; ++i)
596 gtk_entry_set_text (vr->rw[i].e1, "");
599 gtk_entry_set_text (vr->rw[i].e2, "");
605 gtk_toggle_button_set_active (vr->rw[0].rb, TRUE);
606 gtk_entry_set_text (vr->rw[0].e1, ov->v.s);
612 gtk_toggle_button_set_active (vr->rw[0].rb, TRUE);
614 str = num_to_string (ov->v.v);
616 gtk_entry_set_text (vr->rw[0].e1, str);
622 gtk_toggle_button_set_active (vr->rw[1].rb, TRUE);
626 gtk_toggle_button_set_active (vr->rw[2].rb, TRUE);
631 gchar *str = num_to_string (ov->v.range[0]);
632 gtk_toggle_button_set_active (vr->rw[3].rb, TRUE);
633 gtk_entry_set_text (vr->rw[3].e1, str);
637 str = num_to_string (ov->v.range[1]);
638 gtk_entry_set_text (vr->rw[3].e2, str);
645 gchar *str = num_to_string (ov->v.range[1]);
647 gtk_toggle_button_set_active (vr->rw[4].rb, TRUE);
649 gtk_entry_set_text (vr->rw[4].e1, str);
658 gchar *str = num_to_string (ov->v.range[0]);
660 gtk_toggle_button_set_active (vr->rw[5].rb, TRUE);
662 gtk_entry_set_text (vr->rw[5].e1, str);
669 gtk_toggle_button_set_active (vr->rw[6].rb, TRUE);
673 g_warning ("Unknown old value type");