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