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