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