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