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 void psppire_var_type_dialog_set_format (PsppireVarTypeDialog *dialog,
88 const struct fmt_spec *format);
90 static int find_format (const struct fmt_spec *target,
91 const struct fmt_spec formats[], int n_formats);
92 static int find_format_type (int target, const int types[], int n_types);
94 static void select_treeview_at_index (GtkTreeView *, int index);
96 static void update_width_decimals (const PsppireVarTypeDialog *);
97 static void refresh_active_button (PsppireVarTypeDialog *);
98 static void on_active_button_change (GtkToggleButton *,
99 PsppireVarTypeDialog *);
100 static void on_width_changed (GtkEntry *, PsppireVarTypeDialog *);
101 static void on_decimals_changed (GtkEntry *, PsppireVarTypeDialog *);
103 G_DEFINE_TYPE (PsppireVarTypeDialog,
104 psppire_var_type_dialog,
105 PSPPIRE_TYPE_DIALOG);
114 psppire_var_type_dialog_set_property (GObject *object,
119 PsppireVarTypeDialog *obj = PSPPIRE_VAR_TYPE_DIALOG (object);
124 psppire_var_type_dialog_set_format (obj, g_value_get_boxed (value));
127 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
133 psppire_var_type_dialog_get_property (GObject *object,
138 PsppireVarTypeDialog *obj = PSPPIRE_VAR_TYPE_DIALOG (object);
143 g_value_set_boxed (value, &obj->fmt_l);
146 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
152 psppire_var_type_dialog_set_format (PsppireVarTypeDialog *dialog,
153 const struct fmt_spec *format)
155 dialog->base_format = *format;
156 psppire_var_type_dialog_set_state (dialog);
159 static const struct fmt_spec *
160 psppire_var_type_dialog_get_format (const PsppireVarTypeDialog *dialog)
162 return &dialog->fmt_l;
166 psppire_var_type_dialog_init (PsppireVarTypeDialog *obj)
168 /* We do all of our work on widgets in the constructor function, because that
169 runs after the construction properties have been set. Otherwise
170 PsppireDialog's "orientation" property hasn't been set and therefore we
171 have no box to populate. */
172 obj->base_format = F_8_0;
177 psppire_var_type_dialog_class_init (PsppireVarTypeDialogClass *class)
179 GObjectClass *gobject_class;
180 gobject_class = G_OBJECT_CLASS (class);
182 gobject_class->constructor = psppire_var_type_dialog_constructor;
183 gobject_class->set_property = psppire_var_type_dialog_set_property;
184 gobject_class->get_property = psppire_var_type_dialog_get_property;
186 g_object_class_install_property (
187 gobject_class, PROP_FORMAT,
188 g_param_spec_boxed ("format",
190 "The format being edited.",
192 G_PARAM_READABLE | G_PARAM_WRITABLE));
195 PsppireVarTypeDialog *
196 psppire_var_type_dialog_new (const struct fmt_spec *format)
198 return PSPPIRE_VAR_TYPE_DIALOG (
199 g_object_new (PSPPIRE_TYPE_VAR_TYPE_DIALOG,
200 "orientation", PSPPIRE_HORIZONTAL,
206 psppire_var_type_dialog_run (GtkWindow *parent_window,
207 struct fmt_spec *format)
209 PsppireVarTypeDialog *dialog;
211 dialog = psppire_var_type_dialog_new (format);
212 gtk_window_set_transient_for (GTK_WINDOW (dialog), parent_window);
213 gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
214 gtk_widget_show (GTK_WIDGET (dialog));
216 if (psppire_dialog_run (PSPPIRE_DIALOG (dialog)) == GTK_RESPONSE_OK)
217 *format = *psppire_var_type_dialog_get_format (dialog);
219 gtk_widget_destroy (GTK_WIDGET (dialog));
223 /* callback for when any of the radio buttons are toggled */
225 on_toggle (GtkToggleButton *togglebutton, gpointer dialog_)
227 PsppireVarTypeDialog *dialog = dialog_;
229 if ( gtk_toggle_button_get_active (togglebutton) == TRUE)
230 refresh_active_button (dialog);
234 refresh_active_button (PsppireVarTypeDialog *dialog)
238 for (i = 0; i < num_BUTTONS; i++)
240 GtkToggleButton *toggle = GTK_TOGGLE_BUTTON (dialog->radioButton[i]);
242 if (gtk_toggle_button_get_active (toggle))
244 if (dialog->active_button != i)
246 dialog->active_button = i;
247 on_active_button_change (toggle, dialog);
253 g_return_if_reached ();
257 update_adj_ranges (PsppireVarTypeDialog *dialog)
259 enum fmt_type type = dialog->fmt_l.type;
260 const enum fmt_use use = FMT_FOR_OUTPUT;
261 int min_w = fmt_min_width (type, use);
262 int max_w = fmt_max_width (type, use);
263 int max_d = fmt_max_decimals (type, max_w, use);
265 g_object_set (dialog->adj_width,
266 "lower", (double) min_w,
267 "upper", (double) max_w,
270 g_object_set (dialog->adj_decimals,
272 "upper", (double) max_d,
276 /* callback for when any of the radio buttons are toggled */
278 on_active_button_change (GtkToggleButton *togglebutton,
279 PsppireVarTypeDialog *dialog)
284 W_DATE_FORMATS = 1 << 2,
285 W_DOLLAR_FORMATS = 1 << 3,
286 W_CC_FORMATS = 1 << 4,
289 enum widgets widgets;
292 switch (dialog->active_button)
297 case BUTTON_SCIENTIFIC:
298 widgets = W_WIDTH | W_DECIMALS;
306 widgets = W_DATE_FORMATS;
310 widgets = W_DOLLAR_FORMATS;
314 widgets = W_CC_FORMATS | W_WIDTH | W_DECIMALS;
318 /* No button active */
322 gtk_widget_set_visible (dialog->width_decimals, (widgets & W_WIDTH) != 0);
323 gtk_widget_set_visible (dialog->entry_width, (widgets & W_WIDTH) != 0);
324 gtk_widget_set_visible (dialog->entry_decimals, (widgets & W_DECIMALS) != 0);
325 gtk_widget_set_visible (dialog->label_decimals, (widgets & W_DECIMALS) != 0);
326 gtk_widget_set_visible (dialog->date_format_list,
327 (widgets & W_DATE_FORMATS) != 0);
328 gtk_widget_set_visible (dialog->custom_currency_hbox,
329 (widgets & W_CC_FORMATS) != 0);
330 gtk_widget_set_visible (dialog->dollar_window,
331 (widgets & W_DOLLAR_FORMATS) != 0);
333 dialog->fmt_l = dialog->base_format;
335 switch (dialog->active_button)
338 dialog->fmt_l.type = FMT_F;
341 dialog->fmt_l.type = FMT_COMMA;
344 dialog->fmt_l.type = FMT_DOT;
346 case BUTTON_SCIENTIFIC:
347 dialog->fmt_l.type = FMT_E;
350 dialog->fmt_l.type = FMT_A;
353 indx = find_format (&dialog->fmt_l, date_format,
354 sizeof date_format / sizeof *date_format);
355 select_treeview_at_index (dialog->date_format_treeview, indx);
356 dialog->fmt_l = date_format[indx];
359 indx = find_format (&dialog->fmt_l, dollar_format,
360 sizeof dollar_format / sizeof *dollar_format);
361 select_treeview_at_index (dialog->dollar_treeview, indx);
362 dialog->fmt_l = dollar_format[indx];
365 indx = find_format_type (dialog->fmt_l.type, cc_format,
366 sizeof cc_format / sizeof *cc_format);
367 select_treeview_at_index (dialog->custom_treeview, indx);
368 dialog->fmt_l.type = cc_format[indx];
372 fmt_fix_output (&dialog->fmt_l);
373 update_adj_ranges (dialog);
374 update_width_decimals (dialog);
378 add_to_group (GtkWidget *w, gpointer data)
380 GtkSizeGroup *sg = data;
382 gtk_size_group_add_widget (sg, w);
385 /* Set the local width and decimals entry boxes to reflec the local format */
387 update_width_decimals (const PsppireVarTypeDialog *dialog)
389 gtk_adjustment_set_value (dialog->adj_width, dialog->fmt_l.w);
390 gtk_adjustment_set_value (dialog->adj_decimals, dialog->fmt_l.d);
394 on_width_changed (GtkEntry *entry, PsppireVarTypeDialog *dialog)
396 int w = atoi (gtk_entry_get_text (GTK_ENTRY (dialog->entry_width)));
397 fmt_change_width (&dialog->fmt_l, w, FMT_FOR_OUTPUT);
398 update_width_decimals (dialog);
402 on_decimals_changed (GtkEntry *entry, PsppireVarTypeDialog *dialog)
404 int d = atoi (gtk_entry_get_text (GTK_ENTRY (dialog->entry_decimals)));
405 fmt_change_decimals (&dialog->fmt_l, d, FMT_FOR_OUTPUT);
406 update_width_decimals (dialog);
409 /* Callback for when the custom treeview row is changed.
410 It sets dialog box to reflect the selected format */
412 preview_custom (GtkWidget *w, gpointer data)
416 PsppireVarTypeDialog *dialog = data;
418 if ( dialog->active_button != BUTTON_CUSTOM )
421 text = gtk_entry_get_text (GTK_ENTRY (dialog->entry_decimals));
422 dialog->fmt_l.d = atoi (text);
424 text = gtk_entry_get_text (GTK_ENTRY (dialog->entry_width));
425 dialog->fmt_l.w = atoi (text);
428 if ( ! fmt_check_output (&dialog->fmt_l))
430 gtk_label_set_text (GTK_LABEL (dialog->label_psample), "---");
431 gtk_label_set_text (GTK_LABEL (dialog->label_nsample), "---");
439 sample_text = g_strchug (data_out (&v, NULL, &dialog->fmt_l));
440 gtk_label_set_text (GTK_LABEL (dialog->label_psample), sample_text);
441 g_free (sample_text);
444 sample_text = g_strchug (data_out (&v, NULL, &dialog->fmt_l));
445 gtk_label_set_text (GTK_LABEL (dialog->label_nsample), sample_text);
446 g_free (sample_text);
452 get_index_from_treeview (GtkTreeView *treeview)
454 GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview);
460 gtk_tree_selection_get_selected (selection, &model, &iter);
461 path = gtk_tree_model_get_path (model, &iter);
462 if (!path || gtk_tree_path_get_depth (path) < 1)
465 index = gtk_tree_path_get_indices (path)[0];
466 gtk_tree_path_free (path);
471 /* Callback for when a date treeview row is changed.
472 It sets the fmt_l_spec to reflect the selected format */
474 set_date_format_from_treeview (GtkTreeView *treeview,
475 PsppireVarTypeDialog *dialog)
477 dialog->fmt_l = date_format[get_index_from_treeview (treeview)];
480 /* Callback for when a dollar treeview row is changed.
481 It sets the fmt_l_spec to reflect the selected format */
483 set_dollar_format_from_treeview (GtkTreeView *treeview,
484 PsppireVarTypeDialog *dialog)
486 dialog->fmt_l = dollar_format[get_index_from_treeview (treeview)];
489 /* Callback for when a treeview row is changed.
490 It sets the type of the fmt_l to reflect the selected type */
492 set_custom_format_from_treeview (GtkTreeView *treeview,
493 PsppireVarTypeDialog *dialog)
495 dialog->fmt_l.type = cc_format[get_index_from_treeview (treeview)];
496 update_adj_ranges (dialog);
497 fmt_fix_output (&dialog->fmt_l);
498 update_width_decimals (dialog);
501 /* Create the structure */
503 psppire_var_type_dialog_constructor (GType type,
505 GObjectConstructParam *properties)
507 PsppireVarTypeDialog *dialog;
508 GtkContainer *content_area;
513 obj = G_OBJECT_CLASS (psppire_var_type_dialog_parent_class)->constructor (
514 type, n_properties, properties);
515 dialog = PSPPIRE_VAR_TYPE_DIALOG (obj);
517 xml = builder_new ("var-type-dialog.ui");
519 content_area = GTK_CONTAINER (PSPPIRE_DIALOG (dialog)->box);
520 gtk_container_add (GTK_CONTAINER (content_area),
521 get_widget_assert (xml, "var-type-dialog"));
523 dialog->active_button = -1;
525 g_signal_connect (dialog, "delete-event",
526 G_CALLBACK (gtk_widget_hide_on_delete), NULL);
528 dialog->radioButton[BUTTON_NUMERIC] =
529 get_widget_assert (xml,"radiobutton1");
530 dialog->radioButton[BUTTON_COMMA] =
531 get_widget_assert (xml,"radiobutton2");
532 dialog->radioButton[BUTTON_DOT] =
533 get_widget_assert (xml,"radiobutton3");
534 dialog->radioButton[BUTTON_SCIENTIFIC] =
535 get_widget_assert (xml,"radiobutton4");
536 dialog->radioButton[BUTTON_DATE] =
537 get_widget_assert (xml,"radiobutton5");
538 dialog->radioButton[BUTTON_DOLLAR] =
539 get_widget_assert (xml,"radiobutton6");
540 dialog->radioButton[BUTTON_CUSTOM] =
541 get_widget_assert (xml,"radiobutton7");
542 dialog->radioButton[BUTTON_STRING] =
543 get_widget_assert (xml,"radiobutton8");
546 dialog->date_format_list = get_widget_assert (xml, "scrolledwindow4");
547 dialog->width_decimals = get_widget_assert (xml, "width_decimals");
548 dialog->label_decimals = get_widget_assert (xml, "decimals_label");
549 dialog->entry_decimals = get_widget_assert (xml, "decimals_entry");
550 dialog->adj_decimals = gtk_spin_button_get_adjustment (
551 GTK_SPIN_BUTTON (dialog->entry_decimals));
553 dialog->label_psample = get_widget_assert (xml, "psample_label");
554 dialog->label_nsample = get_widget_assert (xml, "nsample_label");
557 dialog->entry_width = get_widget_assert (xml,"width_entry");
558 dialog->adj_width = gtk_spin_button_get_adjustment (
559 GTK_SPIN_BUTTON (dialog->entry_width));
560 dialog->custom_currency_hbox = get_widget_assert (xml,
561 "custom_currency_hbox");
563 dialog->dollar_window = get_widget_assert (xml, "dollar_window");
564 dialog->dollar_treeview =
565 GTK_TREE_VIEW (get_widget_assert (xml, "dollar_treeview"));
567 dialog->custom_treeview =
568 GTK_TREE_VIEW (get_widget_assert (xml, "custom_treeview"));
574 GtkListStore *list_store ;
576 GtkTreeViewColumn *column;
577 GtkCellRenderer *renderer ;
579 /* The "middle_box" is a vbox with serveral children.
580 However only one child is ever shown at a time.
581 We need to make sure that they all have the same width, to avoid
582 upleasant resizing effects */
583 GtkSizeGroup *sizeGroup = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
585 gtk_container_foreach (GTK_CONTAINER (get_widget_assert (xml, "middle_box")),
586 add_to_group, sizeGroup);
589 for (i = 0 ; i < num_BUTTONS; ++i )
590 g_signal_connect (dialog->radioButton[i], "toggled",
591 G_CALLBACK (on_toggle), dialog);
593 /* Populate the date format tree view */
594 dialog->date_format_treeview = GTK_TREE_VIEW (get_widget_assert (xml,
595 "date_format_list_view"));
597 renderer = gtk_cell_renderer_text_new ();
599 column = gtk_tree_view_column_new_with_attributes ("Title",
605 gtk_tree_view_append_column (GTK_TREE_VIEW (dialog->date_format_treeview),
609 list_store = gtk_list_store_new (1, G_TYPE_STRING);
611 for ( i = 0 ; i < sizeof (date_format) / sizeof (date_format[0]) ; ++i )
613 const struct fmt_spec *f = &date_format[i];
614 gtk_list_store_append (list_store, &iter);
615 gtk_list_store_set (list_store, &iter,
616 0, fmt_date_template (f->type, f->w),
620 gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->date_format_treeview),
621 GTK_TREE_MODEL (list_store));
623 g_object_unref (list_store);
625 g_signal_connect (dialog->date_format_treeview, "cursor-changed",
626 G_CALLBACK (set_date_format_from_treeview), dialog);
629 /* populate the dollar treeview */
631 renderer = gtk_cell_renderer_text_new ();
633 column = gtk_tree_view_column_new_with_attributes ("Title",
639 gtk_tree_view_append_column (GTK_TREE_VIEW (dialog->dollar_treeview),
643 list_store = gtk_list_store_new (1, G_TYPE_STRING);
645 for ( i = 0 ; i < sizeof (dollar_format)/sizeof (dollar_format[0]) ; ++i )
647 char *template = settings_dollar_template (&dollar_format[i]);
648 gtk_list_store_append (list_store, &iter);
649 gtk_list_store_set (list_store, &iter,
655 gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->dollar_treeview),
656 GTK_TREE_MODEL (list_store));
658 g_object_unref (list_store);
660 g_signal_connect (dialog->dollar_treeview,
662 G_CALLBACK (set_dollar_format_from_treeview), dialog);
664 g_signal_connect_swapped (dialog->dollar_treeview,
666 G_CALLBACK (update_width_decimals), dialog);
669 /* populate the custom treeview */
671 renderer = gtk_cell_renderer_text_new ();
673 column = gtk_tree_view_column_new_with_attributes ("Title",
679 gtk_tree_view_append_column (GTK_TREE_VIEW (dialog->custom_treeview),
683 list_store = gtk_list_store_new (1, G_TYPE_STRING);
685 for ( i = 0 ; i < 5 ; ++i )
687 enum fmt_type cc_fmts[5] = {FMT_CCA, FMT_CCB, FMT_CCC, FMT_CCD, FMT_CCE};
688 gtk_list_store_append (list_store, &iter);
689 gtk_list_store_set (list_store, &iter,
690 0, fmt_name (cc_fmts[i]),
694 gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->custom_treeview),
695 GTK_TREE_MODEL (list_store));
697 g_object_unref (list_store);
700 g_signal_connect (dialog->custom_treeview,
702 G_CALLBACK (set_custom_format_from_treeview), dialog);
705 g_signal_connect (dialog->custom_treeview,
707 G_CALLBACK (preview_custom), dialog);
710 g_signal_connect (dialog->entry_width, "changed",
711 G_CALLBACK (on_width_changed), dialog);
712 g_signal_connect (dialog->entry_decimals, "changed",
713 G_CALLBACK (on_decimals_changed), dialog);
715 g_signal_connect (dialog->entry_width,
717 G_CALLBACK (preview_custom), dialog);
720 g_signal_connect (dialog->entry_decimals,
722 G_CALLBACK (preview_custom), dialog);
726 g_object_unref (xml);
728 psppire_var_type_dialog_set_state (dialog);
734 /* Set a particular button to be active */
736 var_type_dialog_set_active_button (PsppireVarTypeDialog *dialog, gint b)
738 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dialog->radioButton[b]),
745 select_treeview_at_index (GtkTreeView *treeview, int index)
749 path = gtk_tree_path_new_from_indices (index, -1);
750 gtk_tree_view_set_cursor (treeview, path, 0, 0);
751 gtk_tree_path_free (path);
755 find_format (const struct fmt_spec *target,
756 const struct fmt_spec formats[], int n_formats)
760 for (i = 0; i < n_formats; i++)
761 if (fmt_equal (target, &formats[i]))
768 find_format_type (int target, const int types[], int n_types)
772 for (i = 0; i < n_types; i++)
773 if (target == types[i])
779 /* Set up the state of the dialog box to match the variable VAR */
781 psppire_var_type_dialog_set_state (PsppireVarTypeDialog *dialog)
785 g_return_if_fail (dialog != NULL);
787 /* Populate the radio button states */
788 switch (dialog->base_format.type)
792 button = BUTTON_NUMERIC;
795 button = BUTTON_STRING;
798 button = BUTTON_COMMA;
804 button = BUTTON_DOLLAR;
819 button = BUTTON_DATE;
826 button = BUTTON_CUSTOM;
830 var_type_dialog_set_active_button (dialog, button);
831 refresh_active_button (dialog);
832 on_active_button_change (GTK_TOGGLE_BUTTON (dialog->radioButton[button]),