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