Return NULL if not a gnumeric file
[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
70
71
72
73 /* Obtains the file to import from the user and initializes IA's
74    file substructure.  PARENT_WINDOW must be the window to use
75    as the file chooser window's parent.
76
77    Returns true if successful, false if the file name could not
78    be obtained or the file could not be read. */
79 bool
80 init_file (struct import_assistant *ia, GtkWindow *parent_window)
81 {
82   enum { MAX_LINE_LEN = 16384 }; /* Max length of an acceptable line. */
83   struct file *file = &ia->file;
84   struct spreadsheet_read_info sri;
85   struct spreadsheet_read_options opts;
86
87   file->lines = NULL;
88   file->file_name = choose_file (parent_window, &file->encoding);
89   if (file->file_name == NULL)
90     return false;
91
92   opts.sheet_name = NULL;
93   opts.cell_range = NULL;
94   opts.sheet_index = 1;
95
96   sri.read_names = true;
97   sri.asw = -1;
98
99   printf ("%s:%d %p\n", __FILE__, __LINE__, ia->spreadsheet);
100
101   if (ia->spreadsheet == NULL)
102     ia->spreadsheet = gnumeric_probe (file->file_name);
103
104   printf ("%s:%d %p\n", __FILE__, __LINE__, ia->spreadsheet);
105
106   if (ia->spreadsheet == NULL)
107     ia->spreadsheet = ods_probe (file->file_name);
108
109   printf ("%s:%d %p\n", __FILE__, __LINE__, ia->spreadsheet);
110   
111   if (ia->spreadsheet == NULL)
112     {
113     struct string input;
114     struct line_reader *reader = line_reader_for_file (file->encoding, file->file_name, O_RDONLY);
115     if (reader == NULL)
116       {
117         msg (ME, _("Could not open `%s': %s"),
118              file->file_name, strerror (errno));
119         return false;
120       }
121
122     ds_init_empty (&input);
123     file->lines = xnmalloc (MAX_PREVIEW_LINES, sizeof *file->lines);
124     for (; file->line_cnt < MAX_PREVIEW_LINES; file->line_cnt++)
125       {
126         ds_clear (&input);
127         if (!line_reader_read (reader, &input, MAX_LINE_LEN + 1)
128             || ds_length (&input) > MAX_LINE_LEN)
129           {
130             if (line_reader_eof (reader))
131               break;
132             else if (line_reader_error (reader))
133               msg (ME, _("Error reading `%s': %s"),
134                    file->file_name, strerror (line_reader_error (reader)));
135             else
136               msg (ME, _("Failed to read `%s', because it contains a line "
137                          "over %d bytes long and therefore appears not to be "
138                          "a text file."),
139                    file->file_name, MAX_LINE_LEN);
140             line_reader_close (reader);
141             destroy_file (ia);
142             ds_destroy (&input);
143             return false;
144           }
145
146         ds_init_cstr (&file->lines[file->line_cnt],
147                       recode_string ("UTF-8", line_reader_get_encoding (reader),
148                                      ds_cstr (&input), ds_length (&input)));
149       }
150     ds_destroy (&input);
151
152     if (file->line_cnt == 0)
153       {
154         msg (ME, _("`%s' is empty."), file->file_name);
155         line_reader_close (reader);
156         destroy_file (ia);
157         return false;
158       }
159
160     /* Estimate the number of lines in the file. */
161     if (file->line_cnt < MAX_PREVIEW_LINES)
162       file->total_lines = file->line_cnt;
163     else
164       {
165         struct stat s;
166         off_t position = line_reader_tell (reader);
167         if (fstat (line_reader_fileno (reader), &s) == 0 && position > 0)
168           file->total_lines = (double) file->line_cnt / position * s.st_size;
169         else
170           file->total_lines = 0;
171       }
172
173     line_reader_close (reader);
174   }
175
176   return true;
177 }
178
179 /* Frees IA's file substructure. */
180 void
181 destroy_file (struct import_assistant *ia)
182 {
183   struct file *f = &ia->file;
184   size_t i;
185
186   if (f->lines)
187     {
188       for (i = 0; i < f->line_cnt; i++)
189         ds_destroy (&f->lines[i]);
190       free (f->lines);
191     }
192
193   g_free (f->file_name);
194   g_free (f->encoding);
195 }
196
197 /* Obtains the file to read from the user.  If successful, returns the name of
198    the file and stores the user's chosen encoding for the file into *ENCODINGP.
199    The caller must free each of these strings with g_free().
200
201    On failure, stores a null pointer and stores NULL in *ENCODINGP.
202
203    PARENT_WINDOW must be the window to use as the file chooser window's
204    parent. */
205 static char *
206 choose_file (GtkWindow *parent_window, gchar **encodingp)
207 {
208   char *file_name;
209   GtkFileFilter *filter = NULL;
210
211   GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Import Delimited Text Data"),
212                                         parent_window,
213                                         GTK_FILE_CHOOSER_ACTION_OPEN,
214                                         GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
215                                         GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
216                                         NULL);
217
218   g_object_set (dialog, "local-only", FALSE, NULL);
219
220   filter = gtk_file_filter_new ();
221   gtk_file_filter_set_name (filter, _("Text files"));
222   gtk_file_filter_add_mime_type (filter, "text/*");
223   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
224
225   filter = gtk_file_filter_new ();
226   gtk_file_filter_set_name (filter, _("Text (*.txt) Files"));
227   gtk_file_filter_add_pattern (filter, "*.txt");
228   gtk_file_filter_add_pattern (filter, "*.TXT");
229   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
230
231   filter = gtk_file_filter_new ();
232   gtk_file_filter_set_name (filter, _("Plain Text (ASCII) Files"));
233   gtk_file_filter_add_mime_type (filter, "text/plain");
234   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
235
236   filter = gtk_file_filter_new ();
237   gtk_file_filter_set_name (filter, _("Comma Separated Value Files"));
238   gtk_file_filter_add_mime_type (filter, "text/csv");
239   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
240
241   /* I've never encountered one of these, but it's listed here:
242      http://www.iana.org/assignments/media-types/text/tab-separated-values  */
243   filter = gtk_file_filter_new ();
244   gtk_file_filter_set_name (filter, _("Tab Separated Value Files"));
245   gtk_file_filter_add_mime_type (filter, "text/tab-separated-values");
246   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
247
248   filter = gtk_file_filter_new ();
249   gtk_file_filter_set_name (filter, _("Gnumeric Spreadsheet Files"));
250   gtk_file_filter_add_mime_type (filter, "application/x-gnumeric");
251   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
252
253   filter = gtk_file_filter_new ();
254   gtk_file_filter_set_name (filter, _("OpenOffice.Org Spreadsheet Files"));
255   gtk_file_filter_add_mime_type (filter, "application/vnd.oasis.opendocument.spreadsheet");
256   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
257
258   filter = gtk_file_filter_new ();
259   gtk_file_filter_set_name (filter, _("All Spreadsheet Files"));
260   gtk_file_filter_add_mime_type (filter, "application/x-gnumeric");
261   gtk_file_filter_add_mime_type (filter, "application/vnd.oasis.opendocument.spreadsheet");
262   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
263
264
265   gtk_file_chooser_set_extra_widget (
266     GTK_FILE_CHOOSER (dialog), psppire_encoding_selector_new ("Auto", true));
267
268   filter = gtk_file_filter_new ();
269   gtk_file_filter_set_name (filter, _("All Files"));
270   gtk_file_filter_add_pattern (filter, "*");
271   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
272
273   switch (gtk_dialog_run (GTK_DIALOG (dialog)))
274     {
275     case GTK_RESPONSE_ACCEPT:
276       file_name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
277       *encodingp = psppire_encoding_selector_get_encoding (
278         gtk_file_chooser_get_extra_widget (GTK_FILE_CHOOSER (dialog)));
279       break;
280     default:
281       file_name = NULL;
282       *encodingp = NULL;
283       break;
284     }
285   gtk_widget_destroy (dialog);
286
287   return file_name;
288 }