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