Seperate test-data-import-dialog into different files
[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/value-labels.h"
33 #include "language/data-io/data-parser.h"
34 #include "language/lexer/lexer.h"
35 #include "libpspp/assertion.h"
36 #include "libpspp/i18n.h"
37 #include "libpspp/line-reader.h"
38 #include "libpspp/message.h"
39 #include "ui/gui/checkbox-treeview.h"
40 #include "ui/gui/dialog-common.h"
41 #include "ui/gui/executor.h"
42 #include "ui/gui/helper.h"
43 #include "ui/gui/builder-wrapper.h"
44 #include "ui/gui/psppire-data-window.h"
45 #include "ui/gui/psppire-dialog.h"
46 #include "ui/gui/psppire-encoding-selector.h"
47 #include "ui/gui/psppire-empty-list-store.h"
48 #include "ui/gui/psppire-var-sheet.h"
49 #include "ui/gui/psppire-var-store.h"
50 #include "ui/gui/psppire-scanf.h"
51 #include "ui/syntax-gen.h"
52
53 #include "gl/error.h"
54 #include "gl/intprops.h"
55 #include "gl/xalloc.h"
56
57 #include "gettext.h"
58 #define _(msgid) gettext (msgid)
59 #define N_(msgid) msgid
60
61 struct import_assistant;
62
63 /* Choosing a file and reading it. */
64
65 static char *choose_file (GtkWindow *parent_window, gchar **encodingp);
66
67 /* Obtains the file to import from the user and initializes IA's
68    file substructure.  PARENT_WINDOW must be the window to use
69    as the file chooser window's parent.
70
71    Returns true if successful, false if the file name could not
72    be obtained or the file could not be read. */
73 bool
74 init_file (struct import_assistant *ia, GtkWindow *parent_window)
75 {
76   struct file *file = &ia->file;
77   enum { MAX_PREVIEW_LINES = 1000 }; /* Max number of lines to read. */
78   enum { MAX_LINE_LEN = 16384 }; /* Max length of an acceptable line. */
79   struct line_reader *reader;
80   struct string input;
81
82   file->file_name = choose_file (parent_window, &file->encoding);
83   if (file->file_name == NULL)
84     return false;
85
86   reader = line_reader_for_file (file->encoding, file->file_name, O_RDONLY);
87   if (reader == NULL)
88     {
89       msg (ME, _("Could not open `%s': %s"),
90            file->file_name, strerror (errno));
91       return false;
92     }
93
94   ds_init_empty (&input);
95   file->lines = xnmalloc (MAX_PREVIEW_LINES, sizeof *file->lines);
96   for (; file->line_cnt < MAX_PREVIEW_LINES; file->line_cnt++)
97     {
98       ds_clear (&input);
99       if (!line_reader_read (reader, &input, MAX_LINE_LEN + 1)
100           || ds_length (&input) > MAX_LINE_LEN)
101         {
102           if (line_reader_eof (reader))
103             break;
104           else if (line_reader_error (reader))
105             msg (ME, _("Error reading `%s': %s"),
106                  file->file_name, strerror (line_reader_error (reader)));
107           else
108             msg (ME, _("Failed to read `%s', because it contains a line "
109                        "over %d bytes long and therefore appears not to be "
110                        "a text file."),
111                  file->file_name, MAX_LINE_LEN);
112           line_reader_close (reader);
113           destroy_file (ia);
114           ds_destroy (&input);
115           return false;
116         }
117
118       ds_init_cstr (&file->lines[file->line_cnt],
119                     recode_string ("UTF-8", line_reader_get_encoding (reader),
120                                    ds_cstr (&input), ds_length (&input)));
121     }
122   ds_destroy (&input);
123
124   if (file->line_cnt == 0)
125     {
126       msg (ME, _("`%s' is empty."), file->file_name);
127       line_reader_close (reader);
128       destroy_file (ia);
129       return false;
130     }
131
132   /* Estimate the number of lines in the file. */
133   if (file->line_cnt < MAX_PREVIEW_LINES)
134     file->total_lines = file->line_cnt;
135   else
136     {
137       struct stat s;
138       off_t position = line_reader_tell (reader);
139       if (fstat (line_reader_fileno (reader), &s) == 0 && position > 0)
140         file->total_lines = (double) file->line_cnt / position * s.st_size;
141       else
142         file->total_lines = 0;
143     }
144
145   line_reader_close (reader);
146
147   return true;
148 }
149
150 /* Frees IA's file substructure. */
151 void
152 destroy_file (struct import_assistant *ia)
153 {
154   struct file *f = &ia->file;
155   size_t i;
156
157   for (i = 0; i < f->line_cnt; i++)
158     ds_destroy (&f->lines[i]);
159   free (f->lines);
160   g_free (f->file_name);
161   g_free (f->encoding);
162 }
163
164 /* Obtains the file to read from the user.  If successful, returns the name of
165    the file and stores the user's chosen encoding for the file into *ENCODINGP.
166    The caller must free each of these strings with g_free().
167
168    On failure, stores a null pointer and stores NULL in *ENCODINGP.
169
170    PARENT_WINDOW must be the window to use as the file chooser window's
171    parent. */
172 static char *
173 choose_file (GtkWindow *parent_window, gchar **encodingp)
174 {
175   char *file_name;
176   GtkFileFilter *filter = NULL;
177
178   GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Import Delimited Text Data"),
179                                         parent_window,
180                                         GTK_FILE_CHOOSER_ACTION_OPEN,
181                                         GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
182                                         GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
183                                         NULL);
184
185   g_object_set (dialog, "local-only", FALSE, NULL);
186
187   filter = gtk_file_filter_new ();
188   gtk_file_filter_set_name (filter, _("Text files"));
189   gtk_file_filter_add_mime_type (filter, "text/*");
190   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
191
192   filter = gtk_file_filter_new ();
193   gtk_file_filter_set_name (filter, _("Text (*.txt) Files"));
194   gtk_file_filter_add_pattern (filter, "*.txt");
195   gtk_file_filter_add_pattern (filter, "*.TXT");
196   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
197
198   filter = gtk_file_filter_new ();
199   gtk_file_filter_set_name (filter, _("Plain Text (ASCII) Files"));
200   gtk_file_filter_add_mime_type (filter, "text/plain");
201   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
202
203   filter = gtk_file_filter_new ();
204   gtk_file_filter_set_name (filter, _("Comma Separated Value Files"));
205   gtk_file_filter_add_mime_type (filter, "text/csv");
206   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
207
208   /* I've never encountered one of these, but it's listed here:
209      http://www.iana.org/assignments/media-types/text/tab-separated-values  */
210   filter = gtk_file_filter_new ();
211   gtk_file_filter_set_name (filter, _("Tab Separated Value Files"));
212   gtk_file_filter_add_mime_type (filter, "text/tab-separated-values");
213   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
214
215   gtk_file_chooser_set_extra_widget (
216     GTK_FILE_CHOOSER (dialog), psppire_encoding_selector_new ("Auto", true));
217
218   filter = gtk_file_filter_new ();
219   gtk_file_filter_set_name (filter, _("All Files"));
220   gtk_file_filter_add_pattern (filter, "*");
221   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
222
223   switch (gtk_dialog_run (GTK_DIALOG (dialog)))
224     {
225     case GTK_RESPONSE_ACCEPT:
226       file_name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
227       *encodingp = psppire_encoding_selector_get_encoding (
228         gtk_file_chooser_get_extra_widget (GTK_FILE_CHOOSER (dialog)));
229       break;
230     default:
231       file_name = NULL;
232       *encodingp = NULL;
233       break;
234     }
235   gtk_widget_destroy (dialog);
236
237   return file_name;
238 }