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