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