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