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