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