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