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