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