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