Put the pages as pointers
[pspp] / src / ui / gui / page-formats.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013  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 #include <config.h>
18
19 #include "ui/gui/text-data-import-dialog.h"
20
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <gtk-contrib/psppire-sheet.h>
24 #include <gtk/gtk.h>
25 #include <limits.h>
26 #include <stdlib.h>
27 #include <sys/stat.h>
28
29 #include "data/data-in.h"
30 #include "data/data-out.h"
31 #include "data/format-guesser.h"
32 #include "data/value-labels.h"
33 #include "language/data-io/data-parser.h"
34 #include "language/lexer/lexer.h"
35 #include "libpspp/assertion.h"
36 #include "libpspp/i18n.h"
37 #include "libpspp/line-reader.h"
38 #include "libpspp/message.h"
39 #include "ui/gui/checkbox-treeview.h"
40 #include "ui/gui/dialog-common.h"
41 #include "ui/gui/executor.h"
42 #include "ui/gui/helper.h"
43 #include "ui/gui/builder-wrapper.h"
44 #include "ui/gui/psppire-data-window.h"
45 #include "ui/gui/psppire-dialog.h"
46 #include "ui/gui/psppire-encoding-selector.h"
47 #include "ui/gui/psppire-empty-list-store.h"
48 #include "ui/gui/psppire-var-sheet.h"
49 #include "ui/gui/psppire-var-store.h"
50 #include "ui/gui/psppire-scanf.h"
51 #include "ui/syntax-gen.h"
52
53 #include "gl/error.h"
54 #include "gl/intprops.h"
55 #include "gl/xalloc.h"
56
57 #include "gettext.h"
58 #define _(msgid) gettext (msgid)
59 #define N_(msgid) msgid
60
61 /* The "formats" page of the assistant. */
62
63 static void on_variable_change (PsppireDict *dict, int idx,
64                                 struct import_assistant *);
65 static void clear_modified_vars (struct import_assistant *);
66
67 /* Initializes IA's formats substructure. */
68 void
69 init_formats_page (struct import_assistant *ia)
70 {
71   GtkBuilder *builder = ia->asst.builder;
72   struct formats_page *p = ia->formats;
73
74   p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Formats"),
75                                    GTK_ASSISTANT_PAGE_CONFIRM);
76   p->data_tree_view = GTK_TREE_VIEW (get_widget_assert (builder, "data"));
77   p->modified_vars = NULL;
78   p->modified_var_cnt = 0;
79   p->dict = NULL;
80 }
81
82 /* Frees IA's formats substructure. */
83 void
84 destroy_formats_page (struct import_assistant *ia)
85 {
86   struct formats_page *p = ia->formats;
87
88   if (p->psppire_dict != NULL)
89     {
90       dict_destroy (p->psppire_dict->dict);
91       g_object_unref (p->psppire_dict);
92     }
93   clear_modified_vars (ia);
94 }
95
96 /* Called just before the formats page of the assistant is
97    displayed. */
98 void
99 prepare_formats_page (struct import_assistant *ia)
100 {
101   struct dictionary *dict;
102   PsppireDict *psppire_dict;
103   PsppireVarStore *var_store;
104   GtkBin *vars_scroller;
105   GtkWidget *old_var_sheet;
106   PsppireVarSheet *var_sheet;
107   struct separators_page *seps = ia->separators;
108   struct formats_page *p = ia->formats;
109   struct fmt_guesser *fg;
110   unsigned long int number = 0;
111   size_t column_idx;
112
113   push_watch_cursor (ia);
114
115   dict = dict_create (get_default_encoding ());
116   fg = fmt_guesser_create ();
117   printf ("%s:%d Column count %d\n", __FILE__, __LINE__, seps->column_cnt);
118   for (column_idx = 0; column_idx < seps->column_cnt; column_idx++)
119     {
120       struct variable *modified_var;
121
122       modified_var = (column_idx < p->modified_var_cnt
123                       ? p->modified_vars[column_idx] : NULL);
124       if (modified_var == NULL)
125         {
126           struct column *column = &seps->columns[column_idx];
127           struct variable *var;
128           struct fmt_spec format;
129           char *name;
130           size_t row;
131
132           /* Choose variable name. */
133           name = dict_make_unique_var_name (dict, column->name, &number);
134
135           /* Choose variable format. */
136           fmt_guesser_clear (fg);
137           for (row = ia->first_line->skip_lines; row < ia->file.line_cnt; row++)
138             fmt_guesser_add (fg, column->contents[row]);
139           fmt_guesser_guess (fg, &format);
140           fmt_fix_input (&format);
141
142           /* Create variable. */
143           var = dict_create_var_assert (dict, name, fmt_var_width (&format));
144           var_set_both_formats (var, &format);
145
146           free (name);
147         }
148       else
149         {
150           char *name;
151
152           name = dict_make_unique_var_name (dict, var_get_name (modified_var),
153                                             &number);
154           dict_clone_var_as_assert (dict, modified_var, name);
155           free (name);
156         }
157     }
158   fmt_guesser_destroy (fg);
159
160   psppire_dict = psppire_dict_new_from_dict (dict);
161   g_signal_connect (psppire_dict, "variable_changed",
162                     G_CALLBACK (on_variable_change), ia);
163   ia->formats->dict = dict;
164   ia->formats->psppire_dict = psppire_dict;
165
166   /* XXX: PsppireVarStore doesn't hold a reference to
167      psppire_dict for now, but it should.  After it does, we
168      should g_object_ref the psppire_dict here, since we also
169      hold a reference via ia->formats->dict. */
170   var_store = psppire_var_store_new (psppire_dict);
171   g_object_set (var_store,
172                 "format-type", PSPPIRE_VAR_STORE_INPUT_FORMATS,
173                 (void *) NULL);
174   var_sheet = PSPPIRE_VAR_SHEET (psppire_var_sheet_new ());
175   g_object_set (var_sheet,
176                 "model", var_store,
177                 "may-create-vars", FALSE,
178                 (void *) NULL);
179
180   vars_scroller = GTK_BIN (get_widget_assert (ia->asst.builder, "vars-scroller"));
181   old_var_sheet = gtk_bin_get_child (vars_scroller);
182   if (old_var_sheet != NULL)
183     gtk_widget_destroy (old_var_sheet);
184   gtk_container_add (GTK_CONTAINER (vars_scroller), GTK_WIDGET (var_sheet));
185   gtk_widget_show (GTK_WIDGET (var_sheet));
186
187   gtk_widget_destroy (GTK_WIDGET (ia->formats->data_tree_view));
188   ia->formats->data_tree_view = create_data_tree_view (
189     false,
190     GTK_CONTAINER (get_widget_assert (ia->asst.builder, "data-scroller")),
191     ia);
192
193   pop_watch_cursor (ia);
194 }
195
196 /* Clears the set of user-modified variables from IA's formats
197    substructure.  This discards user modifications to variable
198    formats, thereby causing formats to revert to their
199    defaults.  */
200 static void
201 clear_modified_vars (struct import_assistant *ia)
202 {
203   struct formats_page *p = ia->formats;
204   size_t i;
205
206   for (i = 0; i < p->modified_var_cnt; i++)
207     var_destroy (p->modified_vars[i]);
208   free (p->modified_vars);
209   p->modified_vars = NULL;
210   p->modified_var_cnt = 0;
211 }
212
213 /* Resets the formats page to its defaults, discarding user
214    modifications. */
215 void
216 reset_formats_page (struct import_assistant *ia)
217 {
218   clear_modified_vars (ia);
219   prepare_formats_page (ia);
220 }
221
222
223
224 /* Called when the user changes one of the variables in the
225    dictionary. */
226 static void
227 on_variable_change (PsppireDict *dict, int dict_idx,
228                     struct import_assistant *ia)
229 {
230   struct formats_page *p = ia->formats;
231   GtkTreeView *tv = ia->formats->data_tree_view;
232   gint column_idx = dict_idx + 1;
233
234   push_watch_cursor (ia);
235
236   /* Remove previous column and replace with new column. */
237   gtk_tree_view_remove_column (tv, gtk_tree_view_get_column (tv, column_idx));
238   gtk_tree_view_insert_column (tv, make_data_column (ia, tv, false, dict_idx),
239                                column_idx);
240
241   /* Save a copy of the modified variable in modified_vars, so
242      that its attributes will be preserved if we back up to the
243      previous page with the Prev button and then come back
244      here. */
245   if (dict_idx >= p->modified_var_cnt)
246     {
247       size_t i;
248       p->modified_vars = xnrealloc (p->modified_vars, dict_idx + 1,
249                                     sizeof *p->modified_vars);
250       for (i = 0; i <= dict_idx; i++)
251         p->modified_vars[i] = NULL;
252       p->modified_var_cnt = dict_idx + 1;
253     }
254   if (p->modified_vars[dict_idx])
255     var_destroy (p->modified_vars[dict_idx]);
256   p->modified_vars[dict_idx]
257     = var_clone (psppire_dict_get_variable (dict, dict_idx));
258
259   pop_watch_cursor (ia);
260 }
261
262