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