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