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