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