treewide: Use struct fmt_spec by value instead of pointer in most cases.
[pspp] / src / ui / gui / var-type-dialog.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2005, 2006, 2010, 2011, 2012, 2015, 2020  Free Software Foundation
3
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.
8
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.
13
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/>. */
16
17
18 /*  This module describes the behaviour of the Variable Type dialog box used
19     for inputing the variable type in the var sheet */
20
21 #include <config.h>
22
23 #include <gtk/gtk.h>
24 #include <stdlib.h>
25 #include <string.h>
26
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"
34
35 #include <gettext.h>
36 #define _(msgid) gettext (msgid)
37 #define N_(msgid) msgid
38
39 static const struct fmt_spec date_format[] =
40   {
41     { .type = FMT_DATE, .w = 11, .d = 0 },
42     { .type = FMT_DATE, .w = 9, .d = 0 },
43     { .type = FMT_ADATE, .w = 10, .d = 0 },
44     { .type = FMT_ADATE, .w = 8, .d = 0 },
45     { .type = FMT_EDATE, .w = 10, .d = 0 },
46     { .type = FMT_EDATE, .w = 8, .d = 0 },
47     { .type = FMT_SDATE, .w = 10, .d = 0 },
48     { .type = FMT_SDATE, .w = 8, .d = 0 },
49     { .type = FMT_JDATE, .w = 5, .d = 0 },
50     { .type = FMT_JDATE, .w = 7, .d = 0 },
51     { .type = FMT_QYR, .w = 8, .d = 0 },
52     { .type = FMT_QYR, .w = 6, .d = 0 },
53     { .type = FMT_MOYR, .w = 8, .d = 0 },
54     { .type = FMT_MOYR, .w = 6, .d = 0 },
55     { .type = FMT_WKYR, .w = 10, .d = 0 },
56     { .type = FMT_WKYR, .w = 8, .d = 0 },
57     { .type = FMT_DATETIME, .w = 17, .d = 0 },
58     { .type = FMT_DATETIME, .w = 20, .d = 0 },
59     { .type = FMT_YMDHMS, .w = 16, .d = 0 },
60     { .type = FMT_YMDHMS, .w = 20, .d = 0 }
61   };
62
63
64 static const struct fmt_spec dollar_format[] =
65   {
66     { .type = FMT_DOLLAR, .w = 2, .d = 0 },
67     { .type = FMT_DOLLAR, .w = 3, .d = 0 },
68     { .type = FMT_DOLLAR, .w = 4, .d = 0 },
69     { .type = FMT_DOLLAR, .w = 7, .d = 2 },
70     { .type = FMT_DOLLAR, .w = 6, .d = 0 },
71     { .type = FMT_DOLLAR, .w = 9, .d = 2 },
72     { .type = FMT_DOLLAR, .w = 8, .d = 0 },
73     { .type = FMT_DOLLAR, .w = 11, .d = 2 },
74     { .type = FMT_DOLLAR, .w = 12, .d = 0 },
75     { .type = FMT_DOLLAR, .w = 15, .d = 2 },
76     { .type = FMT_DOLLAR, .w = 16, .d = 0 },
77     { .type = FMT_DOLLAR, .w = 19, .d = 2 }
78   };
79
80 static const int cc_format[] =
81   {
82     FMT_CCA,
83     FMT_CCB,
84     FMT_CCC,
85     FMT_CCD,
86     FMT_CCE,
87   };
88
89 static GObject *psppire_var_type_dialog_constructor (GType type, guint,
90                                                      GObjectConstructParam *);
91 static void psppire_var_type_dialog_set_state (PsppireVarTypeDialog *);
92
93 static void psppire_var_type_dialog_set_format (PsppireVarTypeDialog *dialog,
94                                                 struct fmt_spec format);
95
96 static int find_format (struct fmt_spec target,
97                         const struct fmt_spec formats[], int n_formats);
98 static int find_format_type (int target, const int types[], int n_types);
99
100 static void select_treeview_at_index (GtkTreeView *, int index);
101
102 static void update_width_decimals (const PsppireVarTypeDialog *);
103 static void refresh_active_button (PsppireVarTypeDialog *);
104 static void on_active_button_change (GtkToggleButton *,
105                                      PsppireVarTypeDialog *);
106 static void on_width_changed (GtkEntry *, PsppireVarTypeDialog *);
107 static void on_decimals_changed (GtkEntry *, PsppireVarTypeDialog *);
108
109 G_DEFINE_TYPE (PsppireVarTypeDialog,
110                psppire_var_type_dialog,
111                PSPPIRE_TYPE_DIALOG);
112
113 enum
114   {
115     PROP_0,
116     PROP_FORMAT
117   };
118
119 static void
120 psppire_var_type_dialog_set_property (GObject      *object,
121                                       guint         prop_id,
122                                       const GValue *value,
123                                       GParamSpec   *pspec)
124 {
125   PsppireVarTypeDialog *obj = PSPPIRE_VAR_TYPE_DIALOG (object);
126
127   switch (prop_id)
128     {
129     case PROP_FORMAT:
130       {
131         const struct fmt_spec *f = g_value_get_boxed (value);
132         psppire_var_type_dialog_set_format (obj, *f);
133       }
134       break;
135     default:
136       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
137       break;
138     }
139 }
140
141 static void
142 psppire_var_type_dialog_get_property (GObject      *object,
143                                       guint         prop_id,
144                                       GValue       *value,
145                                       GParamSpec   *pspec)
146 {
147   PsppireVarTypeDialog *obj = PSPPIRE_VAR_TYPE_DIALOG (object);
148
149   switch (prop_id)
150     {
151     case PROP_FORMAT:
152       g_value_set_boxed (value, &obj->fmt_l);
153       break;
154     default:
155       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
156       break;
157     }
158 }
159
160 static void
161 psppire_var_type_dialog_set_format (PsppireVarTypeDialog *dialog,
162                                     struct fmt_spec format)
163 {
164   dialog->base_format = format;
165   psppire_var_type_dialog_set_state (dialog);
166 }
167
168 static struct fmt_spec
169 psppire_var_type_dialog_get_format (const PsppireVarTypeDialog *dialog)
170 {
171   return dialog->fmt_l;
172 }
173
174 static void
175 psppire_var_type_dialog_init (PsppireVarTypeDialog *obj)
176 {
177   /* We do all of our work on widgets in the constructor function, because that
178      runs after the construction properties have been set.  Otherwise
179      PsppireDialog's "orientation" property hasn't been set and therefore we
180      have no box to populate. */
181   obj->base_format = F_8_0;
182   obj->fmt_l = F_8_0;
183 }
184
185 static void
186 psppire_var_type_dialog_class_init (PsppireVarTypeDialogClass *class)
187 {
188   GObjectClass *gobject_class;
189   gobject_class = G_OBJECT_CLASS (class);
190
191   gobject_class->constructor = psppire_var_type_dialog_constructor;
192   gobject_class->set_property = psppire_var_type_dialog_set_property;
193   gobject_class->get_property = psppire_var_type_dialog_get_property;
194
195   g_object_class_install_property (
196     gobject_class, PROP_FORMAT,
197     g_param_spec_boxed ("format",
198                         "Format",
199                         "The format being edited.",
200                         PSPPIRE_TYPE_FORMAT,
201                         G_PARAM_READABLE | G_PARAM_WRITABLE));
202 }
203
204 PsppireVarTypeDialog *
205 psppire_var_type_dialog_new (const struct fmt_spec *format)
206 {
207   return PSPPIRE_VAR_TYPE_DIALOG (
208     g_object_new (PSPPIRE_TYPE_VAR_TYPE_DIALOG,
209                   "format", format,
210                   NULL));
211 }
212
213 gint
214 psppire_var_type_dialog_run (GtkWindow *parent_window,
215                              struct fmt_spec *format)
216 {
217   PsppireVarTypeDialog *dialog;
218
219   dialog = psppire_var_type_dialog_new (format);
220   gtk_window_set_transient_for (GTK_WINDOW (dialog), parent_window);
221   gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
222   gtk_widget_show (GTK_WIDGET (dialog));
223
224   gint result = psppire_dialog_run (PSPPIRE_DIALOG (dialog));
225   if (result == GTK_RESPONSE_OK)
226     *format = psppire_var_type_dialog_get_format (dialog);
227
228   gtk_widget_destroy (GTK_WIDGET (dialog));
229
230   return result;
231 }
232
233
234 /* callback for when any of the radio buttons are toggled */
235 static void
236 on_toggle (GtkToggleButton *togglebutton, gpointer dialog_)
237 {
238   PsppireVarTypeDialog *dialog = dialog_;
239
240   if (gtk_toggle_button_get_active (togglebutton) == TRUE)
241     refresh_active_button (dialog);
242 }
243
244 static void
245 refresh_active_button (PsppireVarTypeDialog *dialog)
246 {
247   int i;
248
249   for (i = 0; i < num_BUTTONS; i++)
250     {
251       GtkToggleButton *toggle = GTK_TOGGLE_BUTTON (dialog->radioButton[i]);
252
253       if (gtk_toggle_button_get_active (toggle))
254         {
255           if (dialog->active_button != i)
256             {
257               dialog->active_button = i;
258               on_active_button_change (toggle, dialog);
259             }
260           return;
261         }
262     }
263
264   g_return_if_reached ();
265 }
266
267 static void
268 update_adj_ranges (PsppireVarTypeDialog *dialog)
269 {
270   enum fmt_type type = dialog->fmt_l.type;
271   const enum fmt_use use = FMT_FOR_OUTPUT;
272   int min_w = fmt_min_width (type, use);
273   int max_w = fmt_max_width (type, use);
274   int max_d = fmt_max_decimals (type, max_w, use);
275
276   g_object_set (dialog->adj_width,
277                 "lower", (double) min_w,
278                 "upper", (double) max_w,
279                 NULL);
280
281   g_object_set (dialog->adj_decimals,
282                 "lower", 0.0,
283                 "upper", (double) max_d,
284                 NULL);
285 }
286
287 /* callback for when any of the radio buttons are toggled */
288 static void
289 on_active_button_change (GtkToggleButton *togglebutton,
290                          PsppireVarTypeDialog *dialog)
291 {
292   enum widgets {
293     W_WIDTH          = 1 << 0,
294     W_DECIMALS       = 1 << 1,
295     W_DATE_FORMATS   = 1 << 2,
296     W_DOLLAR_FORMATS = 1 << 3,
297     W_CC_FORMATS     = 1 << 4,
298   };
299
300   enum widgets widgets;
301   int indx;
302
303   switch (dialog->active_button)
304     {
305     case BUTTON_NUMERIC:
306     case BUTTON_COMMA:
307     case BUTTON_DOT:
308     case BUTTON_SCIENTIFIC:
309       widgets = W_WIDTH | W_DECIMALS;
310       break;
311
312     case BUTTON_STRING:
313       widgets = W_WIDTH;
314       break;
315
316     case BUTTON_DATE:
317       widgets = W_DATE_FORMATS;
318       break;
319
320     case BUTTON_DOLLAR:
321       widgets = W_DOLLAR_FORMATS;
322       break;
323
324     case BUTTON_CUSTOM:
325       widgets = W_CC_FORMATS | W_WIDTH | W_DECIMALS;
326       break;
327
328     default:
329       /* No button active */
330       return;
331     }
332
333   gtk_widget_set_visible (dialog->width_decimals, (widgets & W_WIDTH) != 0);
334   gtk_widget_set_visible (dialog->entry_width, (widgets & W_WIDTH) != 0);
335   gtk_widget_set_visible (dialog->entry_decimals, (widgets & W_DECIMALS) != 0);
336   gtk_widget_set_visible (dialog->label_decimals, (widgets & W_DECIMALS) != 0);
337   gtk_widget_set_visible (dialog->date_format_list,
338                           (widgets & W_DATE_FORMATS) != 0);
339   gtk_widget_set_visible (dialog->custom_currency_hbox,
340                           (widgets & W_CC_FORMATS) != 0);
341   gtk_widget_set_visible (dialog->dollar_window,
342                           (widgets & W_DOLLAR_FORMATS) != 0);
343
344   dialog->fmt_l = dialog->base_format;
345
346   switch (dialog->active_button)
347     {
348     case BUTTON_NUMERIC:
349       dialog->fmt_l.type = FMT_F;
350       break;
351     case BUTTON_COMMA:
352       dialog->fmt_l.type = FMT_COMMA;
353       break;
354     case BUTTON_DOT:
355       dialog->fmt_l.type = FMT_DOT;
356       break;
357     case BUTTON_SCIENTIFIC:
358       dialog->fmt_l.type = FMT_E;
359       break;
360     case BUTTON_STRING:
361       dialog->fmt_l.type = FMT_A;
362       break;
363     case BUTTON_DATE:
364       indx = find_format (dialog->fmt_l, date_format,
365                           sizeof date_format / sizeof *date_format);
366       select_treeview_at_index (dialog->date_format_treeview, indx);
367       dialog->fmt_l = date_format[indx];
368       break;
369     case BUTTON_DOLLAR:
370       indx = find_format (dialog->fmt_l, dollar_format,
371                           sizeof dollar_format / sizeof *dollar_format);
372       select_treeview_at_index (dialog->dollar_treeview, indx);
373       dialog->fmt_l = dollar_format[indx];
374       break;
375     case BUTTON_CUSTOM:
376       indx = find_format_type (dialog->fmt_l.type, cc_format,
377                                sizeof cc_format / sizeof *cc_format);
378       select_treeview_at_index (dialog->custom_treeview, indx);
379       dialog->fmt_l.type = cc_format[indx];
380       break;
381     }
382
383   fmt_fix_output (&dialog->fmt_l);
384   update_adj_ranges (dialog);
385   update_width_decimals (dialog);
386 }
387
388 static void
389 add_to_group (GtkWidget *w, gpointer data)
390 {
391   GtkSizeGroup *sg = data;
392
393   gtk_size_group_add_widget (sg, w);
394 }
395
396 /* Set the local width and decimals entry boxes to reflec the local format */
397 static void
398 update_width_decimals (const PsppireVarTypeDialog *dialog)
399 {
400   gtk_adjustment_set_value (dialog->adj_width, dialog->fmt_l.w);
401   gtk_adjustment_set_value (dialog->adj_decimals, dialog->fmt_l.d);
402 }
403
404 static void
405 on_width_changed (GtkEntry *entry, PsppireVarTypeDialog *dialog)
406 {
407   int w = atoi (gtk_entry_get_text (GTK_ENTRY (dialog->entry_width)));
408   fmt_change_width (&dialog->fmt_l, w, FMT_FOR_OUTPUT);
409   update_width_decimals (dialog);
410 }
411
412 static void
413 on_decimals_changed (GtkEntry *entry, PsppireVarTypeDialog *dialog)
414 {
415   int d = atoi (gtk_entry_get_text (GTK_ENTRY (dialog->entry_decimals)));
416   fmt_change_decimals (&dialog->fmt_l, d, FMT_FOR_OUTPUT);
417   update_width_decimals (dialog);
418 }
419
420 /* Callback for when the custom treeview row is changed.
421    It sets dialog box to reflect the selected format */
422 static void
423 preview_custom (GtkWidget *w, gpointer data)
424 {
425   const gchar *text ;
426
427   PsppireVarTypeDialog *dialog = data;
428
429   if (dialog->active_button != BUTTON_CUSTOM)
430     return;
431
432   text = gtk_entry_get_text (GTK_ENTRY (dialog->entry_decimals));
433   dialog->fmt_l.d = atoi (text);
434
435   text = gtk_entry_get_text (GTK_ENTRY (dialog->entry_width));
436   dialog->fmt_l.w = atoi (text);
437
438   if (! fmt_check_output (dialog->fmt_l))
439     {
440       gtk_label_set_text (GTK_LABEL (dialog->label_psample), "---");
441       gtk_label_set_text (GTK_LABEL (dialog->label_nsample), "---");
442     }
443   else
444     {
445       gchar *sample_text;
446       union value v;
447       v.f = 1234.56;
448
449       sample_text = g_strchug (data_out (&v, NULL, dialog->fmt_l,
450                                          settings_get_fmt_settings ()));
451       gtk_label_set_text (GTK_LABEL (dialog->label_psample), sample_text);
452       g_free (sample_text);
453
454       v.f = -v.f;
455       sample_text = g_strchug (data_out (&v, NULL, dialog->fmt_l,
456                                          settings_get_fmt_settings ()));
457       gtk_label_set_text (GTK_LABEL (dialog->label_nsample), sample_text);
458       g_free (sample_text);
459     }
460 }
461
462 static gint
463 get_index_from_treeview (GtkTreeView *treeview)
464 {
465   GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview);
466   GtkTreeModel *model;
467   GtkTreePath *path;
468   GtkTreeIter iter;
469   gint index;
470
471   if (selection == NULL)
472     return -1;
473
474   gtk_tree_selection_get_selected (selection, &model, &iter);
475   path = gtk_tree_model_get_path (model, &iter);
476   if (!path || gtk_tree_path_get_depth (path) < 1)
477     index = 0;
478   else
479     index = gtk_tree_path_get_indices (path)[0];
480   gtk_tree_path_free (path);
481
482   return index;
483 }
484
485 /* Callback for when a date treeview row is changed.
486    It sets the fmt_l_spec to reflect the selected format */
487 static void
488 set_date_format_from_treeview (GtkTreeView *treeview,
489                                PsppireVarTypeDialog *dialog)
490 {
491   gint idx = get_index_from_treeview (treeview);
492   if (idx < 0)
493     return;
494
495   dialog->fmt_l = date_format[idx];
496 }
497
498 /* Callback for when a dollar treeview row is changed.
499    It sets the fmt_l_spec to reflect the selected format */
500 static void
501 set_dollar_format_from_treeview (GtkTreeView *treeview,
502                                  PsppireVarTypeDialog *dialog)
503 {
504   gint idx = get_index_from_treeview (treeview);
505   if (idx < 0)
506     return;
507
508   dialog->fmt_l = dollar_format[idx];
509 }
510
511 /* Callback for when a treeview row is changed.
512    It sets the type of the fmt_l to reflect the selected type */
513 static void
514 set_custom_format_from_treeview (GtkTreeView *treeview,
515                                  PsppireVarTypeDialog *dialog)
516 {
517   gint idx = get_index_from_treeview (treeview);
518   if (idx < 0)
519     return;
520
521   dialog->fmt_l.type = cc_format[idx];
522   update_adj_ranges (dialog);
523   fmt_fix_output (&dialog->fmt_l);
524   update_width_decimals (dialog);
525 }
526
527 /* Create the structure */
528 static GObject *
529 psppire_var_type_dialog_constructor (GType                  type,
530                                      guint                  n_properties,
531                                      GObjectConstructParam *properties)
532 {
533   PsppireVarTypeDialog *dialog;
534   GtkContainer *content_area;
535   GtkBuilder *xml;
536   GObject *obj;
537   gint i;
538
539   obj = G_OBJECT_CLASS (psppire_var_type_dialog_parent_class)->constructor (
540     type, n_properties, properties);
541   dialog = PSPPIRE_VAR_TYPE_DIALOG (obj);
542
543   g_object_set (dialog, "help-page", "Input-and-Output-Formats",
544                 "title", _("Variable Type and Format"), NULL);
545
546   xml = builder_new ("var-type-dialog.ui");
547
548   content_area = GTK_CONTAINER (PSPPIRE_DIALOG (dialog));
549   gtk_container_add (GTK_CONTAINER (content_area),
550                      get_widget_assert (xml, "var-type-dialog"));
551
552   dialog->active_button = -1;
553
554   g_signal_connect (dialog, "delete-event",
555                     G_CALLBACK (gtk_widget_hide_on_delete), NULL);
556
557   dialog->radioButton[BUTTON_NUMERIC] =
558     get_widget_assert (xml,"radiobutton1");
559   dialog->radioButton[BUTTON_COMMA] =
560     get_widget_assert (xml,"radiobutton2");
561   dialog->radioButton[BUTTON_DOT] =
562     get_widget_assert (xml,"radiobutton3");
563   dialog->radioButton[BUTTON_SCIENTIFIC] =
564     get_widget_assert (xml,"radiobutton4");
565   dialog->radioButton[BUTTON_DATE] =
566     get_widget_assert (xml,"radiobutton5");
567   dialog->radioButton[BUTTON_DOLLAR] =
568     get_widget_assert (xml,"radiobutton6");
569   dialog->radioButton[BUTTON_CUSTOM] =
570     get_widget_assert (xml,"radiobutton7");
571   dialog->radioButton[BUTTON_STRING] =
572     get_widget_assert (xml,"radiobutton8");
573
574
575   dialog->date_format_list = get_widget_assert (xml, "scrolledwindow4");
576   dialog->width_decimals = get_widget_assert (xml, "width_decimals");
577   dialog->label_decimals = get_widget_assert (xml, "decimals_label");
578   dialog->entry_decimals = get_widget_assert (xml, "decimals_entry");
579   dialog->adj_decimals = gtk_spin_button_get_adjustment (
580     GTK_SPIN_BUTTON (dialog->entry_decimals));
581
582   dialog->label_psample = get_widget_assert (xml, "psample_label");
583   dialog->label_nsample = get_widget_assert (xml, "nsample_label");
584
585
586   dialog->entry_width = get_widget_assert (xml,"width_entry");
587   dialog->adj_width = gtk_spin_button_get_adjustment (
588     GTK_SPIN_BUTTON (dialog->entry_width));
589   dialog->custom_currency_hbox = get_widget_assert (xml,
590                                                    "custom_currency_hbox");
591
592   dialog->dollar_window = get_widget_assert (xml, "dollar_window");
593   dialog->dollar_treeview =
594     GTK_TREE_VIEW (get_widget_assert (xml, "dollar_treeview"));
595
596   dialog->custom_treeview =
597     GTK_TREE_VIEW (get_widget_assert (xml, "custom_treeview"));
598
599
600
601   {
602   GtkTreeIter iter;
603   GtkListStore *list_store ;
604
605   GtkTreeViewColumn *column;
606   GtkCellRenderer *renderer ;
607
608   /* The "middle_box" is a vbox with serveral children.
609      However only one child is ever shown at a time.
610      We need to make sure that they all have the same width, to avoid
611      upleasant resizing effects */
612   GtkSizeGroup *sizeGroup = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
613
614   gtk_container_foreach (GTK_CONTAINER (get_widget_assert (xml, "middle_box")),
615                         add_to_group, sizeGroup);
616
617
618   for (i = 0 ; i < num_BUTTONS; ++i)
619     g_signal_connect (dialog->radioButton[i], "toggled",
620                       G_CALLBACK (on_toggle), dialog);
621
622   /* Populate the date format tree view */
623   dialog->date_format_treeview = GTK_TREE_VIEW (get_widget_assert (xml,
624                                               "date_format_list_view"));
625
626   renderer = gtk_cell_renderer_text_new ();
627
628   column = gtk_tree_view_column_new_with_attributes ("Title",
629                                                      renderer,
630                                                      "text",
631                                                      0,
632                                                      NULL);
633
634   gtk_tree_view_append_column (GTK_TREE_VIEW (dialog->date_format_treeview),
635                                column);
636
637
638   list_store = gtk_list_store_new (1, G_TYPE_STRING);
639
640   for (i = 0 ; i < sizeof (date_format) / sizeof (date_format[0]) ; ++i)
641     {
642       struct fmt_spec f = date_format[i];
643       gtk_list_store_append (list_store, &iter);
644       gtk_list_store_set (list_store, &iter,
645                           0, fmt_date_template (f.type, f.w),
646                           -1);
647     }
648
649   gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->date_format_treeview),
650                           GTK_TREE_MODEL (list_store));
651
652   g_object_unref (list_store);
653
654   g_signal_connect (dialog->date_format_treeview, "cursor-changed",
655                    G_CALLBACK (set_date_format_from_treeview), dialog);
656
657
658   /* populate the dollar treeview */
659
660   renderer = gtk_cell_renderer_text_new ();
661
662   column = gtk_tree_view_column_new_with_attributes ("Title",
663                                                      renderer,
664                                                      "text",
665                                                      0,
666                                                      NULL);
667
668   gtk_tree_view_append_column (GTK_TREE_VIEW (dialog->dollar_treeview),
669                                column);
670
671
672   list_store = gtk_list_store_new (1, G_TYPE_STRING);
673
674   for (i = 0 ; i < sizeof (dollar_format)/sizeof (dollar_format[0]) ; ++i)
675     {
676       char *template = settings_dollar_template (dollar_format[i]);
677       gtk_list_store_append (list_store, &iter);
678       gtk_list_store_set (list_store, &iter,
679                           0, template,
680                           -1);
681       free (template);
682     }
683
684   gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->dollar_treeview),
685                           GTK_TREE_MODEL (list_store));
686
687   g_object_unref (list_store);
688
689   g_signal_connect (dialog->dollar_treeview,
690                    "cursor-changed",
691                    G_CALLBACK (set_dollar_format_from_treeview), dialog);
692
693   g_signal_connect_swapped (dialog->dollar_treeview,
694                    "cursor-changed",
695                    G_CALLBACK (update_width_decimals), dialog);
696
697
698   /* populate the custom treeview */
699
700   renderer = gtk_cell_renderer_text_new ();
701
702   column = gtk_tree_view_column_new_with_attributes ("Title",
703                                                      renderer,
704                                                      "text",
705                                                      0,
706                                                      NULL);
707
708   gtk_tree_view_append_column (GTK_TREE_VIEW (dialog->custom_treeview),
709                                column);
710
711
712   list_store = gtk_list_store_new (1, G_TYPE_STRING);
713
714   for (i = 0 ; i < 5 ; ++i)
715     {
716       enum fmt_type cc_fmts[5] = {FMT_CCA, FMT_CCB, FMT_CCC, FMT_CCD, FMT_CCE};
717       gtk_list_store_append (list_store, &iter);
718       gtk_list_store_set (list_store, &iter,
719                           0, fmt_name (cc_fmts[i]),
720                           -1);
721     }
722
723   gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->custom_treeview),
724                           GTK_TREE_MODEL (list_store));
725
726   g_object_unref (list_store);
727
728
729   g_signal_connect (dialog->custom_treeview,
730                    "cursor-changed",
731                    G_CALLBACK (set_custom_format_from_treeview), dialog);
732
733
734   g_signal_connect (dialog->custom_treeview,
735                    "cursor-changed",
736                    G_CALLBACK (preview_custom), dialog);
737
738
739   g_signal_connect (dialog->entry_width, "changed",
740                     G_CALLBACK (on_width_changed), dialog);
741   g_signal_connect (dialog->entry_decimals, "changed",
742                     G_CALLBACK (on_decimals_changed), dialog);
743
744   g_signal_connect (dialog->entry_width,
745                    "changed",
746                    G_CALLBACK (preview_custom), dialog);
747
748
749   g_signal_connect (dialog->entry_decimals,
750                    "changed",
751                    G_CALLBACK (preview_custom), dialog);
752
753   }
754
755   g_object_unref (xml);
756
757   psppire_var_type_dialog_set_state (dialog);
758
759   return obj;
760 }
761
762
763 /* Set a particular button to be active */
764 static void
765 var_type_dialog_set_active_button (PsppireVarTypeDialog *dialog, gint b)
766 {
767   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dialog->radioButton[b]),
768                                TRUE);
769 }
770
771
772
773 static void
774 select_treeview_at_index (GtkTreeView *treeview, int index)
775 {
776   GtkTreePath *path;
777
778   path = gtk_tree_path_new_from_indices (index, -1);
779   gtk_tree_view_set_cursor (treeview, path, 0, 0);
780   gtk_tree_path_free (path);
781 }
782
783 static int
784 find_format (struct fmt_spec target,
785              const struct fmt_spec formats[], int n_formats)
786 {
787   int i;
788
789   for (i = 0; i < n_formats; i++)
790     if (fmt_equal (target, formats[i]))
791       return i;
792
793   return 0;
794 }
795
796 static int
797 find_format_type (int target, const int types[], int n_types)
798 {
799   int i;
800
801   for (i = 0; i < n_types; i++)
802     if (target == types[i])
803       return i;
804
805   return 0;
806 }
807
808 /* Set up the state of the dialog box to match the variable VAR */
809 static void
810 psppire_var_type_dialog_set_state (PsppireVarTypeDialog *dialog)
811 {
812   int button;
813
814   g_return_if_fail (dialog != NULL);
815
816   /* Populate the radio button states */
817   switch (dialog->base_format.type)
818     {
819     default:
820     case FMT_F:
821       button = BUTTON_NUMERIC;
822       break;
823     case FMT_A:
824       button = BUTTON_STRING;
825       break;
826     case FMT_COMMA:
827       button = BUTTON_COMMA;
828       break;
829     case FMT_DOT:
830       button = BUTTON_DOT;
831       break;
832     case FMT_DOLLAR:
833       button = BUTTON_DOLLAR;
834       break;
835     case FMT_DATE:
836     case FMT_EDATE:
837     case FMT_SDATE:
838     case FMT_ADATE:
839     case FMT_JDATE:
840     case FMT_QYR:
841     case FMT_MOYR:
842     case FMT_WKYR:
843     case FMT_DATETIME:
844     case FMT_YMDHMS:
845     case FMT_MTIME:
846     case FMT_TIME:
847     case FMT_DTIME:
848     case FMT_WKDAY:
849     case FMT_MONTH:
850       button = BUTTON_DATE;
851       break;
852     case FMT_CCA:
853     case FMT_CCB:
854     case FMT_CCC:
855     case FMT_CCD:
856     case FMT_CCE:
857       button = BUTTON_CUSTOM;
858       break;
859     }
860
861   var_type_dialog_set_active_button (dialog, button);
862   refresh_active_button (dialog);
863   on_active_button_change (GTK_TOGGLE_BUTTON (dialog->radioButton[button]),
864                            dialog);
865 }