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