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