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