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_window_set_modal (GTK_WINDOW (dialog), TRUE);
211 gtk_widget_show (GTK_WIDGET (dialog));
213 if (psppire_dialog_run (PSPPIRE_DIALOG (dialog)) == GTK_RESPONSE_OK)
214 *format = *psppire_var_type_dialog_get_format (dialog);
216 gtk_widget_destroy (GTK_WIDGET (dialog));
220 /* callback for when any of the radio buttons are toggled */
222 on_toggle (GtkToggleButton *togglebutton, gpointer dialog_)
224 PsppireVarTypeDialog *dialog = dialog_;
226 if ( gtk_toggle_button_get_active (togglebutton) == TRUE)
227 refresh_active_button (dialog);
231 refresh_active_button (PsppireVarTypeDialog *dialog)
235 for (i = 0; i < num_BUTTONS; i++)
237 GtkToggleButton *toggle = GTK_TOGGLE_BUTTON (dialog->radioButton[i]);
239 if (gtk_toggle_button_get_active (toggle))
241 if (dialog->active_button != i)
243 dialog->active_button = i;
244 on_active_button_change (toggle, dialog);
250 g_return_if_reached ();
254 update_adj_ranges (PsppireVarTypeDialog *dialog)
256 enum fmt_type type = dialog->fmt_l.type;
257 const enum fmt_use use = FMT_FOR_OUTPUT;
258 int min_w = fmt_min_width (type, use);
259 int max_w = fmt_max_width (type, use);
260 int max_d = fmt_max_decimals (type, max_w, use);
262 g_object_set (dialog->adj_width,
263 "lower", (double) min_w,
264 "upper", (double) max_w,
267 g_object_set (dialog->adj_decimals,
269 "upper", (double) max_d,
273 /* callback for when any of the radio buttons are toggled */
275 on_active_button_change (GtkToggleButton *togglebutton,
276 PsppireVarTypeDialog *dialog)
281 W_DATE_FORMATS = 1 << 2,
282 W_DOLLAR_FORMATS = 1 << 3,
283 W_CC_FORMATS = 1 << 4,
286 enum widgets widgets;
289 switch (dialog->active_button)
294 case BUTTON_SCIENTIFIC:
295 widgets = W_WIDTH | W_DECIMALS;
303 widgets = W_DATE_FORMATS;
307 widgets = W_DOLLAR_FORMATS;
311 widgets = W_CC_FORMATS | W_WIDTH | W_DECIMALS;
315 /* No button active */
319 gtk_widget_set_visible (dialog->width_decimals, (widgets & W_WIDTH) != 0);
320 gtk_widget_set_visible (dialog->entry_width, (widgets & W_WIDTH) != 0);
321 gtk_widget_set_visible (dialog->entry_decimals, (widgets & W_DECIMALS) != 0);
322 gtk_widget_set_visible (dialog->label_decimals, (widgets & W_DECIMALS) != 0);
323 gtk_widget_set_visible (dialog->date_format_list,
324 (widgets & W_DATE_FORMATS) != 0);
325 gtk_widget_set_visible (dialog->custom_currency_hbox,
326 (widgets & W_CC_FORMATS) != 0);
327 gtk_widget_set_visible (dialog->dollar_window,
328 (widgets & W_DOLLAR_FORMATS) != 0);
330 dialog->fmt_l = dialog->base_format;
332 switch (dialog->active_button)
335 dialog->fmt_l.type = FMT_F;
338 dialog->fmt_l.type = FMT_COMMA;
341 dialog->fmt_l.type = FMT_DOT;
343 case BUTTON_SCIENTIFIC:
344 dialog->fmt_l.type = FMT_E;
347 dialog->fmt_l.type = FMT_A;
350 indx = find_format (&dialog->fmt_l, date_format,
351 sizeof date_format / sizeof *date_format);
352 select_treeview_at_index (dialog->date_format_treeview, indx);
353 dialog->fmt_l = date_format[indx];
356 indx = find_format (&dialog->fmt_l, dollar_format,
357 sizeof dollar_format / sizeof *dollar_format);
358 select_treeview_at_index (dialog->dollar_treeview, indx);
359 dialog->fmt_l = dollar_format[indx];
362 indx = find_format_type (dialog->fmt_l.type, cc_format,
363 sizeof cc_format / sizeof *cc_format);
364 select_treeview_at_index (dialog->custom_treeview, indx);
365 dialog->fmt_l.type = cc_format[indx];
369 fmt_fix_output (&dialog->fmt_l);
370 update_adj_ranges (dialog);
371 update_width_decimals (dialog);
375 add_to_group (GtkWidget *w, gpointer data)
377 GtkSizeGroup *sg = data;
379 gtk_size_group_add_widget (sg, w);
382 /* Set the local width and decimals entry boxes to reflec the local format */
384 update_width_decimals (const PsppireVarTypeDialog *dialog)
386 gtk_adjustment_set_value (dialog->adj_width, dialog->fmt_l.w);
387 gtk_adjustment_set_value (dialog->adj_decimals, dialog->fmt_l.d);
391 on_width_changed (GtkEntry *entry, PsppireVarTypeDialog *dialog)
393 int w = atoi (gtk_entry_get_text (GTK_ENTRY (dialog->entry_width)));
394 fmt_change_width (&dialog->fmt_l, w, FMT_FOR_OUTPUT);
395 update_width_decimals (dialog);
399 on_decimals_changed (GtkEntry *entry, PsppireVarTypeDialog *dialog)
401 int d = atoi (gtk_entry_get_text (GTK_ENTRY (dialog->entry_decimals)));
402 fmt_change_decimals (&dialog->fmt_l, d, FMT_FOR_OUTPUT);
403 update_width_decimals (dialog);
406 /* Callback for when the custom treeview row is changed.
407 It sets dialog box to reflect the selected format */
409 preview_custom (GtkWidget *w, gpointer data)
413 PsppireVarTypeDialog *dialog = data;
415 if ( dialog->active_button != BUTTON_CUSTOM )
418 text = gtk_entry_get_text (GTK_ENTRY (dialog->entry_decimals));
419 dialog->fmt_l.d = atoi (text);
421 text = gtk_entry_get_text (GTK_ENTRY (dialog->entry_width));
422 dialog->fmt_l.w = atoi (text);
425 if ( ! fmt_check_output (&dialog->fmt_l))
427 gtk_label_set_text (GTK_LABEL (dialog->label_psample), "---");
428 gtk_label_set_text (GTK_LABEL (dialog->label_nsample), "---");
436 sample_text = g_strchug (data_out (&v, NULL, &dialog->fmt_l));
437 gtk_label_set_text (GTK_LABEL (dialog->label_psample), sample_text);
438 g_free (sample_text);
441 sample_text = g_strchug (data_out (&v, NULL, &dialog->fmt_l));
442 gtk_label_set_text (GTK_LABEL (dialog->label_nsample), sample_text);
443 g_free (sample_text);
449 get_index_from_treeview (GtkTreeView *treeview)
451 GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview);
457 gtk_tree_selection_get_selected (selection, &model, &iter);
458 path = gtk_tree_model_get_path (model, &iter);
459 if (!path || gtk_tree_path_get_depth (path) < 1)
462 index = gtk_tree_path_get_indices (path)[0];
463 gtk_tree_path_free (path);
468 /* Callback for when a date treeview row is changed.
469 It sets the fmt_l_spec to reflect the selected format */
471 set_date_format_from_treeview (GtkTreeView *treeview,
472 PsppireVarTypeDialog *dialog)
474 dialog->fmt_l = date_format[get_index_from_treeview (treeview)];
477 /* Callback for when a dollar treeview row is changed.
478 It sets the fmt_l_spec to reflect the selected format */
480 set_dollar_format_from_treeview (GtkTreeView *treeview,
481 PsppireVarTypeDialog *dialog)
483 dialog->fmt_l = dollar_format[get_index_from_treeview (treeview)];
486 /* Callback for when a treeview row is changed.
487 It sets the type of the fmt_l to reflect the selected type */
489 set_custom_format_from_treeview (GtkTreeView *treeview,
490 PsppireVarTypeDialog *dialog)
492 dialog->fmt_l.type = cc_format[get_index_from_treeview (treeview)];
493 update_adj_ranges (dialog);
494 fmt_fix_output (&dialog->fmt_l);
495 update_width_decimals (dialog);
498 /* Create the structure */
500 psppire_var_type_dialog_constructor (GType type,
502 GObjectConstructParam *properties)
504 PsppireVarTypeDialog *dialog;
505 GtkContainer *content_area;
510 obj = G_OBJECT_CLASS (psppire_var_type_dialog_parent_class)->constructor (
511 type, n_properties, properties);
512 dialog = PSPPIRE_VAR_TYPE_DIALOG (obj);
514 xml = builder_new ("var-type-dialog.ui");
516 content_area = GTK_CONTAINER (PSPPIRE_DIALOG (dialog)->box);
517 gtk_container_add (GTK_CONTAINER (content_area),
518 get_widget_assert (xml, "var-type-dialog"));
520 dialog->active_button = -1;
522 g_signal_connect (dialog, "delete-event",
523 G_CALLBACK (gtk_widget_hide_on_delete), NULL);
525 dialog->radioButton[BUTTON_NUMERIC] =
526 get_widget_assert (xml,"radiobutton1");
527 dialog->radioButton[BUTTON_COMMA] =
528 get_widget_assert (xml,"radiobutton2");
529 dialog->radioButton[BUTTON_DOT] =
530 get_widget_assert (xml,"radiobutton3");
531 dialog->radioButton[BUTTON_SCIENTIFIC] =
532 get_widget_assert (xml,"radiobutton4");
533 dialog->radioButton[BUTTON_DATE] =
534 get_widget_assert (xml,"radiobutton5");
535 dialog->radioButton[BUTTON_DOLLAR] =
536 get_widget_assert (xml,"radiobutton6");
537 dialog->radioButton[BUTTON_CUSTOM] =
538 get_widget_assert (xml,"radiobutton7");
539 dialog->radioButton[BUTTON_STRING] =
540 get_widget_assert (xml,"radiobutton8");
543 dialog->date_format_list = get_widget_assert (xml, "scrolledwindow4");
544 dialog->width_decimals = get_widget_assert (xml, "width_decimals");
545 dialog->label_decimals = get_widget_assert (xml, "decimals_label");
546 dialog->entry_decimals = get_widget_assert (xml, "decimals_entry");
547 dialog->adj_decimals = gtk_spin_button_get_adjustment (
548 GTK_SPIN_BUTTON (dialog->entry_decimals));
550 dialog->label_psample = get_widget_assert (xml, "psample_label");
551 dialog->label_nsample = get_widget_assert (xml, "nsample_label");
554 dialog->entry_width = get_widget_assert (xml,"width_entry");
555 dialog->adj_width = gtk_spin_button_get_adjustment (
556 GTK_SPIN_BUTTON (dialog->entry_width));
557 dialog->custom_currency_hbox = get_widget_assert (xml,
558 "custom_currency_hbox");
560 dialog->dollar_window = get_widget_assert (xml, "dollar_window");
561 dialog->dollar_treeview =
562 GTK_TREE_VIEW (get_widget_assert (xml, "dollar_treeview"));
564 dialog->custom_treeview =
565 GTK_TREE_VIEW (get_widget_assert (xml, "custom_treeview"));
571 GtkListStore *list_store ;
573 GtkTreeViewColumn *column;
574 GtkCellRenderer *renderer ;
576 /* The "middle_box" is a vbox with serveral children.
577 However only one child is ever shown at a time.
578 We need to make sure that they all have the same width, to avoid
579 upleasant resizing effects */
580 GtkSizeGroup *sizeGroup = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
582 gtk_container_foreach (GTK_CONTAINER (get_widget_assert (xml, "middle_box")),
583 add_to_group, sizeGroup);
586 for (i = 0 ; i < num_BUTTONS; ++i )
587 g_signal_connect (dialog->radioButton[i], "toggled",
588 G_CALLBACK (on_toggle), dialog);
590 /* Populate the date format tree view */
591 dialog->date_format_treeview = GTK_TREE_VIEW (get_widget_assert (xml,
592 "date_format_list_view"));
594 renderer = gtk_cell_renderer_text_new ();
596 column = gtk_tree_view_column_new_with_attributes ("Title",
602 gtk_tree_view_append_column (GTK_TREE_VIEW (dialog->date_format_treeview),
606 list_store = gtk_list_store_new (1, G_TYPE_STRING);
608 for ( i = 0 ; i < sizeof (date_format) / sizeof (date_format[0]) ; ++i )
610 const struct fmt_spec *f = &date_format[i];
611 gtk_list_store_append (list_store, &iter);
612 gtk_list_store_set (list_store, &iter,
613 0, fmt_date_template (f->type, f->w),
617 gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->date_format_treeview),
618 GTK_TREE_MODEL (list_store));
620 g_object_unref (list_store);
622 g_signal_connect (dialog->date_format_treeview, "cursor-changed",
623 G_CALLBACK (set_date_format_from_treeview), dialog);
626 /* populate the dollar treeview */
628 renderer = gtk_cell_renderer_text_new ();
630 column = gtk_tree_view_column_new_with_attributes ("Title",
636 gtk_tree_view_append_column (GTK_TREE_VIEW (dialog->dollar_treeview),
640 list_store = gtk_list_store_new (1, G_TYPE_STRING);
642 for ( i = 0 ; i < sizeof (dollar_format)/sizeof (dollar_format[0]) ; ++i )
644 char *template = settings_dollar_template (&dollar_format[i]);
645 gtk_list_store_append (list_store, &iter);
646 gtk_list_store_set (list_store, &iter,
652 gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->dollar_treeview),
653 GTK_TREE_MODEL (list_store));
655 g_object_unref (list_store);
657 g_signal_connect (dialog->dollar_treeview,
659 G_CALLBACK (set_dollar_format_from_treeview), dialog);
661 g_signal_connect_swapped (dialog->dollar_treeview,
663 G_CALLBACK (update_width_decimals), dialog);
666 /* populate the custom treeview */
668 renderer = gtk_cell_renderer_text_new ();
670 column = gtk_tree_view_column_new_with_attributes ("Title",
676 gtk_tree_view_append_column (GTK_TREE_VIEW (dialog->custom_treeview),
680 list_store = gtk_list_store_new (1, G_TYPE_STRING);
682 for ( i = 0 ; i < 5 ; ++i )
684 enum fmt_type cc_fmts[5] = {FMT_CCA, FMT_CCB, FMT_CCC, FMT_CCD, FMT_CCE};
685 gtk_list_store_append (list_store, &iter);
686 gtk_list_store_set (list_store, &iter,
687 0, fmt_name (cc_fmts[i]),
691 gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->custom_treeview),
692 GTK_TREE_MODEL (list_store));
694 g_object_unref (list_store);
697 g_signal_connect (dialog->custom_treeview,
699 G_CALLBACK (set_custom_format_from_treeview), dialog);
702 g_signal_connect (dialog->custom_treeview,
704 G_CALLBACK (preview_custom), dialog);
707 g_signal_connect (dialog->entry_width, "changed",
708 G_CALLBACK (on_width_changed), dialog);
709 g_signal_connect (dialog->entry_decimals, "changed",
710 G_CALLBACK (on_decimals_changed), dialog);
712 g_signal_connect (dialog->entry_width,
714 G_CALLBACK (preview_custom), dialog);
717 g_signal_connect (dialog->entry_decimals,
719 G_CALLBACK (preview_custom), dialog);
723 g_object_unref (xml);
725 psppire_var_type_dialog_set_state (dialog);
731 /* Set a particular button to be active */
733 var_type_dialog_set_active_button (PsppireVarTypeDialog *dialog, gint b)
735 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dialog->radioButton[b]),
742 select_treeview_at_index (GtkTreeView *treeview, int index)
746 path = gtk_tree_path_new_from_indices (index, -1);
747 gtk_tree_view_set_cursor (treeview, path, 0, 0);
748 gtk_tree_path_free (path);
752 find_format (const struct fmt_spec *target,
753 const struct fmt_spec formats[], int n_formats)
757 for (i = 0; i < n_formats; i++)
758 if (fmt_equal (target, &formats[i]))
765 find_format_type (int target, const int types[], int n_types)
769 for (i = 0; i < n_types; i++)
770 if (target == types[i])
776 /* Set up the state of the dialog box to match the variable VAR */
778 psppire_var_type_dialog_set_state (PsppireVarTypeDialog *dialog)
782 g_return_if_fail (dialog != NULL);
784 /* Populate the radio button states */
785 switch (dialog->base_format.type)
789 button = BUTTON_NUMERIC;
792 button = BUTTON_STRING;
795 button = BUTTON_COMMA;
801 button = BUTTON_DOLLAR;
816 button = BUTTON_DATE;
823 button = BUTTON_CUSTOM;
827 var_type_dialog_set_active_button (dialog, button);
828 refresh_active_button (dialog);
829 on_active_button_change (GTK_TOGGLE_BUTTON (dialog->radioButton[button]),