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