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