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