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