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