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