1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2005, 2006, 2010, 2011, 2012 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/>. */
18 /* This module describes the behaviour of the Variable Type dialog box used
19 for inputing the variable type in the var sheet */
27 #include "data/data-out.h"
28 #include "data/settings.h"
29 #include "data/variable.h"
30 #include "libpspp/message.h"
31 #include "ui/gui/builder-wrapper.h"
32 #include "ui/gui/psppire-format.h"
33 #include "ui/gui/var-type-dialog.h"
35 static const struct fmt_spec date_format[] =
53 {FMT_DATETIME, 17, 0},
58 static const struct fmt_spec dollar_format[] =
74 static const int cc_format[] =
83 static GObject *psppire_var_type_dialog_constructor (GType type, guint,
84 GObjectConstructParam *);
85 static void psppire_var_type_dialog_set_state (PsppireVarTypeDialog *);
87 static int find_format (const struct fmt_spec *target,
88 const struct fmt_spec formats[], int n_formats);
89 static int find_format_type (int target, const int types[], int n_types);
91 static void select_treeview_at_index (GtkTreeView *, int index);
93 static void update_width_decimals (const PsppireVarTypeDialog *);
94 static void refresh_active_button (PsppireVarTypeDialog *);
95 static void on_active_button_change (GtkToggleButton *,
96 PsppireVarTypeDialog *);
97 static void on_width_changed (GtkEntry *, PsppireVarTypeDialog *);
98 static void on_decimals_changed (GtkEntry *, PsppireVarTypeDialog *);
100 G_DEFINE_TYPE (PsppireVarTypeDialog,
101 psppire_var_type_dialog,
102 PSPPIRE_TYPE_DIALOG);
111 psppire_var_type_dialog_set_property (GObject *object,
116 PsppireVarTypeDialog *obj = PSPPIRE_VAR_TYPE_DIALOG (object);
121 psppire_var_type_dialog_set_format (obj, g_value_get_boxed (value));
124 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
130 psppire_var_type_dialog_get_property (GObject *object,
135 PsppireVarTypeDialog *obj = PSPPIRE_VAR_TYPE_DIALOG (object);
140 g_value_set_boxed (value, &obj->fmt_l);
143 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
149 psppire_var_type_dialog_set_format (PsppireVarTypeDialog *dialog,
150 const struct fmt_spec *format)
152 dialog->base_format = *format;
153 psppire_var_type_dialog_set_state (dialog);
156 const struct fmt_spec *
157 psppire_var_type_dialog_get_format (const PsppireVarTypeDialog *dialog)
159 return &dialog->fmt_l;
163 psppire_var_type_dialog_init (PsppireVarTypeDialog *obj)
165 /* We do all of our work on widgets in the constructor function, because that
166 runs after the construction properties have been set. Otherwise
167 PsppireDialog's "orientation" property hasn't been set and therefore we
168 have no box to populate. */
169 obj->base_format = F_8_0;
174 psppire_var_type_dialog_class_init (PsppireVarTypeDialogClass *class)
176 GObjectClass *gobject_class;
177 gobject_class = G_OBJECT_CLASS (class);
179 gobject_class->constructor = psppire_var_type_dialog_constructor;
180 gobject_class->set_property = psppire_var_type_dialog_set_property;
181 gobject_class->get_property = psppire_var_type_dialog_get_property;
183 g_object_class_install_property (
184 gobject_class, PROP_FORMAT,
185 g_param_spec_boxed ("format",
187 "The format being edited.",
189 G_PARAM_READABLE | G_PARAM_WRITABLE));
192 PsppireVarTypeDialog *
193 psppire_var_type_dialog_new (const struct fmt_spec *format)
195 return PSPPIRE_VAR_TYPE_DIALOG (
196 g_object_new (PSPPIRE_TYPE_VAR_TYPE_DIALOG,
197 "orientation", PSPPIRE_HORIZONTAL,
203 psppire_var_type_dialog_run (GtkWindow *parent_window,
204 struct fmt_spec *format)
206 PsppireVarTypeDialog *dialog;
208 dialog = psppire_var_type_dialog_new (format);
209 gtk_window_set_transient_for (GTK_WINDOW (dialog), parent_window);
210 gtk_widget_show (GTK_WIDGET (dialog));
212 if (psppire_dialog_run (PSPPIRE_DIALOG (dialog)) == GTK_RESPONSE_OK)
213 *format = *psppire_var_type_dialog_get_format (dialog);
215 gtk_widget_destroy (GTK_WIDGET (dialog));
219 /* callback for when any of the radio buttons are toggled */
221 on_toggle (GtkToggleButton *togglebutton, gpointer dialog_)
223 PsppireVarTypeDialog *dialog = dialog_;
225 if ( gtk_toggle_button_get_active (togglebutton) == TRUE)
226 refresh_active_button (dialog);
230 refresh_active_button (PsppireVarTypeDialog *dialog)
234 for (i = 0; i < num_BUTTONS; i++)
236 GtkToggleButton *toggle = GTK_TOGGLE_BUTTON (dialog->radioButton[i]);
238 if (gtk_toggle_button_get_active (toggle))
240 if (dialog->active_button != i)
242 dialog->active_button = i;
243 on_active_button_change (toggle, dialog);
249 g_return_if_reached ();
253 update_adj_ranges (PsppireVarTypeDialog *dialog)
255 enum fmt_type type = dialog->fmt_l.type;
256 const enum fmt_use use = FMT_FOR_OUTPUT;
257 int min_w = fmt_min_width (type, use);
258 int max_w = fmt_max_width (type, use);
259 int max_d = fmt_max_decimals (type, max_w, use);
261 g_object_set (dialog->adj_width,
262 "lower", (double) min_w,
263 "upper", (double) max_w,
266 g_object_set (dialog->adj_decimals,
268 "upper", (double) max_d,
272 /* callback for when any of the radio buttons are toggled */
274 on_active_button_change (GtkToggleButton *togglebutton,
275 PsppireVarTypeDialog *dialog)
280 W_DATE_FORMATS = 1 << 2,
281 W_DOLLAR_FORMATS = 1 << 3,
282 W_CC_FORMATS = 1 << 4,
285 enum widgets widgets;
288 switch (dialog->active_button)
293 case BUTTON_SCIENTIFIC:
294 widgets = W_WIDTH | W_DECIMALS;
302 widgets = W_DATE_FORMATS;
306 widgets = W_DOLLAR_FORMATS;
310 widgets = W_CC_FORMATS | W_WIDTH | W_DECIMALS;
314 /* No button active */
318 gtk_widget_set_visible (dialog->width_decimals, (widgets & W_WIDTH) != 0);
319 gtk_widget_set_visible (dialog->entry_width, (widgets & W_WIDTH) != 0);
320 gtk_widget_set_visible (dialog->entry_decimals, (widgets & W_DECIMALS) != 0);
321 gtk_widget_set_visible (dialog->label_decimals, (widgets & W_DECIMALS) != 0);
322 gtk_widget_set_visible (dialog->date_format_list,
323 (widgets & W_DATE_FORMATS) != 0);
324 gtk_widget_set_visible (dialog->custom_currency_hbox,
325 (widgets & W_CC_FORMATS) != 0);
326 gtk_widget_set_visible (dialog->dollar_window,
327 (widgets & W_DOLLAR_FORMATS) != 0);
329 dialog->fmt_l = dialog->base_format;
331 switch (dialog->active_button)
334 dialog->fmt_l.type = FMT_F;
337 dialog->fmt_l.type = FMT_COMMA;
340 dialog->fmt_l.type = FMT_DOT;
342 case BUTTON_SCIENTIFIC:
343 dialog->fmt_l.type = FMT_E;
346 dialog->fmt_l.type = FMT_A;
349 indx = find_format (&dialog->fmt_l, date_format,
350 sizeof date_format / sizeof *date_format);
351 select_treeview_at_index (dialog->date_format_treeview, indx);
352 dialog->fmt_l = date_format[indx];
355 indx = find_format (&dialog->fmt_l, dollar_format,
356 sizeof dollar_format / sizeof *dollar_format);
357 select_treeview_at_index (dialog->dollar_treeview, indx);
358 dialog->fmt_l = dollar_format[indx];
361 indx = find_format_type (dialog->fmt_l.type, cc_format,
362 sizeof cc_format / sizeof *cc_format);
363 select_treeview_at_index (dialog->custom_treeview, indx);
364 dialog->fmt_l.type = cc_format[indx];
368 fmt_fix_output (&dialog->fmt_l);
369 update_adj_ranges (dialog);
370 update_width_decimals (dialog);
374 add_to_group (GtkWidget *w, gpointer data)
376 GtkSizeGroup *sg = data;
378 gtk_size_group_add_widget (sg, w);
381 /* Set the local width and decimals entry boxes to reflec the local format */
383 update_width_decimals (const PsppireVarTypeDialog *dialog)
385 gtk_adjustment_set_value (dialog->adj_width, dialog->fmt_l.w);
386 gtk_adjustment_set_value (dialog->adj_decimals, dialog->fmt_l.d);
390 on_width_changed (GtkEntry *entry, PsppireVarTypeDialog *dialog)
392 int w = atoi (gtk_entry_get_text (GTK_ENTRY (dialog->entry_width)));
393 fmt_change_width (&dialog->fmt_l, w, FMT_FOR_OUTPUT);
394 update_width_decimals (dialog);
398 on_decimals_changed (GtkEntry *entry, PsppireVarTypeDialog *dialog)
400 int d = atoi (gtk_entry_get_text (GTK_ENTRY (dialog->entry_decimals)));
401 fmt_change_decimals (&dialog->fmt_l, d, FMT_FOR_OUTPUT);
402 update_width_decimals (dialog);
405 /* Callback for when the custom treeview row is changed.
406 It sets dialog box to reflect the selected format */
408 preview_custom (GtkWidget *w, gpointer data)
412 PsppireVarTypeDialog *dialog = data;
414 if ( dialog->active_button != BUTTON_CUSTOM )
417 text = gtk_entry_get_text (GTK_ENTRY (dialog->entry_decimals));
418 dialog->fmt_l.d = atoi (text);
420 text = gtk_entry_get_text (GTK_ENTRY (dialog->entry_width));
421 dialog->fmt_l.w = atoi (text);
424 if ( ! fmt_check_output (&dialog->fmt_l))
426 gtk_label_set_text (GTK_LABEL (dialog->label_psample), "---");
427 gtk_label_set_text (GTK_LABEL (dialog->label_nsample), "---");
435 sample_text = g_strchug (data_out (&v, NULL, &dialog->fmt_l));
436 gtk_label_set_text (GTK_LABEL (dialog->label_psample), sample_text);
437 g_free (sample_text);
440 sample_text = g_strchug (data_out (&v, NULL, &dialog->fmt_l));
441 gtk_label_set_text (GTK_LABEL (dialog->label_nsample), sample_text);
442 g_free (sample_text);
448 get_index_from_treeview (GtkTreeView *treeview)
450 GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview);
456 gtk_tree_selection_get_selected (selection, &model, &iter);
457 path = gtk_tree_model_get_path (model, &iter);
458 if (!path || gtk_tree_path_get_depth (path) < 1)
461 index = gtk_tree_path_get_indices (path)[0];
462 gtk_tree_path_free (path);
467 /* Callback for when a date treeview row is changed.
468 It sets the fmt_l_spec to reflect the selected format */
470 set_date_format_from_treeview (GtkTreeView *treeview,
471 PsppireVarTypeDialog *dialog)
473 dialog->fmt_l = date_format[get_index_from_treeview (treeview)];
476 /* Callback for when a dollar treeview row is changed.
477 It sets the fmt_l_spec to reflect the selected format */
479 set_dollar_format_from_treeview (GtkTreeView *treeview,
480 PsppireVarTypeDialog *dialog)
482 dialog->fmt_l = dollar_format[get_index_from_treeview (treeview)];
485 /* Callback for when a treeview row is changed.
486 It sets the type of the fmt_l to reflect the selected type */
488 set_custom_format_from_treeview (GtkTreeView *treeview,
489 PsppireVarTypeDialog *dialog)
491 dialog->fmt_l.type = cc_format[get_index_from_treeview (treeview)];
492 update_adj_ranges (dialog);
493 fmt_fix_output (&dialog->fmt_l);
494 update_width_decimals (dialog);
497 /* Create the structure */
499 psppire_var_type_dialog_constructor (GType type,
501 GObjectConstructParam *properties)
503 PsppireVarTypeDialog *dialog;
504 GtkContainer *content_area;
509 obj = G_OBJECT_CLASS (psppire_var_type_dialog_parent_class)->constructor (
510 type, n_properties, properties);
511 dialog = PSPPIRE_VAR_TYPE_DIALOG (obj);
513 xml = builder_new ("var-type-dialog.ui");
515 content_area = GTK_CONTAINER (PSPPIRE_DIALOG (dialog)->box);
516 gtk_container_add (GTK_CONTAINER (content_area),
517 get_widget_assert (xml, "var-type-dialog"));
519 dialog->active_button = -1;
521 g_signal_connect (dialog, "delete-event",
522 G_CALLBACK (gtk_widget_hide_on_delete), NULL);
524 dialog->radioButton[BUTTON_NUMERIC] =
525 get_widget_assert (xml,"radiobutton1");
526 dialog->radioButton[BUTTON_COMMA] =
527 get_widget_assert (xml,"radiobutton2");
528 dialog->radioButton[BUTTON_DOT] =
529 get_widget_assert (xml,"radiobutton3");
530 dialog->radioButton[BUTTON_SCIENTIFIC] =
531 get_widget_assert (xml,"radiobutton4");
532 dialog->radioButton[BUTTON_DATE] =
533 get_widget_assert (xml,"radiobutton5");
534 dialog->radioButton[BUTTON_DOLLAR] =
535 get_widget_assert (xml,"radiobutton6");
536 dialog->radioButton[BUTTON_CUSTOM] =
537 get_widget_assert (xml,"radiobutton7");
538 dialog->radioButton[BUTTON_STRING] =
539 get_widget_assert (xml,"radiobutton8");
542 dialog->date_format_list = get_widget_assert (xml, "scrolledwindow4");
543 dialog->width_decimals = get_widget_assert (xml, "width_decimals");
544 dialog->label_decimals = get_widget_assert (xml, "decimals_label");
545 dialog->entry_decimals = get_widget_assert (xml, "decimals_entry");
546 dialog->adj_decimals = gtk_spin_button_get_adjustment (
547 GTK_SPIN_BUTTON (dialog->entry_decimals));
549 dialog->label_psample = get_widget_assert (xml, "psample_label");
550 dialog->label_nsample = get_widget_assert (xml, "nsample_label");
553 dialog->entry_width = get_widget_assert (xml,"width_entry");
554 dialog->adj_width = gtk_spin_button_get_adjustment (
555 GTK_SPIN_BUTTON (dialog->entry_width));
556 dialog->custom_currency_hbox = get_widget_assert (xml,
557 "custom_currency_hbox");
559 dialog->dollar_window = get_widget_assert (xml, "dollar_window");
560 dialog->dollar_treeview =
561 GTK_TREE_VIEW (get_widget_assert (xml, "dollar_treeview"));
563 dialog->custom_treeview =
564 GTK_TREE_VIEW (get_widget_assert (xml, "custom_treeview"));
570 GtkListStore *list_store ;
572 GtkTreeViewColumn *column;
573 GtkCellRenderer *renderer ;
575 /* The "middle_box" is a vbox with serveral children.
576 However only one child is ever shown at a time.
577 We need to make sure that they all have the same width, to avoid
578 upleasant resizing effects */
579 GtkSizeGroup *sizeGroup = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
581 gtk_container_foreach (GTK_CONTAINER (get_widget_assert (xml, "middle_box")),
582 add_to_group, sizeGroup);
585 for (i = 0 ; i < num_BUTTONS; ++i )
586 g_signal_connect (dialog->radioButton[i], "toggled",
587 G_CALLBACK (on_toggle), dialog);
589 /* Populate the date format tree view */
590 dialog->date_format_treeview = GTK_TREE_VIEW (get_widget_assert (xml,
591 "date_format_list_view"));
593 renderer = gtk_cell_renderer_text_new ();
595 column = gtk_tree_view_column_new_with_attributes ("Title",
601 gtk_tree_view_append_column (GTK_TREE_VIEW (dialog->date_format_treeview),
605 list_store = gtk_list_store_new (1, G_TYPE_STRING);
607 for ( i = 0 ; i < sizeof (date_format) / sizeof (date_format[0]) ; ++i )
609 const struct fmt_spec *f = &date_format[i];
610 gtk_list_store_append (list_store, &iter);
611 gtk_list_store_set (list_store, &iter,
612 0, fmt_date_template (f->type, f->w),
616 gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->date_format_treeview),
617 GTK_TREE_MODEL (list_store));
619 g_object_unref (list_store);
621 g_signal_connect (dialog->date_format_treeview, "cursor-changed",
622 G_CALLBACK (set_date_format_from_treeview), dialog);
625 /* populate the dollar treeview */
627 renderer = gtk_cell_renderer_text_new ();
629 column = gtk_tree_view_column_new_with_attributes ("Title",
635 gtk_tree_view_append_column (GTK_TREE_VIEW (dialog->dollar_treeview),
639 list_store = gtk_list_store_new (1, G_TYPE_STRING);
641 for ( i = 0 ; i < sizeof (dollar_format)/sizeof (dollar_format[0]) ; ++i )
643 char *template = settings_dollar_template (&dollar_format[i]);
644 gtk_list_store_append (list_store, &iter);
645 gtk_list_store_set (list_store, &iter,
651 gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->dollar_treeview),
652 GTK_TREE_MODEL (list_store));
654 g_object_unref (list_store);
656 g_signal_connect (dialog->dollar_treeview,
658 G_CALLBACK (set_dollar_format_from_treeview), dialog);
660 g_signal_connect_swapped (dialog->dollar_treeview,
662 G_CALLBACK (update_width_decimals), dialog);
665 /* populate the custom treeview */
667 renderer = gtk_cell_renderer_text_new ();
669 column = gtk_tree_view_column_new_with_attributes ("Title",
675 gtk_tree_view_append_column (GTK_TREE_VIEW (dialog->custom_treeview),
679 list_store = gtk_list_store_new (1, G_TYPE_STRING);
681 for ( i = 0 ; i < 5 ; ++i )
683 enum fmt_type cc_fmts[5] = {FMT_CCA, FMT_CCB, FMT_CCC, FMT_CCD, FMT_CCE};
684 gtk_list_store_append (list_store, &iter);
685 gtk_list_store_set (list_store, &iter,
686 0, fmt_name (cc_fmts[i]),
690 gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->custom_treeview),
691 GTK_TREE_MODEL (list_store));
693 g_object_unref (list_store);
696 g_signal_connect (dialog->custom_treeview,
698 G_CALLBACK (set_custom_format_from_treeview), dialog);
701 g_signal_connect (dialog->custom_treeview,
703 G_CALLBACK (preview_custom), dialog);
706 g_signal_connect (dialog->entry_width, "changed",
707 G_CALLBACK (on_width_changed), dialog);
708 g_signal_connect (dialog->entry_decimals, "changed",
709 G_CALLBACK (on_decimals_changed), dialog);
711 g_signal_connect (dialog->entry_width,
713 G_CALLBACK (preview_custom), dialog);
716 g_signal_connect (dialog->entry_decimals,
718 G_CALLBACK (preview_custom), dialog);
722 g_object_unref (xml);
724 psppire_var_type_dialog_set_state (dialog);
730 /* Set a particular button to be active */
732 var_type_dialog_set_active_button (PsppireVarTypeDialog *dialog, gint b)
734 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dialog->radioButton[b]),
741 select_treeview_at_index (GtkTreeView *treeview, int index)
745 path = gtk_tree_path_new_from_indices (index, -1);
746 gtk_tree_view_set_cursor (treeview, path, 0, 0);
747 gtk_tree_path_free (path);
751 find_format (const struct fmt_spec *target,
752 const struct fmt_spec formats[], int n_formats)
756 for (i = 0; i < n_formats; i++)
757 if (fmt_equal (target, &formats[i]))
764 find_format_type (int target, const int types[], int n_types)
768 for (i = 0; i < n_types; i++)
769 if (target == types[i])
775 /* Set up the state of the dialog box to match the variable VAR */
777 psppire_var_type_dialog_set_state (PsppireVarTypeDialog *dialog)
781 g_return_if_fail (dialog != NULL);
783 /* Populate the radio button states */
784 switch (dialog->base_format.type)
788 button = BUTTON_NUMERIC;
791 button = BUTTON_STRING;
794 button = BUTTON_COMMA;
800 button = BUTTON_DOLLAR;
815 button = BUTTON_DATE;
822 button = BUTTON_CUSTOM;
826 var_type_dialog_set_active_button (dialog, button);
827 refresh_active_button (dialog);
828 on_active_button_change (GTK_TOGGLE_BUTTON (dialog->radioButton[button]),