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