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