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