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