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