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