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