Started polymorphism of ODS
[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   printf ("%s:%d %p %d\n", __FILE__, __LINE__, ssp->spreadsheet, ssp->spreadsheet->type);
160   
161   if (ssp->spreadsheet == NULL)
162     ssp->spreadsheet = ods_probe (sri.file_name);
163
164   printf ("%s:%d %p %d\n", __FILE__, __LINE__, ssp->spreadsheet, ssp->spreadsheet->type);
165
166   if (ssp->spreadsheet)
167     {
168       
169       //      update_assistant (ia);
170     }
171   else
172     {
173   printf ("%s:%d %p\n", __FILE__, __LINE__, ssp->spreadsheet);
174
175     struct string input;
176     struct line_reader *reader = line_reader_for_file (file->encoding, file->file_name, O_RDONLY);
177     if (reader == NULL)
178       {
179         msg (ME, _("Could not open `%s': %s"),
180              file->file_name, strerror (errno));
181         return false;
182       }
183
184     ds_init_empty (&input);
185     file->lines = xnmalloc (MAX_PREVIEW_LINES, sizeof *file->lines);
186     for (; file->line_cnt < MAX_PREVIEW_LINES; file->line_cnt++)
187       {
188         ds_clear (&input);
189         if (!line_reader_read (reader, &input, MAX_LINE_LEN + 1)
190             || ds_length (&input) > MAX_LINE_LEN)
191           {
192             if (line_reader_eof (reader))
193               break;
194             else if (line_reader_error (reader))
195               msg (ME, _("Error reading `%s': %s"),
196                    file->file_name, strerror (line_reader_error (reader)));
197             else
198               msg (ME, _("Failed to read `%s', because it contains a line "
199                          "over %d bytes long and therefore appears not to be "
200                          "a text file."),
201                    file->file_name, MAX_LINE_LEN);
202             line_reader_close (reader);
203             destroy_file (ia);
204             ds_destroy (&input);
205             return false;
206           }
207
208         ds_init_cstr (&file->lines[file->line_cnt],
209                       recode_string ("UTF-8", line_reader_get_encoding (reader),
210                                      ds_cstr (&input), ds_length (&input)));
211       }
212     ds_destroy (&input);
213
214     if (file->line_cnt == 0)
215       {
216         msg (ME, _("`%s' is empty."), file->file_name);
217         line_reader_close (reader);
218         destroy_file (ia);
219         return false;
220       }
221
222     /* Estimate the number of lines in the file. */
223     if (file->line_cnt < MAX_PREVIEW_LINES)
224       file->total_lines = file->line_cnt;
225     else
226       {
227         struct stat s;
228         off_t position = line_reader_tell (reader);
229         if (fstat (line_reader_fileno (reader), &s) == 0 && position > 0)
230           file->total_lines = (double) file->line_cnt / position * s.st_size;
231         else
232           file->total_lines = 0;
233       }
234
235     line_reader_close (reader);
236   }
237
238   return true;
239 }
240
241 /* Frees IA's file substructure. */
242 void
243 destroy_file (struct import_assistant *ia)
244 {
245   struct file *f = &ia->file;
246   size_t i;
247
248   if (f->lines)
249     {
250       for (i = 0; i < f->line_cnt; i++)
251         ds_destroy (&f->lines[i]);
252       free (f->lines);
253     }
254
255   g_free (f->file_name);
256   g_free (f->encoding);
257 }
258
259 /* Obtains the file to read from the user.  If successful, returns the name of
260    the file and stores the user's chosen encoding for the file into *ENCODINGP.
261    The caller must free each of these strings with g_free().
262
263    On failure, stores a null pointer and stores NULL in *ENCODINGP.
264
265    PARENT_WINDOW must be the window to use as the file chooser window's
266    parent. */
267 static char *
268 choose_file (GtkWindow *parent_window, gchar **encodingp)
269 {
270   char *file_name;
271   GtkFileFilter *filter = NULL;
272
273   GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Import Delimited Text Data"),
274                                         parent_window,
275                                         GTK_FILE_CHOOSER_ACTION_OPEN,
276                                         GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
277                                         GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
278                                         NULL);
279
280   g_object_set (dialog, "local-only", FALSE, NULL);
281
282   filter = gtk_file_filter_new ();
283   gtk_file_filter_set_name (filter, _("Text files"));
284   gtk_file_filter_add_mime_type (filter, "text/*");
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, _("Text (*.txt) Files"));
289   gtk_file_filter_add_pattern (filter, "*.txt");
290   gtk_file_filter_add_pattern (filter, "*.TXT");
291   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
292
293   filter = gtk_file_filter_new ();
294   gtk_file_filter_set_name (filter, _("Plain Text (ASCII) Files"));
295   gtk_file_filter_add_mime_type (filter, "text/plain");
296   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
297
298   filter = gtk_file_filter_new ();
299   gtk_file_filter_set_name (filter, _("Comma Separated Value Files"));
300   gtk_file_filter_add_mime_type (filter, "text/csv");
301   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
302
303   /* I've never encountered one of these, but it's listed here:
304      http://www.iana.org/assignments/media-types/text/tab-separated-values  */
305   filter = gtk_file_filter_new ();
306   gtk_file_filter_set_name (filter, _("Tab Separated Value Files"));
307   gtk_file_filter_add_mime_type (filter, "text/tab-separated-values");
308   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
309
310   filter = gtk_file_filter_new ();
311   gtk_file_filter_set_name (filter, _("Gnumeric Spreadsheet Files"));
312   gtk_file_filter_add_mime_type (filter, "application/x-gnumeric");
313   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
314
315   filter = gtk_file_filter_new ();
316   gtk_file_filter_set_name (filter, _("OpenOffice.Org Spreadsheet Files"));
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   filter = gtk_file_filter_new ();
321   gtk_file_filter_set_name (filter, _("All Spreadsheet Files"));
322   gtk_file_filter_add_mime_type (filter, "application/x-gnumeric");
323   gtk_file_filter_add_mime_type (filter, "application/vnd.oasis.opendocument.spreadsheet");
324   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
325
326
327   gtk_file_chooser_set_extra_widget (
328     GTK_FILE_CHOOSER (dialog), psppire_encoding_selector_new ("Auto", true));
329
330   filter = gtk_file_filter_new ();
331   gtk_file_filter_set_name (filter, _("All Files"));
332   gtk_file_filter_add_pattern (filter, "*");
333   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
334
335   switch (gtk_dialog_run (GTK_DIALOG (dialog)))
336     {
337     case GTK_RESPONSE_ACCEPT:
338       file_name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
339       *encodingp = psppire_encoding_selector_get_encoding (
340         gtk_file_chooser_get_extra_widget (GTK_FILE_CHOOSER (dialog)));
341       break;
342     default:
343       file_name = NULL;
344       *encodingp = NULL;
345       break;
346     }
347   gtk_widget_destroy (dialog);
348
349   return file_name;
350 }