Merge "master" into "psppsheet" to obtain bug fixes from "master".
[pspp] / src / ui / gui / text-data-import-dialog.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2008, 2009, 2010, 2011, 2012  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/value-labels.h"
32 #include "language/data-io/data-parser.h"
33 #include "language/lexer/lexer.h"
34 #include "libpspp/assertion.h"
35 #include "libpspp/i18n.h"
36 #include "libpspp/line-reader.h"
37 #include "libpspp/message.h"
38 #include "ui/gui/builder-wrapper.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/pspp-sheet-selection.h"
44 #include "ui/gui/pspp-sheet-view.h"
45 #include "ui/gui/psppire-data-window.h"
46 #include "ui/gui/psppire-dialog.h"
47 #include "ui/gui/psppire-encoding-selector.h"
48 #include "ui/gui/psppire-empty-list-store.h"
49 #include "ui/gui/psppire-scanf.h"
50 #include "ui/gui/psppire-var-sheet.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 /* The file to be imported. */
64 struct file
65   {
66     char *file_name;        /* File name. */
67     gchar *encoding;        /* Encoding. */
68     unsigned long int total_lines; /* Number of lines in file. */
69     bool total_is_exact;    /* Is total_lines exact (or an estimate)? */
70
71     /* The first several lines of the file. */
72     struct string *lines;
73     size_t line_cnt;
74   };
75 static bool init_file (struct import_assistant *, GtkWindow *parent);
76 static void destroy_file (struct import_assistant *);
77
78 /* The main body of the GTK+ assistant and related data. */
79 struct assistant
80   {
81     GtkBuilder *builder;
82     GtkAssistant *assistant;
83     GMainLoop *main_loop;
84     GtkWidget *paste_button;
85     GtkWidget *reset_button;
86     int response;
87     int watch_cursor;
88
89     GtkCellRenderer *prop_renderer;
90     GtkCellRenderer *fixed_renderer;
91   };
92 static void init_assistant (struct import_assistant *, GtkWindow *);
93 static void destroy_assistant (struct import_assistant *);
94 static GtkWidget *add_page_to_assistant (struct import_assistant *,
95                                          GtkWidget *page,
96                                          GtkAssistantPageType);
97
98 /* The introduction page of the assistant. */
99 struct intro_page
100   {
101     GtkWidget *page;
102     GtkWidget *all_cases_button;
103     GtkWidget *n_cases_button;
104     GtkWidget *n_cases_spin;
105     GtkWidget *percent_button;
106     GtkWidget *percent_spin;
107   };
108 static void init_intro_page (struct import_assistant *);
109 static void reset_intro_page (struct import_assistant *);
110
111 /* Page where the user chooses the first line of data. */
112 struct first_line_page
113   {
114     int skip_lines;    /* Number of initial lines to skip? */
115     bool variable_names; /* Variable names above first line of data? */
116
117     GtkWidget *page;
118     PsppSheetView *tree_view;
119     GtkWidget *variable_names_cb;
120   };
121 static void init_first_line_page (struct import_assistant *);
122 static void reset_first_line_page (struct import_assistant *);
123
124 /* Page where the user chooses field separators. */
125 struct separators_page
126   {
127     /* How to break lines into columns. */
128     struct string separators;   /* Field separators. */
129     struct string quotes;       /* Quote characters. */
130     bool escape;                /* Doubled quotes yield a quote mark? */
131
132     /* The columns produced thereby. */
133     struct column *columns;     /* Information about each column. */
134     size_t column_cnt;          /* Number of columns. */
135
136     GtkWidget *page;
137     GtkWidget *custom_cb;
138     GtkWidget *custom_entry;
139     GtkWidget *quote_cb;
140     GtkWidget *quote_combo;
141     GtkEntry *quote_entry;
142     GtkWidget *escape_cb;
143     PsppSheetView *fields_tree_view;
144   };
145 /* The columns that the separators divide the data into. */
146 struct column
147   {
148     /* Variable name for this column.  This is the variable name
149        used on the separators page; it can be overridden by the
150        user on the formats page. */
151     char *name;
152
153     /* Maximum length of any row in this column. */
154     size_t width;
155
156     /* Contents of this column: contents[row] is the contents for
157        the given row.
158
159        A null substring indicates a missing column for that row
160        (because the line contains an insufficient number of
161        separators).
162
163        contents[] elements may be substrings of the lines[]
164        strings that represent the whole lines of the file, to
165        save memory.  Other elements are dynamically allocated
166        with ss_alloc_substring. */
167     struct substring *contents;
168   };
169 static void init_separators_page (struct import_assistant *);
170 static void destroy_separators_page (struct import_assistant *);
171 static void prepare_separators_page (struct import_assistant *);
172 static void reset_separators_page (struct import_assistant *);
173
174 /* Page where the user verifies and adjusts input formats. */
175 struct formats_page
176   {
177     struct dictionary *dict;
178
179     GtkWidget *page;
180     PsppSheetView *data_tree_view;
181     PsppireDict *psppire_dict;
182     struct variable **modified_vars;
183     size_t modified_var_cnt;
184   };
185 static void init_formats_page (struct import_assistant *);
186 static void destroy_formats_page (struct import_assistant *);
187 static void prepare_formats_page (struct import_assistant *);
188 static void reset_formats_page (struct import_assistant *);
189
190 struct import_assistant
191   {
192     struct file file;
193     struct assistant asst;
194     struct intro_page intro;
195     struct first_line_page first_line;
196     struct separators_page separators;
197     struct formats_page formats;
198   };
199
200 static void apply_dict (const struct dictionary *, struct string *);
201 static char *generate_syntax (const struct import_assistant *);
202
203 static gboolean get_tooltip_location (GtkWidget *widget, gint wx, gint wy,
204                                       const struct import_assistant *,
205                                       size_t *row, size_t *column);
206 static void make_tree_view (const struct import_assistant *ia,
207                             size_t first_line,
208                             PsppSheetView **tree_view);
209 static void add_line_number_column (const struct import_assistant *,
210                                     PsppSheetView *);
211 static gint get_monospace_width (PsppSheetView *, GtkCellRenderer *,
212                                  size_t char_cnt);
213 static gint get_string_width (PsppSheetView *, GtkCellRenderer *,
214                               const char *string);
215 static PsppSheetViewColumn *make_data_column (struct import_assistant *,
216                                             PsppSheetView *, bool input,
217                                             gint column_idx);
218 static PsppSheetView *create_data_tree_view (bool input, GtkContainer *parent,
219                                            struct import_assistant *);
220 static void push_watch_cursor (struct import_assistant *);
221 static void pop_watch_cursor (struct import_assistant *);
222
223 /* Pops up the Text Data Import assistant. */
224 void
225 text_data_import_assistant (PsppireDataWindow *dw)
226 {
227   GtkWindow *parent_window = GTK_WINDOW (dw);
228   struct import_assistant *ia;
229
230   ia = xzalloc (sizeof *ia);
231   if (!init_file (ia, parent_window))
232     {
233       free (ia);
234       return;
235     }
236
237   init_assistant (ia, parent_window);
238   init_intro_page (ia);
239   init_first_line_page (ia);
240   init_separators_page (ia);
241   init_formats_page (ia);
242
243   gtk_widget_show_all (GTK_WIDGET (ia->asst.assistant));
244
245   ia->asst.main_loop = g_main_loop_new (NULL, false);
246   g_main_loop_run (ia->asst.main_loop);
247   g_main_loop_unref (ia->asst.main_loop);
248
249   switch (ia->asst.response)
250     {
251     case GTK_RESPONSE_APPLY:
252       free (execute_syntax_string (dw, generate_syntax (ia)));
253       break;
254     case PSPPIRE_RESPONSE_PASTE:
255       free (paste_syntax_to_window (generate_syntax (ia)));
256       break;
257     default:
258       break;
259     }
260
261   destroy_formats_page (ia);
262   destroy_separators_page (ia);
263   destroy_assistant (ia);
264   destroy_file (ia);
265   free (ia);
266 }
267
268 /* Emits PSPP syntax to S that applies the dictionary attributes
269    (such as missing values and value labels) of the variables in
270    DICT.  */
271 static void
272 apply_dict (const struct dictionary *dict, struct string *s)
273 {
274   size_t var_cnt = dict_get_var_cnt (dict);
275   size_t i;
276
277   for (i = 0; i < var_cnt; i++)
278     {
279       struct variable *var = dict_get_var (dict, i);
280       const char *name = var_get_name (var);
281       enum val_type type = var_get_type (var);
282       int width = var_get_width (var);
283       enum measure measure = var_get_measure (var);
284       enum alignment alignment = var_get_alignment (var);
285       const struct fmt_spec *format = var_get_print_format (var);
286
287       if (var_has_missing_values (var))
288         {
289           const struct missing_values *mv = var_get_missing_values (var);
290           size_t j;
291
292           syntax_gen_pspp (s, "MISSING VALUES %ss (", name);
293           for (j = 0; j < mv_n_values (mv); j++)
294             {
295               if (j)
296                 ds_put_cstr (s, ", ");
297               syntax_gen_value (s, mv_get_value (mv, j), width, format);
298             }
299
300           if (mv_has_range (mv))
301             {
302               double low, high;
303               if (mv_has_value (mv))
304                 ds_put_cstr (s, ", ");
305               mv_get_range (mv, &low, &high);
306               syntax_gen_num_range (s, low, high, format);
307             }
308           ds_put_cstr (s, ").\n");
309         }
310       if (var_has_value_labels (var))
311         {
312           const struct val_labs *vls = var_get_value_labels (var);
313           const struct val_lab **labels = val_labs_sorted (vls);
314           size_t n_labels = val_labs_count (vls);
315           size_t i;
316
317           syntax_gen_pspp (s, "VALUE LABELS %ss", name);
318           for (i = 0; i < n_labels; i++)
319             {
320               const struct val_lab *vl = labels[i];
321               ds_put_cstr (s, "\n  ");
322               syntax_gen_value (s, &vl->value, width, format);
323               ds_put_byte (s, ' ');
324               syntax_gen_string (s, ss_cstr (val_lab_get_escaped_label (vl)));
325             }
326           free (labels);
327           ds_put_cstr (s, ".\n");
328         }
329       if (var_has_label (var))
330         syntax_gen_pspp (s, "VARIABLE LABELS %ss %sq.\n",
331                          name, var_get_label (var));
332       if (measure != var_default_measure (type))
333         syntax_gen_pspp (s, "VARIABLE LEVEL %ss (%ss).\n",
334                          name,
335                          (measure == MEASURE_NOMINAL ? "NOMINAL"
336                           : measure == MEASURE_ORDINAL ? "ORDINAL"
337                           : "SCALE"));
338       if (alignment != var_default_alignment (type))
339         syntax_gen_pspp (s, "VARIABLE ALIGNMENT %ss (%ss).\n",
340                          name,
341                          (alignment == ALIGN_LEFT ? "LEFT"
342                           : alignment == ALIGN_CENTRE ? "CENTER"
343                           : "RIGHT"));
344       if (var_get_display_width (var) != var_default_display_width (width))
345         syntax_gen_pspp (s, "VARIABLE WIDTH %ss (%d).\n",
346                          name, var_get_display_width (var));
347     }
348 }
349
350 /* Generates and returns PSPP syntax to execute the import
351    operation described by IA.  The caller must free the syntax
352    with free(). */
353 static char *
354 generate_syntax (const struct import_assistant *ia)
355 {
356   struct string s = DS_EMPTY_INITIALIZER;
357   size_t var_cnt;
358   size_t i;
359
360   syntax_gen_pspp (&s,
361                    "GET DATA\n"
362                    "  /TYPE=TXT\n"
363                    "  /FILE=%sq\n",
364                    ia->file.file_name);
365   if (ia->file.encoding && strcmp (ia->file.encoding, "Auto"))
366     syntax_gen_pspp (&s, "  /ENCODING=%sq\n", ia->file.encoding);
367   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (
368                                       ia->intro.n_cases_button)))
369     ds_put_format (&s, "  /IMPORTCASES=FIRST %d\n",
370                    gtk_spin_button_get_value_as_int (
371                      GTK_SPIN_BUTTON (ia->intro.n_cases_spin)));
372   else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (
373                                            ia->intro.percent_button)))
374     ds_put_format (&s, "  /IMPORTCASES=PERCENT %d\n",
375                    gtk_spin_button_get_value_as_int (
376                      GTK_SPIN_BUTTON (ia->intro.percent_spin)));
377   else
378     ds_put_cstr (&s, "  /IMPORTCASES=ALL\n");
379   ds_put_cstr (&s,
380                "  /ARRANGEMENT=DELIMITED\n"
381                "  /DELCASE=LINE\n");
382   if (ia->first_line.skip_lines > 0)
383     ds_put_format (&s, "  /FIRSTCASE=%d\n", ia->first_line.skip_lines + 1);
384   ds_put_cstr (&s, "  /DELIMITERS=\"");
385   if (ds_find_byte (&ia->separators.separators, '\t') != SIZE_MAX)
386     ds_put_cstr (&s, "\\t");
387   if (ds_find_byte (&ia->separators.separators, '\\') != SIZE_MAX)
388     ds_put_cstr (&s, "\\\\");
389   for (i = 0; i < ds_length (&ia->separators.separators); i++)
390     {
391       char c = ds_at (&ia->separators.separators, i);
392       if (c == '"')
393         ds_put_cstr (&s, "\"\"");
394       else if (c != '\t' && c != '\\')
395         ds_put_byte (&s, c);
396     }
397   ds_put_cstr (&s, "\"\n");
398   if (!ds_is_empty (&ia->separators.quotes))
399     syntax_gen_pspp (&s, "  /QUALIFIER=%sq\n", ds_cstr (&ia->separators.quotes));
400   if (!ds_is_empty (&ia->separators.quotes) && ia->separators.escape)
401     ds_put_cstr (&s, "  /ESCAPE\n");
402   ds_put_cstr (&s, "  /VARIABLES=\n");
403
404   var_cnt = dict_get_var_cnt (ia->formats.dict);
405   for (i = 0; i < var_cnt; i++)
406     {
407       struct variable *var = dict_get_var (ia->formats.dict, i);
408       char format_string[FMT_STRING_LEN_MAX + 1];
409       fmt_to_string (var_get_print_format (var), format_string);
410       ds_put_format (&s, "    %s %s%s\n",
411                      var_get_name (var), format_string,
412                      i == var_cnt - 1 ? "." : "");
413     }
414
415   apply_dict (ia->formats.dict, &s);
416
417   return ds_cstr (&s);
418 }
419 \f
420 /* Choosing a file and reading it. */
421
422 static char *choose_file (GtkWindow *parent_window, gchar **encodingp);
423
424 /* Obtains the file to import from the user and initializes IA's
425    file substructure.  PARENT_WINDOW must be the window to use
426    as the file chooser window's parent.
427
428    Returns true if successful, false if the file name could not
429    be obtained or the file could not be read. */
430 static bool
431 init_file (struct import_assistant *ia, GtkWindow *parent_window)
432 {
433   struct file *file = &ia->file;
434   enum { MAX_PREVIEW_LINES = 1000 }; /* Max number of lines to read. */
435   enum { MAX_LINE_LEN = 16384 }; /* Max length of an acceptable line. */
436   struct line_reader *reader;
437
438   file->file_name = choose_file (parent_window, &file->encoding);
439   if (file->file_name == NULL)
440     return false;
441
442   reader = line_reader_for_file (file->encoding, file->file_name, O_RDONLY);
443   if (reader == NULL)
444     {
445       msg (ME, _("Could not open `%s': %s"),
446            file->file_name, strerror (errno));
447       return false;
448     }
449
450   file->lines = xnmalloc (MAX_PREVIEW_LINES, sizeof *file->lines);
451   for (; file->line_cnt < MAX_PREVIEW_LINES; file->line_cnt++)
452     {
453       struct string *line = &file->lines[file->line_cnt];
454
455       ds_init_empty (line);
456       if (!line_reader_read (reader, line, MAX_LINE_LEN + 1)
457           || ds_length (line) > MAX_LINE_LEN)
458         {
459           if (line_reader_eof (reader))
460             break;
461           else if (line_reader_error (reader))
462             msg (ME, _("Error reading `%s': %s"),
463                  file->file_name, strerror (line_reader_error (reader)));
464           else
465             msg (ME, _("Failed to read `%s', because it contains a line "
466                        "over %d bytes long and therefore appears not to be "
467                        "a text file."),
468                  file->file_name, MAX_LINE_LEN);
469           line_reader_close (reader);
470           destroy_file (ia);
471           return false;
472         }
473     }
474
475   if (file->line_cnt == 0)
476     {
477       msg (ME, _("`%s' is empty."), file->file_name);
478       line_reader_close (reader);
479       destroy_file (ia);
480       return false;
481     }
482
483   /* Estimate the number of lines in the file. */
484   if (file->line_cnt < MAX_PREVIEW_LINES)
485     file->total_lines = file->line_cnt;
486   else
487     {
488       struct stat s;
489       off_t position = line_reader_tell (reader);
490       if (fstat (line_reader_fileno (reader), &s) == 0 && position > 0)
491         file->total_lines = (double) file->line_cnt / position * s.st_size;
492       else
493         file->total_lines = 0;
494     }
495
496   line_reader_close (reader);
497
498   return true;
499 }
500
501 /* Frees IA's file substructure. */
502 static void
503 destroy_file (struct import_assistant *ia)
504 {
505   struct file *f = &ia->file;
506   size_t i;
507
508   for (i = 0; i < f->line_cnt; i++)
509     ds_destroy (&f->lines[i]);
510   free (f->lines);
511   g_free (f->file_name);
512   g_free (f->encoding);
513 }
514
515 /* Obtains the file to read from the user.  If successful, returns the name of
516    the file and stores the user's chosen encoding for the file into *ENCODINGP.
517    The caller must free each of these strings with g_free().
518
519    On failure, stores a null pointer and stores NULL in *ENCODINGP.
520
521    PARENT_WINDOW must be the window to use as the file chooser window's
522    parent. */
523 static char *
524 choose_file (GtkWindow *parent_window, gchar **encodingp)
525 {
526   char *file_name;
527   GtkFileFilter *filter = NULL;
528
529   GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Import Delimited Text Data"),
530                                         parent_window,
531                                         GTK_FILE_CHOOSER_ACTION_OPEN,
532                                         GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
533                                         GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
534                                         NULL);
535
536   g_object_set (dialog, "local-only", FALSE, NULL);
537
538   filter = gtk_file_filter_new ();
539   gtk_file_filter_set_name (filter, _("Text files"));
540   gtk_file_filter_add_mime_type (filter, "text/*");
541   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
542
543   filter = gtk_file_filter_new ();
544   gtk_file_filter_set_name (filter, _("Text (*.txt) Files"));
545   gtk_file_filter_add_pattern (filter, "*.txt");
546   gtk_file_filter_add_pattern (filter, "*.TXT");
547   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
548
549   filter = gtk_file_filter_new ();
550   gtk_file_filter_set_name (filter, _("Plain Text (ASCII) Files"));
551   gtk_file_filter_add_mime_type (filter, "text/plain");
552   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
553
554   filter = gtk_file_filter_new ();
555   gtk_file_filter_set_name (filter, _("Comma Separated Value Files"));
556   gtk_file_filter_add_mime_type (filter, "text/csv");
557   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
558
559   /* I've never encountered one of these, but it's listed here:
560      http://www.iana.org/assignments/media-types/text/tab-separated-values  */
561   filter = gtk_file_filter_new ();
562   gtk_file_filter_set_name (filter, _("Tab Separated Value Files"));
563   gtk_file_filter_add_mime_type (filter, "text/tab-separated-values");
564   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
565
566   gtk_file_chooser_set_extra_widget (
567     GTK_FILE_CHOOSER (dialog), psppire_encoding_selector_new ("Auto", true));
568
569   filter = gtk_file_filter_new ();
570   gtk_file_filter_set_name (filter, _("All Files"));
571   gtk_file_filter_add_pattern (filter, "*");
572   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
573
574   switch (gtk_dialog_run (GTK_DIALOG (dialog)))
575     {
576     case GTK_RESPONSE_ACCEPT:
577       file_name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
578       *encodingp = psppire_encoding_selector_get_encoding (
579         gtk_file_chooser_get_extra_widget (GTK_FILE_CHOOSER (dialog)));
580       break;
581     default:
582       file_name = NULL;
583       *encodingp = NULL;
584       break;
585     }
586   gtk_widget_destroy (dialog);
587
588   return file_name;
589 }
590 \f
591 /* Assistant. */
592
593 static void close_assistant (struct import_assistant *, int response);
594 static void on_prepare (GtkAssistant *assistant, GtkWidget *page,
595                         struct import_assistant *);
596 static void on_cancel (GtkAssistant *assistant, struct import_assistant *);
597 static void on_close (GtkAssistant *assistant, struct import_assistant *);
598 static void on_paste (GtkButton *button, struct import_assistant *);
599 static void on_reset (GtkButton *button, struct import_assistant *);
600 static void close_assistant (struct import_assistant *, int response);
601
602 /* Initializes IA's asst substructure.  PARENT_WINDOW must be the
603    window to use as the assistant window's parent.  */
604 static void
605 init_assistant (struct import_assistant *ia, GtkWindow *parent_window)
606 {
607   struct assistant *a = &ia->asst;
608
609   a->builder = builder_new ("text-data-import.ui");
610   a->assistant = GTK_ASSISTANT (gtk_assistant_new ());
611   g_signal_connect (a->assistant, "prepare", G_CALLBACK (on_prepare), ia);
612   g_signal_connect (a->assistant, "cancel", G_CALLBACK (on_cancel), ia);
613   g_signal_connect (a->assistant, "close", G_CALLBACK (on_close), ia);
614   a->paste_button = gtk_button_new_from_stock (GTK_STOCK_PASTE);
615   gtk_assistant_add_action_widget (a->assistant, a->paste_button);
616   g_signal_connect (a->paste_button, "clicked", G_CALLBACK (on_paste), ia);
617   a->reset_button = gtk_button_new_from_stock ("pspp-stock-reset");
618   gtk_assistant_add_action_widget (a->assistant, a->reset_button);
619   g_signal_connect (a->reset_button, "clicked", G_CALLBACK (on_reset), ia);
620   gtk_window_set_title (GTK_WINDOW (a->assistant),
621                         _("Importing Delimited Text Data"));
622   gtk_window_set_transient_for (GTK_WINDOW (a->assistant), parent_window);
623   gtk_window_set_icon_name (GTK_WINDOW (a->assistant), "pspp");
624
625   a->prop_renderer = gtk_cell_renderer_text_new ();
626   g_object_ref_sink (a->prop_renderer);
627   a->fixed_renderer = gtk_cell_renderer_text_new ();
628   g_object_ref_sink (a->fixed_renderer);
629   g_object_set (G_OBJECT (a->fixed_renderer),
630                 "family", "Monospace",
631                 (void *) NULL);
632 }
633
634 /* Frees IA's asst substructure. */
635 static void
636 destroy_assistant (struct import_assistant *ia)
637 {
638   struct assistant *a = &ia->asst;
639
640   g_object_unref (a->prop_renderer);
641   g_object_unref (a->fixed_renderer);
642   g_object_unref (a->builder);
643 }
644
645 /* Appends a page of the given TYPE, with PAGE as its content, to
646    the GtkAssistant encapsulated by IA.  Returns the GtkWidget
647    that represents the page. */
648 static GtkWidget *
649 add_page_to_assistant (struct import_assistant *ia,
650                        GtkWidget *page, GtkAssistantPageType type)
651 {
652   const char *title;
653   char *title_copy;
654   GtkWidget *content;
655
656   title = gtk_window_get_title (GTK_WINDOW (page));
657   title_copy = xstrdup (title ? title : "");
658
659   content = gtk_bin_get_child (GTK_BIN (page));
660   assert (content);
661   g_object_ref (content);
662   gtk_container_remove (GTK_CONTAINER (page), content);
663
664   gtk_widget_destroy (page);
665
666   gtk_assistant_append_page (ia->asst.assistant, content);
667   gtk_assistant_set_page_type (ia->asst.assistant, content, type);
668   gtk_assistant_set_page_title (ia->asst.assistant, content, title_copy);
669   gtk_assistant_set_page_complete (ia->asst.assistant, content, true);
670
671   free (title_copy);
672
673   return content;
674 }
675
676 /* Called just before PAGE is displayed as the current page of
677    ASSISTANT, this updates IA content according to the new
678    page. */
679 static void
680 on_prepare (GtkAssistant *assistant, GtkWidget *page,
681             struct import_assistant *ia)
682 {
683
684   if (gtk_assistant_get_page_type (assistant, page)
685       == GTK_ASSISTANT_PAGE_CONFIRM)
686     gtk_widget_grab_focus (assistant->apply);
687   else
688     gtk_widget_grab_focus (assistant->forward);
689
690   if (page == ia->separators.page)
691     prepare_separators_page (ia);
692   else if (page == ia->formats.page)
693     prepare_formats_page (ia);
694
695   gtk_widget_show (ia->asst.reset_button);
696   if (page == ia->formats.page)
697     gtk_widget_show (ia->asst.paste_button);
698   else
699     gtk_widget_hide (ia->asst.paste_button);
700 }
701
702 /* Called when the Cancel button in the assistant is clicked. */
703 static void
704 on_cancel (GtkAssistant *assistant, struct import_assistant *ia)
705 {
706   close_assistant (ia, GTK_RESPONSE_CANCEL);
707 }
708
709 /* Called when the Apply button on the last page of the assistant
710    is clicked. */
711 static void
712 on_close (GtkAssistant *assistant, struct import_assistant *ia)
713 {
714   close_assistant (ia, GTK_RESPONSE_APPLY);
715 }
716
717 /* Called when the Paste button on the last page of the assistant
718    is clicked. */
719 static void
720 on_paste (GtkButton *button, struct import_assistant *ia)
721 {
722   close_assistant (ia, PSPPIRE_RESPONSE_PASTE);
723 }
724
725 /* Called when the Reset button is clicked. */
726 static void
727 on_reset (GtkButton *button, struct import_assistant *ia)
728 {
729   gint page_num = gtk_assistant_get_current_page (ia->asst.assistant);
730   GtkWidget *page = gtk_assistant_get_nth_page (ia->asst.assistant, page_num);
731
732   if (page == ia->intro.page)
733     reset_intro_page (ia);
734   else if (page == ia->first_line.page)
735     reset_first_line_page (ia);
736   else if (page == ia->separators.page)
737     reset_separators_page (ia);
738   else if (page == ia->formats.page)
739     reset_formats_page (ia);
740 }
741
742 /* Causes the assistant to close, returning RESPONSE for
743    interpretation by text_data_import_assistant. */
744 static void
745 close_assistant (struct import_assistant *ia, int response)
746 {
747   ia->asst.response = response;
748   g_main_loop_quit (ia->asst.main_loop);
749   gtk_widget_hide (GTK_WIDGET (ia->asst.assistant));
750 }
751 \f
752 /* The "intro" page of the assistant. */
753
754 static void on_intro_amount_changed (struct import_assistant *);
755
756 /* Initializes IA's intro substructure. */
757 static void
758 init_intro_page (struct import_assistant *ia)
759 {
760   GtkBuilder *builder = ia->asst.builder;
761   struct intro_page *p = &ia->intro;
762   struct string s;
763   GtkWidget *hbox_n_cases ;
764   GtkWidget *hbox_percent ;
765   GtkWidget *table ;
766
767
768   p->n_cases_spin = gtk_spin_button_new_with_range (0, INT_MAX, 100);
769
770   hbox_n_cases = psppire_scanf_new (_("Only the first %4d cases"), &p->n_cases_spin);
771
772   table  = get_widget_assert (builder, "button-table");
773
774   gtk_table_attach_defaults (GTK_TABLE (table), hbox_n_cases,
775                     1, 2,
776                     1, 2);
777
778   p->percent_spin = gtk_spin_button_new_with_range (0, 100, 10);
779
780   hbox_percent = psppire_scanf_new (_("Only the first %3d %% of file (approximately)"), &p->percent_spin);
781
782   gtk_table_attach_defaults (GTK_TABLE (table), hbox_percent,
783                              1, 2,
784                              2, 3);
785
786   p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Intro"),
787                                    GTK_ASSISTANT_PAGE_INTRO);
788
789   p->all_cases_button = get_widget_assert (builder, "import-all-cases");
790
791   p->n_cases_button = get_widget_assert (builder, "import-n-cases");
792
793   p->percent_button = get_widget_assert (builder, "import-percent");
794
795   g_signal_connect_swapped (p->all_cases_button, "toggled",
796                     G_CALLBACK (on_intro_amount_changed), ia);
797   g_signal_connect_swapped (p->n_cases_button, "toggled",
798                     G_CALLBACK (on_intro_amount_changed), ia);
799   g_signal_connect_swapped (p->percent_button, "toggled",
800                     G_CALLBACK (on_intro_amount_changed), ia);
801
802   on_intro_amount_changed (ia);
803
804   ds_init_empty (&s);
805   ds_put_cstr (&s, _("This assistant will guide you through the process of "
806                      "importing data into PSPP from a text file with one line "
807                      "per case,  in which fields are separated by tabs, "
808                      "commas, or other delimiters.\n\n"));
809   if (ia->file.total_is_exact)
810     ds_put_format (
811       &s, ngettext ("The selected file contains %zu line of text.  ",
812                     "The selected file contains %zu lines of text.  ",
813                     ia->file.line_cnt),
814       ia->file.line_cnt);
815   else if (ia->file.total_lines > 0)
816     {
817       ds_put_format (
818         &s, ngettext (
819           "The selected file contains approximately %lu line of text.  ",
820           "The selected file contains approximately %lu lines of text.  ",
821           ia->file.total_lines),
822         ia->file.total_lines);
823       ds_put_format (
824         &s, ngettext (
825           "Only the first %zu line of the file will be shown for "
826           "preview purposes in the following screens.  ",
827           "Only the first %zu lines of the file will be shown for "
828           "preview purposes in the following screens.  ",
829           ia->file.line_cnt),
830         ia->file.line_cnt);
831     }
832   ds_put_cstr (&s, _("You may choose below how much of the file should "
833                      "actually be imported."));
834   gtk_label_set_text (GTK_LABEL (get_widget_assert (builder, "intro-label")),
835                       ds_cstr (&s));
836   ds_destroy (&s);
837 }
838
839 /* Resets IA's intro page to its initial state. */
840 static void
841 reset_intro_page (struct import_assistant *ia)
842 {
843   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->intro.all_cases_button),
844                                 true);
845 }
846
847 /* Called when one of the radio buttons is clicked. */
848 static void
849 on_intro_amount_changed (struct import_assistant *ia)
850 {
851   struct intro_page *p = &ia->intro;
852
853   gtk_widget_set_sensitive (p->n_cases_spin,
854                             gtk_toggle_button_get_active (
855                               GTK_TOGGLE_BUTTON (p->n_cases_button)));
856
857   gtk_widget_set_sensitive (p->percent_spin,
858                             gtk_toggle_button_get_active (
859                               GTK_TOGGLE_BUTTON (p->percent_button)));
860 }
861 \f
862 /* The "first line" page of the assistant. */
863
864 static PsppSheetView *create_lines_tree_view (GtkContainer *parent_window,
865                                             struct import_assistant *);
866 static void on_first_line_change (PsppSheetSelection *,
867                                   struct import_assistant *);
868 static void on_variable_names_cb_toggle (GtkToggleButton *,
869                                          struct import_assistant *);
870 static void set_first_line (struct import_assistant *);
871 static void get_first_line (struct import_assistant *);
872
873 /* Initializes IA's first_line substructure. */
874 static void
875 init_first_line_page (struct import_assistant *ia)
876 {
877   struct first_line_page *p = &ia->first_line;
878   GtkBuilder *builder = ia->asst.builder;
879
880   p->page = add_page_to_assistant (ia, get_widget_assert (builder, "FirstLine"),
881                                    GTK_ASSISTANT_PAGE_CONTENT);
882   gtk_widget_destroy (get_widget_assert (builder, "first-line"));
883   p->tree_view = create_lines_tree_view (
884     GTK_CONTAINER (get_widget_assert (builder, "first-line-scroller")), ia);
885   p->variable_names_cb = get_widget_assert (builder, "variable-names");
886   pspp_sheet_selection_set_mode (
887     pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (p->tree_view)),
888     PSPP_SHEET_SELECTION_BROWSE);
889   pspp_sheet_view_set_rubber_banding (PSPP_SHEET_VIEW (p->tree_view), TRUE);
890   set_first_line (ia);
891   g_signal_connect (pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (p->tree_view)),
892                     "changed", G_CALLBACK (on_first_line_change), ia);
893   g_signal_connect (p->variable_names_cb, "toggled",
894                     G_CALLBACK (on_variable_names_cb_toggle), ia);
895 }
896
897 /* Resets the first_line page to its initial content. */
898 static void
899 reset_first_line_page (struct import_assistant *ia)
900 {
901   ia->first_line.skip_lines = 0;
902   ia->first_line.variable_names = false;
903   set_first_line (ia);
904 }
905
906 static void
907 render_line (PsppSheetViewColumn *tree_column,
908              GtkCellRenderer *cell,
909              GtkTreeModel *tree_model,
910              GtkTreeIter *iter,
911              gpointer data)
912 {
913   gint row = empty_list_store_iter_to_row (iter);
914   struct string *lines;
915
916   lines = g_object_get_data (G_OBJECT (tree_model), "lines");
917   g_return_if_fail (lines != NULL);
918
919   g_object_set (cell, "text", ds_cstr (&lines[row]), NULL);
920 }
921
922
923 /* Creates and returns a tree view that contains each of the
924    lines in IA's file as a row. */
925 static PsppSheetView *
926 create_lines_tree_view (GtkContainer *parent, struct import_assistant *ia)
927 {
928   PsppSheetView *tree_view;
929   PsppSheetViewColumn *column;
930   size_t max_line_length;
931   gint content_width, header_width;
932   size_t i;
933   const gchar *title = _("Text");
934
935   make_tree_view (ia, 0, &tree_view);
936
937   column = pspp_sheet_view_column_new_with_attributes (
938     title, ia->asst.fixed_renderer, (void *) NULL);
939   pspp_sheet_view_column_set_cell_data_func (column, ia->asst.fixed_renderer,
940                                            render_line, NULL, NULL);
941   pspp_sheet_view_column_set_resizable (column, TRUE);
942
943   max_line_length = 0;
944   for (i = 0; i < ia->file.line_cnt; i++)
945     {
946       size_t w = ds_length (&ia->file.lines[i]);
947       max_line_length = MAX (max_line_length, w);
948     }
949
950   content_width = get_monospace_width (tree_view, ia->asst.fixed_renderer,
951                                        max_line_length);
952   header_width = get_string_width (tree_view, ia->asst.prop_renderer, title);
953   pspp_sheet_view_column_set_fixed_width (column, MAX (content_width,
954                                                      header_width));
955   pspp_sheet_view_append_column (tree_view, column);
956
957   gtk_container_add (parent, GTK_WIDGET (tree_view));
958   gtk_widget_show (GTK_WIDGET (tree_view));
959
960   return tree_view;
961 }
962
963 /* Called when the line selected in the first_line tree view
964    changes. */
965 static void
966 on_first_line_change (PsppSheetSelection *selection UNUSED,
967                       struct import_assistant *ia)
968 {
969   get_first_line (ia);
970 }
971
972 /* Called when the checkbox that indicates whether variable
973    names are in the row above the first line is toggled. */
974 static void
975 on_variable_names_cb_toggle (GtkToggleButton *variable_names_cb UNUSED,
976                              struct import_assistant *ia)
977 {
978   get_first_line (ia);
979 }
980
981 /* Sets the widgets to match IA's first_line substructure. */
982 static void
983 set_first_line (struct import_assistant *ia)
984 {
985   GtkTreePath *path;
986
987   path = gtk_tree_path_new_from_indices (ia->first_line.skip_lines, -1);
988   pspp_sheet_view_set_cursor (PSPP_SHEET_VIEW (ia->first_line.tree_view),
989                             path, NULL, false);
990   gtk_tree_path_free (path);
991
992   gtk_toggle_button_set_active (
993     GTK_TOGGLE_BUTTON (ia->first_line.variable_names_cb),
994     ia->first_line.variable_names);
995   gtk_widget_set_sensitive (ia->first_line.variable_names_cb,
996                             ia->first_line.skip_lines > 0);
997 }
998
999 /* Sets IA's first_line substructure to match the widgets. */
1000 static void
1001 get_first_line (struct import_assistant *ia)
1002 {
1003   PsppSheetSelection *selection;
1004   GtkTreeIter iter;
1005   GtkTreeModel *model;
1006
1007   selection = pspp_sheet_view_get_selection (ia->first_line.tree_view);
1008   if (pspp_sheet_selection_get_selected (selection, &model, &iter))
1009     {
1010       GtkTreePath *path = gtk_tree_model_get_path (model, &iter);
1011       int row = gtk_tree_path_get_indices (path)[0];
1012       gtk_tree_path_free (path);
1013
1014       ia->first_line.skip_lines = row;
1015       ia->first_line.variable_names =
1016         (ia->first_line.skip_lines > 0
1017          && gtk_toggle_button_get_active (
1018            GTK_TOGGLE_BUTTON (ia->first_line.variable_names_cb)));
1019     }
1020   gtk_widget_set_sensitive (ia->first_line.variable_names_cb,
1021                             ia->first_line.skip_lines > 0);
1022 }
1023 \f
1024 /* The "separators" page of the assistant. */
1025
1026 static void revise_fields_preview (struct import_assistant *ia);
1027 static void choose_likely_separators (struct import_assistant *ia);
1028 static void find_commonest_chars (unsigned long int histogram[UCHAR_MAX + 1],
1029                                   const char *targets, const char *def,
1030                                   struct string *result);
1031 static void clear_fields (struct import_assistant *ia);
1032 static void revise_fields_preview (struct import_assistant *);
1033 static void set_separators (struct import_assistant *);
1034 static void get_separators (struct import_assistant *);
1035 static void on_separators_custom_entry_notify (GObject *UNUSED,
1036                                                GParamSpec *UNUSED,
1037                                                struct import_assistant *);
1038 static void on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
1039                                             struct import_assistant *);
1040 static void on_quote_combo_change (GtkComboBox *combo,
1041                                    struct import_assistant *);
1042 static void on_quote_cb_toggle (GtkToggleButton *quote_cb,
1043                                 struct import_assistant *);
1044 static void on_separator_toggle (GtkToggleButton *, struct import_assistant *);
1045 static void render_input_cell (PsppSheetViewColumn *tree_column,
1046                                GtkCellRenderer *cell,
1047                                GtkTreeModel *model, GtkTreeIter *iter,
1048                                gpointer ia);
1049 static gboolean on_query_input_tooltip (GtkWidget *widget, gint wx, gint wy,
1050                                         gboolean keyboard_mode UNUSED,
1051                                         GtkTooltip *tooltip,
1052                                         struct import_assistant *);
1053
1054 /* A common field separator and its identifying name. */
1055 struct separator
1056   {
1057     const char *name;           /* Name (for use with get_widget_assert). */
1058     int c;                      /* Separator character. */
1059   };
1060
1061 /* All the separators in the dialog box. */
1062 static const struct separator separators[] =
1063   {
1064     {"space", ' '},
1065     {"tab", '\t'},
1066     {"bang", '!'},
1067     {"colon", ':'},
1068     {"comma", ','},
1069     {"hyphen", '-'},
1070     {"pipe", '|'},
1071     {"semicolon", ';'},
1072     {"slash", '/'},
1073   };
1074 #define SEPARATOR_CNT (sizeof separators / sizeof *separators)
1075
1076 static void
1077 set_quote_list (GtkComboBoxEntry *cb)
1078 {
1079   GtkListStore *list =  gtk_list_store_new (1, G_TYPE_STRING);
1080   GtkTreeIter iter;
1081   gint i;
1082   const gchar *seperator[3] = {"'\"", "\'", "\""};
1083
1084   for (i = 0; i < 3; i++)
1085     {
1086       const gchar *s = seperator[i];
1087
1088       /* Add a new row to the model */
1089       gtk_list_store_append (list, &iter);
1090       gtk_list_store_set (list, &iter,
1091                           0, s,
1092                           -1);
1093
1094     }
1095
1096   gtk_combo_box_set_model (GTK_COMBO_BOX (cb), GTK_TREE_MODEL (list));
1097   g_object_unref (list);
1098
1099   gtk_combo_box_entry_set_text_column (cb, 0);
1100 }
1101
1102 /* Initializes IA's separators substructure. */
1103 static void
1104 init_separators_page (struct import_assistant *ia)
1105 {
1106   GtkBuilder *builder = ia->asst.builder;
1107   struct separators_page *p = &ia->separators;
1108   size_t i;
1109
1110   choose_likely_separators (ia);
1111
1112   p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Separators"),
1113                                    GTK_ASSISTANT_PAGE_CONTENT);
1114   p->custom_cb = get_widget_assert (builder, "custom-cb");
1115   p->custom_entry = get_widget_assert (builder, "custom-entry");
1116   p->quote_combo = get_widget_assert (builder, "quote-combo");
1117   p->quote_entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (p->quote_combo)));
1118   p->quote_cb = get_widget_assert (builder, "quote-cb");
1119   p->escape_cb = get_widget_assert (builder, "escape");
1120
1121   set_separators (ia);
1122   set_quote_list (GTK_COMBO_BOX_ENTRY (p->quote_combo));
1123   p->fields_tree_view = PSPP_SHEET_VIEW (get_widget_assert (builder, "fields"));
1124   g_signal_connect (p->quote_combo, "changed",
1125                     G_CALLBACK (on_quote_combo_change), ia);
1126   g_signal_connect (p->quote_cb, "toggled",
1127                     G_CALLBACK (on_quote_cb_toggle), ia);
1128   g_signal_connect (p->custom_entry, "notify::text",
1129                     G_CALLBACK (on_separators_custom_entry_notify), ia);
1130   g_signal_connect (p->custom_cb, "toggled",
1131                     G_CALLBACK (on_separators_custom_cb_toggle), ia);
1132   for (i = 0; i < SEPARATOR_CNT; i++)
1133     g_signal_connect (get_widget_assert (builder, separators[i].name),
1134                       "toggled", G_CALLBACK (on_separator_toggle), ia);
1135   g_signal_connect (p->escape_cb, "toggled",
1136                     G_CALLBACK (on_separator_toggle), ia);
1137 }
1138
1139 /* Frees IA's separators substructure. */
1140 static void
1141 destroy_separators_page (struct import_assistant *ia)
1142 {
1143   struct separators_page *s = &ia->separators;
1144
1145   ds_destroy (&s->separators);
1146   ds_destroy (&s->quotes);
1147   clear_fields (ia);
1148 }
1149
1150 /* Called just before the separators page becomes visible in the
1151    assistant. */
1152 static void
1153 prepare_separators_page (struct import_assistant *ia)
1154 {
1155   revise_fields_preview (ia);
1156 }
1157
1158 /* Called when the Reset button is clicked on the separators
1159    page, resets the separators to the defaults. */
1160 static void
1161 reset_separators_page (struct import_assistant *ia)
1162 {
1163   choose_likely_separators (ia);
1164   set_separators (ia);
1165 }
1166
1167 /* Frees and clears the column data in IA's separators
1168    substructure. */
1169 static void
1170 clear_fields (struct import_assistant *ia)
1171 {
1172   struct separators_page *s = &ia->separators;
1173
1174   if (s->column_cnt > 0)
1175     {
1176       struct column *col;
1177       size_t row;
1178
1179       for (row = 0; row < ia->file.line_cnt; row++)
1180         {
1181           const struct string *line = &ia->file.lines[row];
1182           const char *line_start = ds_data (line);
1183           const char *line_end = ds_end (line);
1184
1185           for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1186             {
1187               char *s = ss_data (col->contents[row]);
1188               if (!(s >= line_start && s <= line_end))
1189                 ss_dealloc (&col->contents[row]);
1190             }
1191         }
1192
1193       for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1194         {
1195           free (col->name);
1196           free (col->contents);
1197         }
1198
1199       free (s->columns);
1200       s->columns = NULL;
1201       s->column_cnt = 0;
1202     }
1203 }
1204
1205 /* Breaks the file data in IA into columns based on the
1206    separators set in IA's separators substructure. */
1207 static void
1208 split_fields (struct import_assistant *ia)
1209 {
1210   struct separators_page *s = &ia->separators;
1211   size_t columns_allocated;
1212   bool space_sep;
1213   size_t row;
1214
1215   clear_fields (ia);
1216
1217   /* Is space in the set of separators? */
1218   space_sep = ss_find_byte (ds_ss (&s->separators), ' ') != SIZE_MAX;
1219
1220   /* Split all the lines, not just those from
1221      ia->first_line.skip_lines on, so that we split the line that
1222      contains variables names if ia->first_line.variable_names is
1223      true. */
1224   columns_allocated = 0;
1225   for (row = 0; row < ia->file.line_cnt; row++)
1226     {
1227       struct string *line = &ia->file.lines[row];
1228       struct substring text = ds_ss (line);
1229       size_t column_idx;
1230
1231       for (column_idx = 0; ; column_idx++)
1232         {
1233           struct substring field;
1234           struct column *column;
1235
1236           if (space_sep)
1237             ss_ltrim (&text, ss_cstr (" "));
1238           if (ss_is_empty (text))
1239             {
1240               if (column_idx != 0)
1241                 break;
1242               field = text;
1243             }
1244           else if (!ds_is_empty (&s->quotes)
1245                    && ds_find_byte (&s->quotes, text.string[0]) != SIZE_MAX)
1246             {
1247               int quote = ss_get_byte (&text);
1248               if (!s->escape)
1249                 ss_get_until (&text, quote, &field);
1250               else
1251                 {
1252                   struct string s;
1253                   int c;
1254
1255                   ds_init_empty (&s);
1256                   while ((c = ss_get_byte (&text)) != EOF)
1257                     if (c != quote)
1258                       ds_put_byte (&s, c);
1259                     else if (ss_match_byte (&text, quote))
1260                       ds_put_byte (&s, quote);
1261                     else
1262                       break;
1263                   field = ds_ss (&s);
1264                 }
1265             }
1266           else
1267             ss_get_bytes (&text, ss_cspan (text, ds_ss (&s->separators)),
1268                           &field);
1269
1270           if (column_idx >= s->column_cnt)
1271             {
1272               struct column *column;
1273
1274               if (s->column_cnt >= columns_allocated)
1275                 s->columns = x2nrealloc (s->columns, &columns_allocated,
1276                                          sizeof *s->columns);
1277               column = &s->columns[s->column_cnt++];
1278               column->name = NULL;
1279               column->width = 0;
1280               column->contents = xcalloc (ia->file.line_cnt,
1281                                           sizeof *column->contents);
1282             }
1283           column = &s->columns[column_idx];
1284           column->contents[row] = field;
1285           if (ss_length (field) > column->width)
1286             column->width = ss_length (field);
1287
1288           if (space_sep)
1289             ss_ltrim (&text, ss_cstr (" "));
1290           if (ss_is_empty (text))
1291             break;
1292           if (ss_find_byte (ds_ss (&s->separators), ss_first (text))
1293               != SIZE_MAX)
1294             ss_advance (&text, 1);
1295         }
1296     }
1297 }
1298
1299 /* Chooses a name for each column on the separators page */
1300 static void
1301 choose_column_names (struct import_assistant *ia)
1302 {
1303   const struct first_line_page *f = &ia->first_line;
1304   struct separators_page *s = &ia->separators;
1305   struct dictionary *dict;
1306   unsigned long int generated_name_count = 0;
1307   struct column *col;
1308   size_t name_row;
1309
1310   dict = dict_create (get_default_encoding ());
1311   name_row = f->variable_names && f->skip_lines ? f->skip_lines : 0;
1312   for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1313     {
1314       char *hint, *name;
1315
1316       hint = name_row ? ss_xstrdup (col->contents[name_row - 1]) : NULL;
1317       name = dict_make_unique_var_name (dict, hint, &generated_name_count);
1318       free (hint);
1319
1320       col->name = name;
1321       dict_create_var_assert (dict, name, 0);
1322     }
1323   dict_destroy (dict);
1324 }
1325
1326 /* Picks the most likely separator and quote characters based on
1327    IA's file data. */
1328 static void
1329 choose_likely_separators (struct import_assistant *ia)
1330 {
1331   unsigned long int histogram[UCHAR_MAX + 1] = { 0 };
1332   size_t row;
1333
1334   /* Construct a histogram of all the characters used in the
1335      file. */
1336   for (row = 0; row < ia->file.line_cnt; row++)
1337     {
1338       struct substring line = ds_ss (&ia->file.lines[row]);
1339       size_t length = ss_length (line);
1340       size_t i;
1341       for (i = 0; i < length; i++)
1342         histogram[(unsigned char) line.string[i]]++;
1343     }
1344
1345   find_commonest_chars (histogram, "\"'", "", &ia->separators.quotes);
1346   find_commonest_chars (histogram, ",;:/|!\t-", ",",
1347                         &ia->separators.separators);
1348   ia->separators.escape = true;
1349 }
1350
1351 /* Chooses the most common character among those in TARGETS,
1352    based on the frequency data in HISTOGRAM, and stores it in
1353    RESULT.  If there is a tie for the most common character among
1354    those in TARGETS, the earliest character is chosen.  If none
1355    of the TARGETS appear at all, then DEF is used as a
1356    fallback. */
1357 static void
1358 find_commonest_chars (unsigned long int histogram[UCHAR_MAX + 1],
1359                       const char *targets, const char *def,
1360                       struct string *result)
1361 {
1362   unsigned char max = 0;
1363   unsigned long int max_count = 0;
1364
1365   for (; *targets != '\0'; targets++)
1366     {
1367       unsigned char c = *targets;
1368       unsigned long int count = histogram[c];
1369       if (count > max_count)
1370         {
1371           max = c;
1372           max_count = count;
1373         }
1374     }
1375   if (max_count > 0)
1376     {
1377       ds_clear (result);
1378       ds_put_byte (result, max);
1379     }
1380   else
1381     ds_assign_cstr (result, def);
1382 }
1383
1384 /* Revises the contents of the fields tree view based on the
1385    currently chosen set of separators. */
1386 static void
1387 revise_fields_preview (struct import_assistant *ia)
1388 {
1389   GtkWidget *w;
1390
1391   push_watch_cursor (ia);
1392
1393   w = GTK_WIDGET (ia->separators.fields_tree_view);
1394   gtk_widget_destroy (w);
1395   get_separators (ia);
1396   split_fields (ia);
1397   choose_column_names (ia);
1398   ia->separators.fields_tree_view = create_data_tree_view (
1399     true,
1400     GTK_CONTAINER (get_widget_assert (ia->asst.builder, "fields-scroller")),
1401     ia);
1402
1403   pop_watch_cursor (ia);
1404 }
1405
1406 /* Sets the widgets to match IA's separators substructure. */
1407 static void
1408 set_separators (struct import_assistant *ia)
1409 {
1410   struct separators_page *s = &ia->separators;
1411   unsigned int seps;
1412   struct string custom;
1413   bool any_custom;
1414   bool any_quotes;
1415   size_t i;
1416
1417   ds_init_empty (&custom);
1418   seps = 0;
1419   for (i = 0; i < ds_length (&s->separators); i++)
1420     {
1421       unsigned char c = ds_at (&s->separators, i);
1422       int j;
1423
1424       for (j = 0; j < SEPARATOR_CNT; j++)
1425         {
1426           const struct separator *s = &separators[j];
1427           if (s->c == c)
1428             {
1429               seps += 1u << j;
1430               goto next;
1431             }
1432         }
1433
1434       ds_put_byte (&custom, c);
1435     next:;
1436     }
1437
1438   for (i = 0; i < SEPARATOR_CNT; i++)
1439     {
1440       const struct separator *s = &separators[i];
1441       GtkWidget *button = get_widget_assert (ia->asst.builder, s->name);
1442       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
1443                                     (seps & (1u << i)) != 0);
1444     }
1445   any_custom = !ds_is_empty (&custom);
1446   gtk_entry_set_text (GTK_ENTRY (s->custom_entry), ds_cstr (&custom));
1447   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->custom_cb),
1448                                 any_custom);
1449   gtk_widget_set_sensitive (s->custom_entry, any_custom);
1450   ds_destroy (&custom);
1451
1452   any_quotes = !ds_is_empty (&s->quotes);
1453
1454   gtk_entry_set_text (s->quote_entry,
1455                       any_quotes ? ds_cstr (&s->quotes) : "\"");
1456   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->quote_cb),
1457                                 any_quotes);
1458   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->escape_cb),
1459                                 s->escape);
1460   gtk_widget_set_sensitive (s->quote_combo, any_quotes);
1461   gtk_widget_set_sensitive (s->escape_cb, any_quotes);
1462 }
1463
1464 /* Sets IA's separators substructure to match the widgets. */
1465 static void
1466 get_separators (struct import_assistant *ia)
1467 {
1468   struct separators_page *s = &ia->separators;
1469   int i;
1470
1471   ds_clear (&s->separators);
1472   for (i = 0; i < SEPARATOR_CNT; i++)
1473     {
1474       const struct separator *sep = &separators[i];
1475       GtkWidget *button = get_widget_assert (ia->asst.builder, sep->name);
1476       if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
1477         ds_put_byte (&s->separators, sep->c);
1478     }
1479
1480   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->custom_cb)))
1481     ds_put_cstr (&s->separators,
1482                  gtk_entry_get_text (GTK_ENTRY (s->custom_entry)));
1483
1484   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->quote_cb)))
1485     {
1486       gchar *text = gtk_combo_box_get_active_text (
1487                       GTK_COMBO_BOX (s->quote_combo));
1488       ds_assign_cstr (&s->quotes, text);
1489       g_free (text);
1490     }
1491   else
1492     ds_clear (&s->quotes);
1493   s->escape = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->escape_cb));
1494 }
1495
1496 /* Called when the user changes the entry field for custom
1497    separators. */
1498 static void
1499 on_separators_custom_entry_notify (GObject *gobject UNUSED,
1500                                    GParamSpec *arg1 UNUSED,
1501                                    struct import_assistant *ia)
1502 {
1503   revise_fields_preview (ia);
1504 }
1505
1506 /* Called when the user toggles the checkbox that enables custom
1507    separators. */
1508 static void
1509 on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
1510                                 struct import_assistant *ia)
1511 {
1512   bool is_active = gtk_toggle_button_get_active (custom_cb);
1513   gtk_widget_set_sensitive (ia->separators.custom_entry, is_active);
1514   revise_fields_preview (ia);
1515 }
1516
1517 /* Called when the user changes the selection in the combo box
1518    that selects a quote character. */
1519 static void
1520 on_quote_combo_change (GtkComboBox *combo, struct import_assistant *ia)
1521 {
1522   revise_fields_preview (ia);
1523 }
1524
1525 /* Called when the user toggles the checkbox that enables
1526    quoting. */
1527 static void
1528 on_quote_cb_toggle (GtkToggleButton *quote_cb, struct import_assistant *ia)
1529 {
1530   bool is_active = gtk_toggle_button_get_active (quote_cb);
1531   gtk_widget_set_sensitive (ia->separators.quote_combo, is_active);
1532   gtk_widget_set_sensitive (ia->separators.escape_cb, is_active);
1533   revise_fields_preview (ia);
1534 }
1535
1536 /* Called when the user toggles one of the separators
1537    checkboxes. */
1538 static void
1539 on_separator_toggle (GtkToggleButton *toggle UNUSED,
1540                      struct import_assistant *ia)
1541 {
1542   revise_fields_preview (ia);
1543 }
1544
1545 /* Called to render one of the cells in the fields preview tree
1546    view. */
1547 static void
1548 render_input_cell (PsppSheetViewColumn *tree_column, GtkCellRenderer *cell,
1549                    GtkTreeModel *model, GtkTreeIter *iter,
1550                    gpointer ia_)
1551 {
1552   struct import_assistant *ia = ia_;
1553   struct substring field;
1554   size_t row;
1555   gint column;
1556
1557   column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1558                                                "column-number"));
1559   row = empty_list_store_iter_to_row (iter) + ia->first_line.skip_lines;
1560   field = ia->separators.columns[column].contents[row];
1561   if (field.string != NULL)
1562     {
1563       GValue text = {0, };
1564       g_value_init (&text, G_TYPE_STRING);
1565       g_value_take_string (&text, ss_xstrdup (field));
1566       g_object_set_property (G_OBJECT (cell), "text", &text);
1567       g_value_unset (&text);
1568       g_object_set (cell, "background-set", FALSE, (void *) NULL);
1569     }
1570   else
1571     g_object_set (cell,
1572                   "text", "",
1573                   "background", "red",
1574                   "background-set", TRUE,
1575                   (void *) NULL);
1576 }
1577
1578 /* Called to render a tooltip on one of the cells in the fields
1579    preview tree view. */
1580 static gboolean
1581 on_query_input_tooltip (GtkWidget *widget, gint wx, gint wy,
1582                         gboolean keyboard_mode UNUSED,
1583                         GtkTooltip *tooltip, struct import_assistant *ia)
1584 {
1585   size_t row, column;
1586
1587   if (!get_tooltip_location (widget, wx, wy, ia, &row, &column))
1588     return FALSE;
1589
1590   if (ia->separators.columns[column].contents[row].string != NULL)
1591     return FALSE;
1592
1593   gtk_tooltip_set_text (tooltip,
1594                         _("This input line has too few separators "
1595                           "to fill in this field."));
1596   return TRUE;
1597 }
1598 \f
1599 /* The "formats" page of the assistant. */
1600
1601 static void on_variable_change (PsppireDict *dict, int idx,
1602                                 struct import_assistant *);
1603 static void clear_modified_vars (struct import_assistant *);
1604
1605 /* Initializes IA's formats substructure. */
1606 static void
1607 init_formats_page (struct import_assistant *ia)
1608 {
1609   GtkBuilder *builder = ia->asst.builder;
1610   struct formats_page *p = &ia->formats;
1611
1612   p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Formats"),
1613                                    GTK_ASSISTANT_PAGE_CONFIRM);
1614   p->data_tree_view = PSPP_SHEET_VIEW (get_widget_assert (builder, "data"));
1615   p->modified_vars = NULL;
1616   p->modified_var_cnt = 0;
1617   p->dict = NULL;
1618 }
1619
1620 /* Frees IA's formats substructure. */
1621 static void
1622 destroy_formats_page (struct import_assistant *ia)
1623 {
1624   struct formats_page *p = &ia->formats;
1625
1626   if (p->psppire_dict != NULL)
1627     {
1628       dict_destroy (p->psppire_dict->dict);
1629       g_object_unref (p->psppire_dict);
1630     }
1631   clear_modified_vars (ia);
1632 }
1633
1634 /* Called just before the formats page of the assistant is
1635    displayed. */
1636 static void
1637 prepare_formats_page (struct import_assistant *ia)
1638 {
1639   struct dictionary *dict;
1640   PsppireDict *psppire_dict;
1641   GtkBin *vars_scroller;
1642   GtkWidget *old_var_sheet;
1643   PsppireVarSheet *var_sheet;
1644   struct separators_page *s = &ia->separators;
1645   struct formats_page *p = &ia->formats;
1646   struct fmt_guesser *fg;
1647   unsigned long int number = 0;
1648   size_t column_idx;
1649
1650   push_watch_cursor (ia);
1651
1652   dict = dict_create (get_default_encoding ());
1653   fg = fmt_guesser_create ();
1654   for (column_idx = 0; column_idx < s->column_cnt; column_idx++)
1655     {
1656       struct variable *modified_var;
1657
1658       modified_var = (column_idx < p->modified_var_cnt
1659                       ? p->modified_vars[column_idx] : NULL);
1660       if (modified_var == NULL)
1661         {
1662           struct column *column = &s->columns[column_idx];
1663           struct variable *var;
1664           struct fmt_spec format;
1665           char *name;
1666           size_t row;
1667
1668           /* Choose variable name. */
1669           name = dict_make_unique_var_name (dict, column->name, &number);
1670
1671           /* Choose variable format. */
1672           fmt_guesser_clear (fg);
1673           for (row = ia->first_line.skip_lines; row < ia->file.line_cnt; row++)
1674             fmt_guesser_add (fg, column->contents[row]);
1675           fmt_guesser_guess (fg, &format);
1676           fmt_fix_input (&format);
1677
1678           /* Create variable. */
1679           var = dict_create_var_assert (dict, name, fmt_var_width (&format));
1680           var_set_both_formats (var, &format);
1681
1682           free (name);
1683         }
1684       else
1685         {
1686           char *name;
1687
1688           name = dict_make_unique_var_name (dict, var_get_name (modified_var),
1689                                             &number);
1690           dict_clone_var_as_assert (dict, modified_var, name);
1691           free (name);
1692         }
1693     }
1694   fmt_guesser_destroy (fg);
1695
1696   psppire_dict = psppire_dict_new_from_dict (dict);
1697   g_signal_connect (psppire_dict, "variable_changed",
1698                     G_CALLBACK (on_variable_change), ia);
1699   ia->formats.dict = dict;
1700   ia->formats.psppire_dict = psppire_dict;
1701
1702   /* XXX: PsppireVarStore doesn't hold a reference to
1703      psppire_dict for now, but it should.  After it does, we
1704      should g_object_ref the psppire_dict here, since we also
1705      hold a reference via ia->formats.dict. */
1706   var_sheet = PSPPIRE_VAR_SHEET (psppire_var_sheet_new ());
1707   g_object_set (var_sheet,
1708                 "dictionary", psppire_dict,
1709                 "may-create-vars", FALSE,
1710                 "may-delete-vars", FALSE,
1711                 "format-use", FMT_FOR_INPUT,
1712                 "enable-grid-lines", PSPP_SHEET_VIEW_GRID_LINES_BOTH,
1713                 (void *) NULL);
1714
1715   vars_scroller = GTK_BIN (get_widget_assert (ia->asst.builder, "vars-scroller"));
1716   old_var_sheet = gtk_bin_get_child (vars_scroller);
1717   if (old_var_sheet != NULL)
1718     gtk_widget_destroy (old_var_sheet);
1719   gtk_container_add (GTK_CONTAINER (vars_scroller), GTK_WIDGET (var_sheet));
1720   gtk_widget_show (GTK_WIDGET (var_sheet));
1721
1722   gtk_widget_destroy (GTK_WIDGET (ia->formats.data_tree_view));
1723   ia->formats.data_tree_view = create_data_tree_view (
1724     false,
1725     GTK_CONTAINER (get_widget_assert (ia->asst.builder, "data-scroller")),
1726     ia);
1727
1728   pop_watch_cursor (ia);
1729 }
1730
1731 /* Clears the set of user-modified variables from IA's formats
1732    substructure.  This discards user modifications to variable
1733    formats, thereby causing formats to revert to their
1734    defaults.  */
1735 static void
1736 clear_modified_vars (struct import_assistant *ia)
1737 {
1738   struct formats_page *p = &ia->formats;
1739   size_t i;
1740
1741   for (i = 0; i < p->modified_var_cnt; i++)
1742     var_destroy (p->modified_vars[i]);
1743   free (p->modified_vars);
1744   p->modified_vars = NULL;
1745   p->modified_var_cnt = 0;
1746 }
1747
1748 /* Resets the formats page to its defaults, discarding user
1749    modifications. */
1750 static void
1751 reset_formats_page (struct import_assistant *ia)
1752 {
1753   clear_modified_vars (ia);
1754   prepare_formats_page (ia);
1755 }
1756
1757 /* Called when the user changes one of the variables in the
1758    dictionary. */
1759 static void
1760 on_variable_change (PsppireDict *dict, int dict_idx,
1761                     struct import_assistant *ia)
1762 {
1763   struct formats_page *p = &ia->formats;
1764   PsppSheetView *tv = ia->formats.data_tree_view;
1765   gint column_idx = dict_idx + 1;
1766
1767   push_watch_cursor (ia);
1768
1769   /* Remove previous column and replace with new column. */
1770   pspp_sheet_view_remove_column (tv, pspp_sheet_view_get_column (tv, column_idx));
1771   pspp_sheet_view_insert_column (tv, make_data_column (ia, tv, false, dict_idx),
1772                                column_idx);
1773
1774   /* Save a copy of the modified variable in modified_vars, so
1775      that its attributes will be preserved if we back up to the
1776      previous page with the Prev button and then come back
1777      here. */
1778   if (dict_idx >= p->modified_var_cnt)
1779     {
1780       size_t i;
1781       p->modified_vars = xnrealloc (p->modified_vars, dict_idx + 1,
1782                                     sizeof *p->modified_vars);
1783       for (i = 0; i <= dict_idx; i++)
1784         p->modified_vars[i] = NULL;
1785       p->modified_var_cnt = dict_idx + 1;
1786     }
1787   if (p->modified_vars[dict_idx])
1788     var_destroy (p->modified_vars[dict_idx]);
1789   p->modified_vars[dict_idx]
1790     = var_clone (psppire_dict_get_variable (dict, dict_idx));
1791
1792   pop_watch_cursor (ia);
1793 }
1794
1795 /* Parses the contents of the field at (ROW,COLUMN) according to
1796    its variable format.  If OUTPUTP is non-null, then *OUTPUTP
1797    receives the formatted output for that field (which must be
1798    freed with free).  If TOOLTIPP is non-null, then *TOOLTIPP
1799    receives a message suitable for use in a tooltip, if one is
1800    needed, or a null pointer otherwise.  Returns true if a
1801    tooltip message is needed, otherwise false. */
1802 static bool
1803 parse_field (struct import_assistant *ia,
1804              size_t row, size_t column,
1805              char **outputp, char **tooltipp)
1806 {
1807   struct substring field;
1808   union value val;
1809   struct variable *var;
1810   const struct fmt_spec *in;
1811   struct fmt_spec out;
1812   char *tooltip;
1813   bool ok;
1814
1815   field = ia->separators.columns[column].contents[row];
1816   var = dict_get_var (ia->formats.dict, column);
1817   value_init (&val, var_get_width (var));
1818   in = var_get_print_format (var);
1819   out = fmt_for_output_from_input (in);
1820   tooltip = NULL;
1821   if (field.string != NULL)
1822     {
1823       char *error;
1824
1825       error = data_in (field, "UTF-8", in->type, &val, var_get_width (var),
1826                        dict_get_encoding (ia->formats.dict));
1827       if (error != NULL)
1828         {
1829           tooltip = xasprintf (_("Cannot parse field content `%.*s' as "
1830                                  "format %s: %s"),
1831                                (int) field.length, field.string,
1832                                fmt_name (in->type), error);
1833           free (error);
1834         }
1835     }
1836   else
1837     {
1838       tooltip = xstrdup (_("This input line has too few separators "
1839                            "to fill in this field."));
1840       value_set_missing (&val, var_get_width (var));
1841     }
1842   if (outputp != NULL)
1843     {
1844       *outputp = data_out (&val, dict_get_encoding (ia->formats.dict),  &out);
1845     }
1846   value_destroy (&val, var_get_width (var));
1847
1848   ok = tooltip == NULL;
1849   if (tooltipp != NULL)
1850     *tooltipp = tooltip;
1851   else
1852     free (tooltip);
1853   return ok;
1854 }
1855
1856 /* Called to render one of the cells in the data preview tree
1857    view. */
1858 static void
1859 render_output_cell (PsppSheetViewColumn *tree_column,
1860                     GtkCellRenderer *cell,
1861                     GtkTreeModel *model,
1862                     GtkTreeIter *iter,
1863                     gpointer ia_)
1864 {
1865   struct import_assistant *ia = ia_;
1866   char *output;
1867   GValue gvalue = { 0, };
1868   bool ok;
1869
1870   ok = parse_field (ia,
1871                     (empty_list_store_iter_to_row (iter)
1872                      + ia->first_line.skip_lines),
1873                     GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1874                                                         "column-number")),
1875                     &output, NULL);
1876
1877   g_value_init (&gvalue, G_TYPE_STRING);
1878   g_value_take_string (&gvalue, output);
1879   g_object_set_property (G_OBJECT (cell), "text", &gvalue);
1880   g_value_unset (&gvalue);
1881
1882   if (ok)
1883     g_object_set (cell, "background-set", FALSE, (void *) NULL);
1884   else
1885     g_object_set (cell,
1886                   "background", "red",
1887                   "background-set", TRUE,
1888                   (void *) NULL);
1889 }
1890
1891 /* Called to render a tooltip for one of the cells in the data
1892    preview tree view. */
1893 static gboolean
1894 on_query_output_tooltip (GtkWidget *widget, gint wx, gint wy,
1895                         gboolean keyboard_mode UNUSED,
1896                         GtkTooltip *tooltip, struct import_assistant *ia)
1897 {
1898   size_t row, column;
1899   char *text;
1900
1901   if (!get_tooltip_location (widget, wx, wy, ia, &row, &column))
1902     return FALSE;
1903
1904   if (parse_field (ia, row, column, NULL, &text))
1905     return FALSE;
1906
1907   gtk_tooltip_set_text (tooltip, text);
1908   free (text);
1909   return TRUE;
1910 }
1911 \f
1912 /* Utility functions used by multiple pages of the assistant. */
1913
1914 static gboolean
1915 get_tooltip_location (GtkWidget *widget, gint wx, gint wy,
1916                       const struct import_assistant *ia,
1917                       size_t *row, size_t *column)
1918 {
1919   PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1920   gint bx, by;
1921   GtkTreePath *path;
1922   GtkTreeIter iter;
1923   PsppSheetViewColumn *tree_column;
1924   GtkTreeModel *tree_model;
1925   bool ok;
1926
1927   /* Check that WIDGET is really visible on the screen before we
1928      do anything else.  This is a bug fix for a sticky situation:
1929      when text_data_import_assistant() returns, it frees the data
1930      necessary to compose the tool tip message, but there may be
1931      a tool tip under preparation at that point (even if there is
1932      no visible tool tip) that will call back into us a little
1933      bit later.  Perhaps the correct solution to this problem is
1934      to make the data related to the tool tips part of a GObject
1935      that only gets destroyed when all references are released,
1936      but this solution appears to be effective too. */
1937   if (!gtk_widget_get_mapped (widget))
1938     return FALSE;
1939
1940   pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view,
1941                                                      wx, wy, &bx, &by);
1942   if (!pspp_sheet_view_get_path_at_pos (tree_view, bx, by,
1943                                       &path, &tree_column, NULL, NULL))
1944     return FALSE;
1945
1946   *column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1947                                                 "column-number"));
1948
1949   tree_model = pspp_sheet_view_get_model (tree_view);
1950   ok = gtk_tree_model_get_iter (tree_model, &iter, path);
1951   gtk_tree_path_free (path);
1952   if (!ok)
1953     return FALSE;
1954
1955   *row = empty_list_store_iter_to_row (&iter) + ia->first_line.skip_lines;
1956   return TRUE;
1957 }
1958
1959 static void
1960 make_tree_view (const struct import_assistant *ia,
1961                 size_t first_line,
1962                 PsppSheetView **tree_view)
1963 {
1964   GtkTreeModel *model;
1965
1966   *tree_view = PSPP_SHEET_VIEW (pspp_sheet_view_new ());
1967   pspp_sheet_view_set_grid_lines (*tree_view, PSPP_SHEET_VIEW_GRID_LINES_BOTH);
1968   model = GTK_TREE_MODEL (psppire_empty_list_store_new (
1969                             ia->file.line_cnt - first_line));
1970   g_object_set_data (G_OBJECT (model), "lines", ia->file.lines + first_line);
1971   g_object_set_data (G_OBJECT (model), "first-line",
1972                      GINT_TO_POINTER (first_line));
1973   pspp_sheet_view_set_model (*tree_view, model);
1974   g_object_unref (model);
1975
1976   add_line_number_column (ia, *tree_view);
1977 }
1978
1979 static void
1980 render_line_number (PsppSheetViewColumn *tree_column,
1981                     GtkCellRenderer *cell,
1982                     GtkTreeModel *tree_model,
1983                     GtkTreeIter *iter,
1984                     gpointer data)
1985 {
1986   gint row = empty_list_store_iter_to_row (iter);
1987   char s[INT_BUFSIZE_BOUND (int)];
1988   int first_line;
1989
1990   first_line = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_model),
1991                                                    "first-line"));
1992   sprintf (s, "%d", first_line + row);
1993   g_object_set (cell, "text", s, NULL);
1994 }
1995
1996 static void
1997 add_line_number_column (const struct import_assistant *ia,
1998                         PsppSheetView *treeview)
1999 {
2000   PsppSheetViewColumn *column;
2001
2002   column = pspp_sheet_view_column_new_with_attributes (
2003     _("Line"), ia->asst.prop_renderer, (void *) NULL);
2004   pspp_sheet_view_column_set_fixed_width (
2005     column, get_monospace_width (treeview, ia->asst.prop_renderer, 5));
2006   pspp_sheet_view_column_set_resizable (column, TRUE);
2007   pspp_sheet_view_column_set_cell_data_func (column, ia->asst.prop_renderer,
2008                                              render_line_number, NULL, NULL);
2009   pspp_sheet_view_append_column (treeview, column);
2010 }
2011
2012 static gint
2013 get_monospace_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
2014                      size_t char_cnt)
2015 {
2016   struct string s;
2017   gint width;
2018
2019   ds_init_empty (&s);
2020   ds_put_byte_multiple (&s, '0', char_cnt);
2021   ds_put_byte (&s, ' ');
2022   width = get_string_width (treeview, renderer, ds_cstr (&s));
2023   ds_destroy (&s);
2024
2025   return width;
2026 }
2027
2028 static gint
2029 get_string_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
2030                   const char *string)
2031 {
2032   gint width;
2033   g_object_set (G_OBJECT (renderer), "text", string, (void *) NULL);
2034   gtk_cell_renderer_get_size (renderer, GTK_WIDGET (treeview),
2035                               NULL, NULL, NULL, &width, NULL);
2036   return width;
2037 }
2038
2039 static PsppSheetViewColumn *
2040 make_data_column (struct import_assistant *ia, PsppSheetView *tree_view,
2041                   bool input, gint dict_idx)
2042 {
2043   struct variable *var = NULL;
2044   struct column *column = NULL;
2045   size_t char_cnt;
2046   gint content_width, header_width;
2047   PsppSheetViewColumn *tree_column;
2048   char *name;
2049
2050   if (input)
2051     column = &ia->separators.columns[dict_idx];
2052   else
2053     var = dict_get_var (ia->formats.dict, dict_idx);
2054
2055   name = escape_underscores (input ? column->name : var_get_name (var));
2056   char_cnt = input ? column->width : var_get_print_format (var)->w;
2057   content_width = get_monospace_width (tree_view, ia->asst.fixed_renderer,
2058                                        char_cnt);
2059   header_width = get_string_width (tree_view, ia->asst.prop_renderer,
2060                                    name);
2061
2062   tree_column = pspp_sheet_view_column_new ();
2063   g_object_set_data (G_OBJECT (tree_column), "column-number",
2064                      GINT_TO_POINTER (dict_idx));
2065   pspp_sheet_view_column_set_title (tree_column, name);
2066   pspp_sheet_view_column_pack_start (tree_column, ia->asst.fixed_renderer,
2067                                    FALSE);
2068   pspp_sheet_view_column_set_cell_data_func (
2069     tree_column, ia->asst.fixed_renderer,
2070     input ? render_input_cell : render_output_cell, ia, NULL);
2071   pspp_sheet_view_column_set_fixed_width (tree_column, MAX (content_width,
2072                                                           header_width));
2073   pspp_sheet_view_column_set_resizable (tree_column, TRUE);
2074
2075   free (name);
2076
2077   return tree_column;
2078 }
2079
2080 static PsppSheetView *
2081 create_data_tree_view (bool input, GtkContainer *parent,
2082                        struct import_assistant *ia)
2083 {
2084   PsppSheetView *tree_view;
2085   gint i;
2086
2087   make_tree_view (ia, ia->first_line.skip_lines, &tree_view);
2088   pspp_sheet_selection_set_mode (pspp_sheet_view_get_selection (tree_view),
2089                                PSPP_SHEET_SELECTION_NONE);
2090
2091   for (i = 0; i < ia->separators.column_cnt; i++)
2092     pspp_sheet_view_append_column (tree_view,
2093                                  make_data_column (ia, tree_view, input, i));
2094
2095   g_object_set (G_OBJECT (tree_view), "has-tooltip", TRUE, (void *) NULL);
2096   g_signal_connect (tree_view, "query-tooltip",
2097                     G_CALLBACK (input ? on_query_input_tooltip
2098                                 : on_query_output_tooltip), ia);
2099
2100   gtk_container_add (parent, GTK_WIDGET (tree_view));
2101   gtk_widget_show (GTK_WIDGET (tree_view));
2102
2103   return tree_view;
2104 }
2105
2106 /* Increments the "watch cursor" level, setting the cursor for
2107    the assistant window to a watch face to indicate to the user
2108    that the ongoing operation may take some time. */
2109 static void
2110 push_watch_cursor (struct import_assistant *ia)
2111 {
2112   if (++ia->asst.watch_cursor == 1)
2113     {
2114       GtkWidget *widget = GTK_WIDGET (ia->asst.assistant);
2115       GdkDisplay *display = gtk_widget_get_display (widget);
2116       GdkCursor *cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
2117       gdk_window_set_cursor (widget->window, cursor);
2118       gdk_cursor_unref (cursor);
2119       gdk_display_flush (display);
2120     }
2121 }
2122
2123 /* Decrements the "watch cursor" level.  If the level reaches
2124    zero, the cursor is reset to its default shape. */
2125 static void
2126 pop_watch_cursor (struct import_assistant *ia)
2127 {
2128   if (--ia->asst.watch_cursor == 0)
2129     {
2130       GtkWidget *widget = GTK_WIDGET (ia->asst.assistant);
2131       gdk_window_set_cursor (widget->window, NULL);
2132     }
2133 }