Merge 'master' into 'gtk3'.
[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/gtk.h>
24 #include <limits.h>
25 #include <stdlib.h>
26 #include <sys/stat.h>
27
28 #include "data/data-in.h"
29 #include "data/data-out.h"
30 #include "data/format-guesser.h"
31 #include "data/value-labels.h"
32 #include "language/data-io/data-parser.h"
33 #include "language/lexer/lexer.h"
34 #include "libpspp/assertion.h"
35 #include "libpspp/i18n.h"
36 #include "libpspp/line-reader.h"
37 #include "libpspp/message.h"
38 #include "ui/gui/dialog-common.h"
39 #include "ui/gui/executor.h"
40 #include "ui/gui/helper.h"
41 #include "ui/gui/builder-wrapper.h"
42 #include "ui/gui/psppire-data-window.h"
43 #include "ui/gui/psppire-dialog.h"
44 #include "ui/gui/psppire-encoding-selector.h"
45 #include "ui/gui/psppire-empty-list-store.h"
46 #include "ui/gui/psppire-var-sheet.h"
47 #include "ui/gui/psppire-scanf.h"
48
49 #include "gl/intprops.h"
50 #include "gl/xalloc.h"
51
52 #include "gettext.h"
53 #define _(msgid) gettext (msgid)
54 #define N_(msgid) msgid
55
56 /* Page where the user verifies and adjusts input formats. */
57 struct formats_page
58   {
59     GtkWidget *page;
60     PsppSheetView *data_tree_view;
61     PsppireDict *psppire_dict;
62     struct variable **modified_vars;
63     size_t modified_var_cnt;
64   };
65
66 /* The "formats" page of the assistant. */
67
68 static void on_variable_change (PsppireDict *dict, int idx,
69                                 unsigned int what,
70                                 const struct variable *oldvar,
71                                 struct import_assistant *);
72
73 static void clear_modified_vars (struct import_assistant *);
74
75 /* Initializes IA's formats substructure. */
76
77 struct formats_page *
78 formats_page_create (struct import_assistant *ia)
79 {
80   GtkBuilder *builder = ia->asst.builder;
81   struct formats_page *p = xzalloc (sizeof *p);
82
83   p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Formats"),
84                          GTK_ASSISTANT_PAGE_CONFIRM);
85
86   p->data_tree_view = PSPP_SHEET_VIEW (get_widget_assert (builder, "data"));
87   p->modified_vars = NULL;
88   p->modified_var_cnt = 0;
89
90   return p;
91 }
92
93 /* Frees IA's formats substructure. */
94 void
95 destroy_formats_page (struct import_assistant *ia)
96 {
97   struct formats_page *p = ia->formats;
98
99   if (p->psppire_dict != NULL)
100     {
101       dict_destroy (p->psppire_dict->dict);
102       g_object_unref (p->psppire_dict);
103     }
104   clear_modified_vars (ia);
105 }
106
107 /* Called just before the formats page of the assistant is
108    displayed. */
109 void
110 prepare_formats_page (struct import_assistant *ia)
111 {
112   struct dictionary *dict;
113   PsppireDict *psppire_dict;
114   GtkBin *vars_scroller;
115   GtkWidget *old_var_sheet;
116   PsppireVarSheet *var_sheet;
117   struct formats_page *p = ia->formats;
118   struct fmt_guesser *fg;
119   unsigned long int number = 0;
120   size_t column_idx;
121
122   push_watch_cursor (ia);
123
124   dict = dict_create (get_default_encoding ());
125   fg = fmt_guesser_create ();
126   for (column_idx = 0; column_idx < ia->column_cnt; column_idx++)
127     {
128       struct variable *modified_var = 
129         (column_idx < p->modified_var_cnt ? p->modified_vars[column_idx] : NULL);
130       if (modified_var == NULL)
131         {
132           struct column *column = &ia->columns[column_idx];
133           struct variable *var;
134           struct fmt_spec format;
135           char *name;
136           size_t row;
137
138           /* Choose variable name. */
139           name = dict_make_unique_var_name (dict, column->name, &number);
140
141           /* Choose variable format. */
142           fmt_guesser_clear (fg);
143           for (row = ia->skip_lines; row < ia->file.line_cnt; row++)
144             fmt_guesser_add (fg, column->contents[row]);
145           fmt_guesser_guess (fg, &format);
146           fmt_fix_input (&format);
147
148           /* Create variable. */
149           var = dict_create_var_assert (dict, name, fmt_var_width (&format));
150           var_set_both_formats (var, &format);
151
152           free (name);
153         }
154       else
155         {
156           char *name;
157
158           name = dict_make_unique_var_name (dict, var_get_name (modified_var),
159                                             &number);
160           dict_clone_var_as_assert (dict, modified_var, name);
161           free (name);
162         }
163     }
164   fmt_guesser_destroy (fg);
165
166   psppire_dict = psppire_dict_new_from_dict (dict);
167   g_signal_connect (psppire_dict, "variable-changed",
168                     G_CALLBACK (on_variable_change), ia);
169   ia->dict = dict;
170   ia->formats->psppire_dict = psppire_dict;
171
172   /* XXX: PsppireVarStore doesn't hold a reference to
173      psppire_dict for now, but it should.  After it does, we
174      should g_object_ref the psppire_dict here, since we also
175      hold a reference via ia->formats->dict. */
176   var_sheet = PSPPIRE_VAR_SHEET (psppire_var_sheet_new ());
177   g_object_set (var_sheet,
178                 "dictionary", psppire_dict,
179                 "may-create-vars", FALSE,
180                 "may-delete-vars", FALSE,
181                 "format-use", FMT_FOR_INPUT,
182                 "enable-grid-lines", PSPP_SHEET_VIEW_GRID_LINES_BOTH,
183                 (void *) NULL);
184
185   vars_scroller = GTK_BIN (get_widget_assert (ia->asst.builder, "vars-scroller"));
186   old_var_sheet = gtk_bin_get_child (vars_scroller);
187   if (old_var_sheet != NULL)
188     gtk_widget_destroy (old_var_sheet);
189   gtk_container_add (GTK_CONTAINER (vars_scroller), GTK_WIDGET (var_sheet));
190   gtk_widget_show (GTK_WIDGET (var_sheet));
191
192   gtk_widget_destroy (GTK_WIDGET (ia->formats->data_tree_view));
193   ia->formats->data_tree_view = create_data_tree_view (
194     false,
195     GTK_CONTAINER (get_widget_assert (ia->asst.builder, "data-scroller")),
196     ia);
197
198   gtk_widget_show (ia->asst.paste_button);
199
200   pop_watch_cursor (ia);
201 }
202
203 /* Clears the set of user-modified variables from IA's formats
204    substructure.  This discards user modifications to variable
205    formats, thereby causing formats to revert to their
206    defaults.  */
207 static void
208 clear_modified_vars (struct import_assistant *ia)
209 {
210   struct formats_page *p = ia->formats;
211   size_t i;
212
213   for (i = 0; i < p->modified_var_cnt; i++)
214     var_destroy (p->modified_vars[i]);
215   free (p->modified_vars);
216   p->modified_vars = NULL;
217   p->modified_var_cnt = 0;
218 }
219
220 /* Resets the formats page to its defaults, discarding user
221    modifications. */
222 void
223 reset_formats_page (struct import_assistant *ia)
224 {
225   clear_modified_vars (ia);
226   prepare_formats_page (ia);
227 }
228
229
230
231 /* Called when the user changes one of the variables in the
232    dictionary. */
233 static void
234 on_variable_change (PsppireDict *dict, int dict_idx,
235                     unsigned int what, const struct variable *oldvar,
236                     struct import_assistant *ia)
237 {
238   struct formats_page *p = ia->formats;
239   PsppSheetView *tv = ia->formats->data_tree_view;
240   gint column_idx = dict_idx + 1;
241
242   push_watch_cursor (ia);
243
244   /* Remove previous column and replace with new column. */
245   pspp_sheet_view_remove_column (tv, pspp_sheet_view_get_column (tv, column_idx));
246   pspp_sheet_view_insert_column (tv, make_data_column (ia, tv, false, dict_idx),
247                                  column_idx);
248
249   /* Save a copy of the modified variable in modified_vars, so
250      that its attributes will be preserved if we back up to the
251      previous page with the Prev button and then come back
252      here. */
253   if (dict_idx >= p->modified_var_cnt)
254     {
255       size_t i;
256       p->modified_vars = xnrealloc (p->modified_vars, dict_idx + 1,
257                                     sizeof *p->modified_vars);
258       for (i = 0; i <= dict_idx; i++)
259         p->modified_vars[i] = NULL;
260       p->modified_var_cnt = dict_idx + 1;
261     }
262   if (p->modified_vars[dict_idx])
263     var_destroy (p->modified_vars[dict_idx]);
264   p->modified_vars[dict_idx]
265     = var_clone (psppire_dict_get_variable (dict, dict_idx));
266
267   pop_watch_cursor (ia);
268 }
269
270
271
272
273 void
274 formats_append_syntax (const struct import_assistant *ia, struct string *s)
275 {
276   int i;
277   int var_cnt;
278   ds_put_cstr (s, "  /VARIABLES=\n");
279   
280   var_cnt = dict_get_var_cnt (ia->dict);
281   for (i = 0; i < var_cnt; i++)
282     {
283       struct variable *var = dict_get_var (ia->dict, i);
284       char format_string[FMT_STRING_LEN_MAX + 1];
285       fmt_to_string (var_get_print_format (var), format_string);
286       ds_put_format (s, "    %s %s%s\n",
287                      var_get_name (var), format_string,
288                      i == var_cnt - 1 ? "." : "");
289     }
290 }