Put the pages as pointers
[pspp] / src / ui / gui / page-file.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/casereader.h"
33 #include "data/gnumeric-reader.h"
34 #include "data/ods-reader.h"
35 #include "data/spreadsheet-reader.h"
36 #include "data/value-labels.h"
37 #include "language/data-io/data-parser.h"
38 #include "language/lexer/lexer.h"
39 #include "libpspp/assertion.h"
40 #include "libpspp/i18n.h"
41 #include "libpspp/line-reader.h"
42 #include "libpspp/message.h"
43 #include "ui/gui/checkbox-treeview.h"
44 #include "ui/gui/dialog-common.h"
45 #include "ui/gui/executor.h"
46 #include "ui/gui/helper.h"
47 #include "ui/gui/builder-wrapper.h"
48 #include "ui/gui/psppire-data-window.h"
49 #include "ui/gui/psppire-dialog.h"
50 #include "ui/gui/psppire-encoding-selector.h"
51 #include "ui/gui/psppire-empty-list-store.h"
52 #include "ui/gui/psppire-var-sheet.h"
53 #include "ui/gui/psppire-var-store.h"
54 #include "ui/gui/psppire-scanf.h"
55 #include "ui/syntax-gen.h"
56
57 #include "gl/error.h"
58 #include "gl/intprops.h"
59 #include "gl/xalloc.h"
60
61 #include "gettext.h"
62 #define _(msgid) gettext (msgid)
63 #define N_(msgid) msgid
64
65 struct import_assistant;
66
67 /* Choose a file */
68 static char *choose_file (GtkWindow *parent_window, gchar **encodingp);
69 enum { MAX_PREVIEW_LINES = 1000 }; /* Max number of lines to read. */
70
71
72 /*
73   Update IA according to the contents of DICT and CREADER.
74   CREADER will be destroyed by this function.
75 */
76 void 
77 update_assistant (struct import_assistant *ia)
78 {
79   struct sheet_spec_page *ssp = ia->sheet_spec;
80
81   struct file *file = &ia->file;
82   struct separators_page *sepp = ia->separators;
83   int rows = 0;
84   if (ssp->dict)
85     {
86       struct ccase *c;
87       int col;
88
89
90       sepp->column_cnt = dict_get_var_cnt (ssp->dict);
91       sepp->columns = xcalloc (sepp->column_cnt, sizeof (*sepp->columns));
92       for (col = 0; col < sepp->column_cnt ; ++col)
93         {
94           const struct variable *var = dict_get_var (ssp->dict, col);
95           sepp->columns[col].name = xstrdup (var_get_name (var));
96           sepp->columns[col].contents = NULL;
97         }
98
99       for (; (c = casereader_read (ssp->reader)) != NULL; case_unref (c))
100         {
101           rows++;
102           for (col = 0; col < sepp->column_cnt ; ++col)
103             {
104               char *ss;
105               const struct variable *var = dict_get_var (ssp->dict, col);
106
107               sepp->columns[col].contents = xrealloc (sepp->columns[col].contents,
108                                                       sizeof (struct substring) * rows);
109
110               ss = data_out (case_data (c, var), dict_get_encoding (ssp->dict), 
111                              var_get_print_format (var));
112
113               sepp->columns[col].contents[rows - 1] = ss_cstr (ss);
114             }
115
116           if (rows > MAX_PREVIEW_LINES)
117             {
118               case_unref (c);
119               break;
120             }
121         }
122     }
123   
124   file->line_cnt = rows;
125 }
126
127
128 /* Obtains the file to import from the user and initializes IA's
129    file substructure.  PARENT_WINDOW must be the window to use
130    as the file chooser window's parent.
131
132    Returns true if successful, false if the file name could not
133    be obtained or the file could not be read. */
134 bool
135 init_file (struct import_assistant *ia, GtkWindow *parent_window)
136 {
137   enum { MAX_LINE_LEN = 16384 }; /* Max length of an acceptable line. */
138   struct file *file = &ia->file;
139   struct sheet_spec_page *ssp = ia->sheet_spec;
140   struct spreadsheet_read_info sri;
141   struct spreadsheet_read_options opts;
142
143   file->lines = NULL;
144   file->file_name = choose_file (parent_window, &file->encoding);
145   if (file->file_name == NULL)
146     return false;
147
148   opts.sheet_name = NULL;
149   opts.cell_range = NULL;
150   opts.sheet_index = 1;
151
152   sri.file_name = file->file_name;
153   sri.read_names = true;
154   sri.asw = -1;
155
156   if (ssp->spreadsheet == NULL)
157     ssp->spreadsheet = gnumeric_probe (sri.file_name);
158   
159   if (ssp->spreadsheet == NULL)
160     ssp->spreadsheet = ods_probe (sri.file_name);
161
162   if (ssp->spreadsheet)
163     {
164       
165       //      update_assistant (ia);
166     }
167   else
168     {
169     struct string input;
170     struct line_reader *reader = line_reader_for_file (file->encoding, file->file_name, O_RDONLY);
171     if (reader == NULL)
172       {
173         msg (ME, _("Could not open `%s': %s"),
174              file->file_name, strerror (errno));
175         return false;
176       }
177
178     ds_init_empty (&input);
179     file->lines = xnmalloc (MAX_PREVIEW_LINES, sizeof *file->lines);
180     for (; file->line_cnt < MAX_PREVIEW_LINES; file->line_cnt++)
181       {
182         ds_clear (&input);
183         if (!line_reader_read (reader, &input, MAX_LINE_LEN + 1)
184             || ds_length (&input) > MAX_LINE_LEN)
185           {
186             if (line_reader_eof (reader))
187               break;
188             else if (line_reader_error (reader))
189               msg (ME, _("Error reading `%s': %s"),
190                    file->file_name, strerror (line_reader_error (reader)));
191             else
192               msg (ME, _("Failed to read `%s', because it contains a line "
193                          "over %d bytes long and therefore appears not to be "
194                          "a text file."),
195                    file->file_name, MAX_LINE_LEN);
196             line_reader_close (reader);
197             destroy_file (ia);
198             ds_destroy (&input);
199             return false;
200           }
201
202         ds_init_cstr (&file->lines[file->line_cnt],
203                       recode_string ("UTF-8", line_reader_get_encoding (reader),
204                                      ds_cstr (&input), ds_length (&input)));
205       }
206     ds_destroy (&input);
207
208     if (file->line_cnt == 0)
209       {
210         msg (ME, _("`%s' is empty."), file->file_name);
211         line_reader_close (reader);
212         destroy_file (ia);
213         return false;
214       }
215
216     /* Estimate the number of lines in the file. */
217     if (file->line_cnt < MAX_PREVIEW_LINES)
218       file->total_lines = file->line_cnt;
219     else
220       {
221         struct stat s;
222         off_t position = line_reader_tell (reader);
223         if (fstat (line_reader_fileno (reader), &s) == 0 && position > 0)
224           file->total_lines = (double) file->line_cnt / position * s.st_size;
225         else
226           file->total_lines = 0;
227       }
228
229     line_reader_close (reader);
230   }
231
232   return true;
233 }
234
235 /* Frees IA's file substructure. */
236 void
237 destroy_file (struct import_assistant *ia)
238 {
239   struct file *f = &ia->file;
240   size_t i;
241
242   if (f->lines)
243     {
244       for (i = 0; i < f->line_cnt; i++)
245         ds_destroy (&f->lines[i]);
246       free (f->lines);
247     }
248
249   g_free (f->file_name);
250   g_free (f->encoding);
251 }
252
253 /* Obtains the file to read from the user.  If successful, returns the name of
254    the file and stores the user's chosen encoding for the file into *ENCODINGP.
255    The caller must free each of these strings with g_free().
256
257    On failure, stores a null pointer and stores NULL in *ENCODINGP.
258
259    PARENT_WINDOW must be the window to use as the file chooser window's
260    parent. */
261 static char *
262 choose_file (GtkWindow *parent_window, gchar **encodingp)
263 {
264   char *file_name;
265   GtkFileFilter *filter = NULL;
266
267   GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Import Delimited Text Data"),
268                                         parent_window,
269                                         GTK_FILE_CHOOSER_ACTION_OPEN,
270                                         GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
271                                         GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
272                                         NULL);
273
274   g_object_set (dialog, "local-only", FALSE, NULL);
275
276   filter = gtk_file_filter_new ();
277   gtk_file_filter_set_name (filter, _("Text files"));
278   gtk_file_filter_add_mime_type (filter, "text/*");
279   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
280
281   filter = gtk_file_filter_new ();
282   gtk_file_filter_set_name (filter, _("Text (*.txt) Files"));
283   gtk_file_filter_add_pattern (filter, "*.txt");
284   gtk_file_filter_add_pattern (filter, "*.TXT");
285   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
286
287   filter = gtk_file_filter_new ();
288   gtk_file_filter_set_name (filter, _("Plain Text (ASCII) Files"));
289   gtk_file_filter_add_mime_type (filter, "text/plain");
290   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
291
292   filter = gtk_file_filter_new ();
293   gtk_file_filter_set_name (filter, _("Comma Separated Value Files"));
294   gtk_file_filter_add_mime_type (filter, "text/csv");
295   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
296
297   /* I've never encountered one of these, but it's listed here:
298      http://www.iana.org/assignments/media-types/text/tab-separated-values  */
299   filter = gtk_file_filter_new ();
300   gtk_file_filter_set_name (filter, _("Tab Separated Value Files"));
301   gtk_file_filter_add_mime_type (filter, "text/tab-separated-values");
302   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
303
304   filter = gtk_file_filter_new ();
305   gtk_file_filter_set_name (filter, _("Gnumeric Spreadsheet Files"));
306   gtk_file_filter_add_mime_type (filter, "application/x-gnumeric");
307   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
308
309   filter = gtk_file_filter_new ();
310   gtk_file_filter_set_name (filter, _("OpenOffice.Org Spreadsheet Files"));
311   gtk_file_filter_add_mime_type (filter, "application/vnd.oasis.opendocument.spreadsheet");
312   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
313
314   filter = gtk_file_filter_new ();
315   gtk_file_filter_set_name (filter, _("All Spreadsheet Files"));
316   gtk_file_filter_add_mime_type (filter, "application/x-gnumeric");
317   gtk_file_filter_add_mime_type (filter, "application/vnd.oasis.opendocument.spreadsheet");
318   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
319
320
321   gtk_file_chooser_set_extra_widget (
322     GTK_FILE_CHOOSER (dialog), psppire_encoding_selector_new ("Auto", true));
323
324   filter = gtk_file_filter_new ();
325   gtk_file_filter_set_name (filter, _("All Files"));
326   gtk_file_filter_add_pattern (filter, "*");
327   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
328
329   switch (gtk_dialog_run (GTK_DIALOG (dialog)))
330     {
331     case GTK_RESPONSE_ACCEPT:
332       file_name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
333       *encodingp = psppire_encoding_selector_get_encoding (
334         gtk_file_chooser_get_extra_widget (GTK_FILE_CHOOSER (dialog)));
335       break;
336     default:
337       file_name = NULL;
338       *encodingp = NULL;
339       break;
340     }
341   gtk_widget_destroy (dialog);
342
343   return file_name;
344 }