Merge 'master' into 'psppsheet'.
[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/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/line-reader.h"
37 #include "libpspp/message.h"
38 #include "ui/gui/builder-wrapper.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/pspp-sheet-selection.h"
44 #include "ui/gui/pspp-sheet-view.h"
45 #include "ui/gui/psppire-data-window.h"
46 #include "ui/gui/psppire-dialog.h"
47 #include "ui/gui/psppire-encoding-selector.h"
48 #include "ui/gui/psppire-empty-list-store.h"
49 #include "ui/gui/psppire-scanf.h"
50 #include "ui/gui/psppire-var-sheet.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     PsppSheetView *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     PsppSheetView *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     PsppSheetView *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                             PsppSheetView **tree_view);
209 static void add_line_number_column (const struct import_assistant *,
210                                     PsppSheetView *);
211 static gint get_monospace_width (PsppSheetView *, GtkCellRenderer *,
212                                  size_t char_cnt);
213 static gint get_string_width (PsppSheetView *, GtkCellRenderer *,
214                               const char *string);
215 static PsppSheetViewColumn *make_data_column (struct import_assistant *,
216                                             PsppSheetView *, bool input,
217                                             gint column_idx);
218 static PsppSheetView *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   struct string input;
438
439   file->file_name = choose_file (parent_window, &file->encoding);
440   if (file->file_name == NULL)
441     return false;
442
443   reader = line_reader_for_file (file->encoding, file->file_name, O_RDONLY);
444   if (reader == NULL)
445     {
446       msg (ME, _("Could not open `%s': %s"),
447            file->file_name, strerror (errno));
448       return false;
449     }
450
451   ds_init_empty (&input);
452   file->lines = xnmalloc (MAX_PREVIEW_LINES, sizeof *file->lines);
453   for (; file->line_cnt < MAX_PREVIEW_LINES; file->line_cnt++)
454     {
455       ds_clear (&input);
456       if (!line_reader_read (reader, &input, MAX_LINE_LEN + 1)
457           || ds_length (&input) > 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           ds_destroy (&input);
472           return false;
473         }
474
475       ds_init_cstr (&file->lines[file->line_cnt],
476                     recode_string ("UTF-8", line_reader_get_encoding (reader),
477                                    ds_cstr (&input), ds_length (&input)));
478     }
479   ds_destroy (&input);
480
481   if (file->line_cnt == 0)
482     {
483       msg (ME, _("`%s' is empty."), file->file_name);
484       line_reader_close (reader);
485       destroy_file (ia);
486       return false;
487     }
488
489   /* Estimate the number of lines in the file. */
490   if (file->line_cnt < MAX_PREVIEW_LINES)
491     file->total_lines = file->line_cnt;
492   else
493     {
494       struct stat s;
495       off_t position = line_reader_tell (reader);
496       if (fstat (line_reader_fileno (reader), &s) == 0 && position > 0)
497         file->total_lines = (double) file->line_cnt / position * s.st_size;
498       else
499         file->total_lines = 0;
500     }
501
502   line_reader_close (reader);
503
504   return true;
505 }
506
507 /* Frees IA's file substructure. */
508 static void
509 destroy_file (struct import_assistant *ia)
510 {
511   struct file *f = &ia->file;
512   size_t i;
513
514   for (i = 0; i < f->line_cnt; i++)
515     ds_destroy (&f->lines[i]);
516   free (f->lines);
517   g_free (f->file_name);
518   g_free (f->encoding);
519 }
520
521 /* Obtains the file to read from the user.  If successful, returns the name of
522    the file and stores the user's chosen encoding for the file into *ENCODINGP.
523    The caller must free each of these strings with g_free().
524
525    On failure, stores a null pointer and stores NULL in *ENCODINGP.
526
527    PARENT_WINDOW must be the window to use as the file chooser window's
528    parent. */
529 static char *
530 choose_file (GtkWindow *parent_window, gchar **encodingp)
531 {
532   char *file_name;
533   GtkFileFilter *filter = NULL;
534
535   GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Import Delimited Text Data"),
536                                         parent_window,
537                                         GTK_FILE_CHOOSER_ACTION_OPEN,
538                                         GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
539                                         GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
540                                         NULL);
541
542   g_object_set (dialog, "local-only", FALSE, NULL);
543
544   filter = gtk_file_filter_new ();
545   gtk_file_filter_set_name (filter, _("Text files"));
546   gtk_file_filter_add_mime_type (filter, "text/*");
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, _("Text (*.txt) Files"));
551   gtk_file_filter_add_pattern (filter, "*.txt");
552   gtk_file_filter_add_pattern (filter, "*.TXT");
553   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
554
555   filter = gtk_file_filter_new ();
556   gtk_file_filter_set_name (filter, _("Plain Text (ASCII) Files"));
557   gtk_file_filter_add_mime_type (filter, "text/plain");
558   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
559
560   filter = gtk_file_filter_new ();
561   gtk_file_filter_set_name (filter, _("Comma Separated Value Files"));
562   gtk_file_filter_add_mime_type (filter, "text/csv");
563   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
564
565   /* I've never encountered one of these, but it's listed here:
566      http://www.iana.org/assignments/media-types/text/tab-separated-values  */
567   filter = gtk_file_filter_new ();
568   gtk_file_filter_set_name (filter, _("Tab Separated Value Files"));
569   gtk_file_filter_add_mime_type (filter, "text/tab-separated-values");
570   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
571
572   gtk_file_chooser_set_extra_widget (
573     GTK_FILE_CHOOSER (dialog), psppire_encoding_selector_new ("Auto", true));
574
575   filter = gtk_file_filter_new ();
576   gtk_file_filter_set_name (filter, _("All Files"));
577   gtk_file_filter_add_pattern (filter, "*");
578   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
579
580   switch (gtk_dialog_run (GTK_DIALOG (dialog)))
581     {
582     case GTK_RESPONSE_ACCEPT:
583       file_name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
584       *encodingp = psppire_encoding_selector_get_encoding (
585         gtk_file_chooser_get_extra_widget (GTK_FILE_CHOOSER (dialog)));
586       break;
587     default:
588       file_name = NULL;
589       *encodingp = NULL;
590       break;
591     }
592   gtk_widget_destroy (dialog);
593
594   return file_name;
595 }
596 \f
597 /* Assistant. */
598
599 static void close_assistant (struct import_assistant *, int response);
600 static void on_prepare (GtkAssistant *assistant, GtkWidget *page,
601                         struct import_assistant *);
602 static void on_cancel (GtkAssistant *assistant, struct import_assistant *);
603 static void on_close (GtkAssistant *assistant, struct import_assistant *);
604 static void on_paste (GtkButton *button, struct import_assistant *);
605 static void on_reset (GtkButton *button, struct import_assistant *);
606 static void close_assistant (struct import_assistant *, int response);
607
608 /* Initializes IA's asst substructure.  PARENT_WINDOW must be the
609    window to use as the assistant window's parent.  */
610 static void
611 init_assistant (struct import_assistant *ia, GtkWindow *parent_window)
612 {
613   struct assistant *a = &ia->asst;
614
615   a->builder = builder_new ("text-data-import.ui");
616   a->assistant = GTK_ASSISTANT (gtk_assistant_new ());
617   g_signal_connect (a->assistant, "prepare", G_CALLBACK (on_prepare), ia);
618   g_signal_connect (a->assistant, "cancel", G_CALLBACK (on_cancel), ia);
619   g_signal_connect (a->assistant, "close", G_CALLBACK (on_close), ia);
620   a->paste_button = gtk_button_new_from_stock (GTK_STOCK_PASTE);
621   gtk_assistant_add_action_widget (a->assistant, a->paste_button);
622   g_signal_connect (a->paste_button, "clicked", G_CALLBACK (on_paste), ia);
623   a->reset_button = gtk_button_new_from_stock ("pspp-stock-reset");
624   gtk_assistant_add_action_widget (a->assistant, a->reset_button);
625   g_signal_connect (a->reset_button, "clicked", G_CALLBACK (on_reset), ia);
626   gtk_window_set_title (GTK_WINDOW (a->assistant),
627                         _("Importing Delimited Text Data"));
628   gtk_window_set_transient_for (GTK_WINDOW (a->assistant), parent_window);
629   gtk_window_set_icon_name (GTK_WINDOW (a->assistant), "pspp");
630
631   a->prop_renderer = gtk_cell_renderer_text_new ();
632   g_object_ref_sink (a->prop_renderer);
633   a->fixed_renderer = gtk_cell_renderer_text_new ();
634   g_object_ref_sink (a->fixed_renderer);
635   g_object_set (G_OBJECT (a->fixed_renderer),
636                 "family", "Monospace",
637                 (void *) NULL);
638 }
639
640 /* Frees IA's asst substructure. */
641 static void
642 destroy_assistant (struct import_assistant *ia)
643 {
644   struct assistant *a = &ia->asst;
645
646   g_object_unref (a->prop_renderer);
647   g_object_unref (a->fixed_renderer);
648   g_object_unref (a->builder);
649 }
650
651 /* Appends a page of the given TYPE, with PAGE as its content, to
652    the GtkAssistant encapsulated by IA.  Returns the GtkWidget
653    that represents the page. */
654 static GtkWidget *
655 add_page_to_assistant (struct import_assistant *ia,
656                        GtkWidget *page, GtkAssistantPageType type)
657 {
658   const char *title;
659   char *title_copy;
660   GtkWidget *content;
661
662   title = gtk_window_get_title (GTK_WINDOW (page));
663   title_copy = xstrdup (title ? title : "");
664
665   content = gtk_bin_get_child (GTK_BIN (page));
666   assert (content);
667   g_object_ref (content);
668   gtk_container_remove (GTK_CONTAINER (page), content);
669
670   gtk_widget_destroy (page);
671
672   gtk_assistant_append_page (ia->asst.assistant, content);
673   gtk_assistant_set_page_type (ia->asst.assistant, content, type);
674   gtk_assistant_set_page_title (ia->asst.assistant, content, title_copy);
675   gtk_assistant_set_page_complete (ia->asst.assistant, content, true);
676
677   free (title_copy);
678
679   return content;
680 }
681
682 /* Called just before PAGE is displayed as the current page of
683    ASSISTANT, this updates IA content according to the new
684    page. */
685 static void
686 on_prepare (GtkAssistant *assistant, GtkWidget *page,
687             struct import_assistant *ia)
688 {
689
690   if (gtk_assistant_get_page_type (assistant, page)
691       == GTK_ASSISTANT_PAGE_CONFIRM)
692     gtk_widget_grab_focus (assistant->apply);
693   else
694     gtk_widget_grab_focus (assistant->forward);
695
696   if (page == ia->separators.page)
697     prepare_separators_page (ia);
698   else if (page == ia->formats.page)
699     prepare_formats_page (ia);
700
701   gtk_widget_show (ia->asst.reset_button);
702   if (page == ia->formats.page)
703     gtk_widget_show (ia->asst.paste_button);
704   else
705     gtk_widget_hide (ia->asst.paste_button);
706 }
707
708 /* Called when the Cancel button in the assistant is clicked. */
709 static void
710 on_cancel (GtkAssistant *assistant, struct import_assistant *ia)
711 {
712   close_assistant (ia, GTK_RESPONSE_CANCEL);
713 }
714
715 /* Called when the Apply button on the last page of the assistant
716    is clicked. */
717 static void
718 on_close (GtkAssistant *assistant, struct import_assistant *ia)
719 {
720   close_assistant (ia, GTK_RESPONSE_APPLY);
721 }
722
723 /* Called when the Paste button on the last page of the assistant
724    is clicked. */
725 static void
726 on_paste (GtkButton *button, struct import_assistant *ia)
727 {
728   close_assistant (ia, PSPPIRE_RESPONSE_PASTE);
729 }
730
731 /* Called when the Reset button is clicked. */
732 static void
733 on_reset (GtkButton *button, struct import_assistant *ia)
734 {
735   gint page_num = gtk_assistant_get_current_page (ia->asst.assistant);
736   GtkWidget *page = gtk_assistant_get_nth_page (ia->asst.assistant, page_num);
737
738   if (page == ia->intro.page)
739     reset_intro_page (ia);
740   else if (page == ia->first_line.page)
741     reset_first_line_page (ia);
742   else if (page == ia->separators.page)
743     reset_separators_page (ia);
744   else if (page == ia->formats.page)
745     reset_formats_page (ia);
746 }
747
748 /* Causes the assistant to close, returning RESPONSE for
749    interpretation by text_data_import_assistant. */
750 static void
751 close_assistant (struct import_assistant *ia, int response)
752 {
753   ia->asst.response = response;
754   g_main_loop_quit (ia->asst.main_loop);
755   gtk_widget_hide (GTK_WIDGET (ia->asst.assistant));
756 }
757 \f
758 /* The "intro" page of the assistant. */
759
760 static void on_intro_amount_changed (struct import_assistant *);
761
762 /* Initializes IA's intro substructure. */
763 static void
764 init_intro_page (struct import_assistant *ia)
765 {
766   GtkBuilder *builder = ia->asst.builder;
767   struct intro_page *p = &ia->intro;
768   struct string s;
769   GtkWidget *hbox_n_cases ;
770   GtkWidget *hbox_percent ;
771   GtkWidget *table ;
772
773
774   p->n_cases_spin = gtk_spin_button_new_with_range (0, INT_MAX, 100);
775
776   hbox_n_cases = psppire_scanf_new (_("Only the first %4d cases"), &p->n_cases_spin);
777
778   table  = get_widget_assert (builder, "button-table");
779
780   gtk_table_attach_defaults (GTK_TABLE (table), hbox_n_cases,
781                     1, 2,
782                     1, 2);
783
784   p->percent_spin = gtk_spin_button_new_with_range (0, 100, 10);
785
786   hbox_percent = psppire_scanf_new (_("Only the first %3d %% of file (approximately)"), &p->percent_spin);
787
788   gtk_table_attach_defaults (GTK_TABLE (table), hbox_percent,
789                              1, 2,
790                              2, 3);
791
792   p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Intro"),
793                                    GTK_ASSISTANT_PAGE_INTRO);
794
795   p->all_cases_button = get_widget_assert (builder, "import-all-cases");
796
797   p->n_cases_button = get_widget_assert (builder, "import-n-cases");
798
799   p->percent_button = get_widget_assert (builder, "import-percent");
800
801   g_signal_connect_swapped (p->all_cases_button, "toggled",
802                     G_CALLBACK (on_intro_amount_changed), ia);
803   g_signal_connect_swapped (p->n_cases_button, "toggled",
804                     G_CALLBACK (on_intro_amount_changed), ia);
805   g_signal_connect_swapped (p->percent_button, "toggled",
806                     G_CALLBACK (on_intro_amount_changed), ia);
807
808   on_intro_amount_changed (ia);
809
810   ds_init_empty (&s);
811   ds_put_cstr (&s, _("This assistant will guide you through the process of "
812                      "importing data into PSPP from a text file with one line "
813                      "per case,  in which fields are separated by tabs, "
814                      "commas, or other delimiters.\n\n"));
815   if (ia->file.total_is_exact)
816     ds_put_format (
817       &s, ngettext ("The selected file contains %zu line of text.  ",
818                     "The selected file contains %zu lines of text.  ",
819                     ia->file.line_cnt),
820       ia->file.line_cnt);
821   else if (ia->file.total_lines > 0)
822     {
823       ds_put_format (
824         &s, ngettext (
825           "The selected file contains approximately %lu line of text.  ",
826           "The selected file contains approximately %lu lines of text.  ",
827           ia->file.total_lines),
828         ia->file.total_lines);
829       ds_put_format (
830         &s, ngettext (
831           "Only the first %zu line of the file will be shown for "
832           "preview purposes in the following screens.  ",
833           "Only the first %zu lines of the file will be shown for "
834           "preview purposes in the following screens.  ",
835           ia->file.line_cnt),
836         ia->file.line_cnt);
837     }
838   ds_put_cstr (&s, _("You may choose below how much of the file should "
839                      "actually be imported."));
840   gtk_label_set_text (GTK_LABEL (get_widget_assert (builder, "intro-label")),
841                       ds_cstr (&s));
842   ds_destroy (&s);
843 }
844
845 /* Resets IA's intro page to its initial state. */
846 static void
847 reset_intro_page (struct import_assistant *ia)
848 {
849   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->intro.all_cases_button),
850                                 true);
851 }
852
853 /* Called when one of the radio buttons is clicked. */
854 static void
855 on_intro_amount_changed (struct import_assistant *ia)
856 {
857   struct intro_page *p = &ia->intro;
858
859   gtk_widget_set_sensitive (p->n_cases_spin,
860                             gtk_toggle_button_get_active (
861                               GTK_TOGGLE_BUTTON (p->n_cases_button)));
862
863   gtk_widget_set_sensitive (p->percent_spin,
864                             gtk_toggle_button_get_active (
865                               GTK_TOGGLE_BUTTON (p->percent_button)));
866 }
867 \f
868 /* The "first line" page of the assistant. */
869
870 static PsppSheetView *create_lines_tree_view (GtkContainer *parent_window,
871                                             struct import_assistant *);
872 static void on_first_line_change (PsppSheetSelection *,
873                                   struct import_assistant *);
874 static void on_variable_names_cb_toggle (GtkToggleButton *,
875                                          struct import_assistant *);
876 static void set_first_line (struct import_assistant *);
877 static void get_first_line (struct import_assistant *);
878
879 /* Initializes IA's first_line substructure. */
880 static void
881 init_first_line_page (struct import_assistant *ia)
882 {
883   struct first_line_page *p = &ia->first_line;
884   GtkBuilder *builder = ia->asst.builder;
885
886   p->page = add_page_to_assistant (ia, get_widget_assert (builder, "FirstLine"),
887                                    GTK_ASSISTANT_PAGE_CONTENT);
888   gtk_widget_destroy (get_widget_assert (builder, "first-line"));
889   p->tree_view = create_lines_tree_view (
890     GTK_CONTAINER (get_widget_assert (builder, "first-line-scroller")), ia);
891   p->variable_names_cb = get_widget_assert (builder, "variable-names");
892   pspp_sheet_selection_set_mode (
893     pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (p->tree_view)),
894     PSPP_SHEET_SELECTION_BROWSE);
895   pspp_sheet_view_set_rubber_banding (PSPP_SHEET_VIEW (p->tree_view), TRUE);
896   set_first_line (ia);
897   g_signal_connect (pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (p->tree_view)),
898                     "changed", G_CALLBACK (on_first_line_change), ia);
899   g_signal_connect (p->variable_names_cb, "toggled",
900                     G_CALLBACK (on_variable_names_cb_toggle), ia);
901 }
902
903 /* Resets the first_line page to its initial content. */
904 static void
905 reset_first_line_page (struct import_assistant *ia)
906 {
907   ia->first_line.skip_lines = 0;
908   ia->first_line.variable_names = false;
909   set_first_line (ia);
910 }
911
912 static void
913 render_line (PsppSheetViewColumn *tree_column,
914              GtkCellRenderer *cell,
915              GtkTreeModel *tree_model,
916              GtkTreeIter *iter,
917              gpointer data)
918 {
919   gint row = empty_list_store_iter_to_row (iter);
920   struct string *lines;
921
922   lines = g_object_get_data (G_OBJECT (tree_model), "lines");
923   g_return_if_fail (lines != NULL);
924
925   g_object_set (cell, "text", ds_cstr (&lines[row]), NULL);
926 }
927
928
929 /* Creates and returns a tree view that contains each of the
930    lines in IA's file as a row. */
931 static PsppSheetView *
932 create_lines_tree_view (GtkContainer *parent, struct import_assistant *ia)
933 {
934   PsppSheetView *tree_view;
935   PsppSheetViewColumn *column;
936   size_t max_line_length;
937   gint content_width, header_width;
938   size_t i;
939   const gchar *title = _("Text");
940
941   make_tree_view (ia, 0, &tree_view);
942
943   column = pspp_sheet_view_column_new_with_attributes (
944     title, ia->asst.fixed_renderer, (void *) NULL);
945   pspp_sheet_view_column_set_cell_data_func (column, ia->asst.fixed_renderer,
946                                            render_line, NULL, NULL);
947   pspp_sheet_view_column_set_resizable (column, TRUE);
948
949   max_line_length = 0;
950   for (i = 0; i < ia->file.line_cnt; i++)
951     {
952       size_t w = ds_length (&ia->file.lines[i]);
953       max_line_length = MAX (max_line_length, w);
954     }
955
956   content_width = get_monospace_width (tree_view, ia->asst.fixed_renderer,
957                                        max_line_length);
958   header_width = get_string_width (tree_view, ia->asst.prop_renderer, title);
959   pspp_sheet_view_column_set_fixed_width (column, MAX (content_width,
960                                                      header_width));
961   pspp_sheet_view_append_column (tree_view, column);
962
963   gtk_container_add (parent, GTK_WIDGET (tree_view));
964   gtk_widget_show (GTK_WIDGET (tree_view));
965
966   return tree_view;
967 }
968
969 /* Called when the line selected in the first_line tree view
970    changes. */
971 static void
972 on_first_line_change (PsppSheetSelection *selection UNUSED,
973                       struct import_assistant *ia)
974 {
975   get_first_line (ia);
976 }
977
978 /* Called when the checkbox that indicates whether variable
979    names are in the row above the first line is toggled. */
980 static void
981 on_variable_names_cb_toggle (GtkToggleButton *variable_names_cb UNUSED,
982                              struct import_assistant *ia)
983 {
984   get_first_line (ia);
985 }
986
987 /* Sets the widgets to match IA's first_line substructure. */
988 static void
989 set_first_line (struct import_assistant *ia)
990 {
991   GtkTreePath *path;
992
993   path = gtk_tree_path_new_from_indices (ia->first_line.skip_lines, -1);
994   pspp_sheet_view_set_cursor (PSPP_SHEET_VIEW (ia->first_line.tree_view),
995                             path, NULL, false);
996   gtk_tree_path_free (path);
997
998   gtk_toggle_button_set_active (
999     GTK_TOGGLE_BUTTON (ia->first_line.variable_names_cb),
1000     ia->first_line.variable_names);
1001   gtk_widget_set_sensitive (ia->first_line.variable_names_cb,
1002                             ia->first_line.skip_lines > 0);
1003 }
1004
1005 /* Sets IA's first_line substructure to match the widgets. */
1006 static void
1007 get_first_line (struct import_assistant *ia)
1008 {
1009   PsppSheetSelection *selection;
1010   GtkTreeIter iter;
1011   GtkTreeModel *model;
1012
1013   selection = pspp_sheet_view_get_selection (ia->first_line.tree_view);
1014   if (pspp_sheet_selection_get_selected (selection, &model, &iter))
1015     {
1016       GtkTreePath *path = gtk_tree_model_get_path (model, &iter);
1017       int row = gtk_tree_path_get_indices (path)[0];
1018       gtk_tree_path_free (path);
1019
1020       ia->first_line.skip_lines = row;
1021       ia->first_line.variable_names =
1022         (ia->first_line.skip_lines > 0
1023          && gtk_toggle_button_get_active (
1024            GTK_TOGGLE_BUTTON (ia->first_line.variable_names_cb)));
1025     }
1026   gtk_widget_set_sensitive (ia->first_line.variable_names_cb,
1027                             ia->first_line.skip_lines > 0);
1028 }
1029 \f
1030 /* The "separators" page of the assistant. */
1031
1032 static void revise_fields_preview (struct import_assistant *ia);
1033 static void choose_likely_separators (struct import_assistant *ia);
1034 static void find_commonest_chars (unsigned long int histogram[UCHAR_MAX + 1],
1035                                   const char *targets, const char *def,
1036                                   struct string *result);
1037 static void clear_fields (struct import_assistant *ia);
1038 static void revise_fields_preview (struct import_assistant *);
1039 static void set_separators (struct import_assistant *);
1040 static void get_separators (struct import_assistant *);
1041 static void on_separators_custom_entry_notify (GObject *UNUSED,
1042                                                GParamSpec *UNUSED,
1043                                                struct import_assistant *);
1044 static void on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
1045                                             struct import_assistant *);
1046 static void on_quote_combo_change (GtkComboBox *combo,
1047                                    struct import_assistant *);
1048 static void on_quote_cb_toggle (GtkToggleButton *quote_cb,
1049                                 struct import_assistant *);
1050 static void on_separator_toggle (GtkToggleButton *, struct import_assistant *);
1051 static void render_input_cell (PsppSheetViewColumn *tree_column,
1052                                GtkCellRenderer *cell,
1053                                GtkTreeModel *model, GtkTreeIter *iter,
1054                                gpointer ia);
1055 static gboolean on_query_input_tooltip (GtkWidget *widget, gint wx, gint wy,
1056                                         gboolean keyboard_mode UNUSED,
1057                                         GtkTooltip *tooltip,
1058                                         struct import_assistant *);
1059
1060 /* A common field separator and its identifying name. */
1061 struct separator
1062   {
1063     const char *name;           /* Name (for use with get_widget_assert). */
1064     int c;                      /* Separator character. */
1065   };
1066
1067 /* All the separators in the dialog box. */
1068 static const struct separator separators[] =
1069   {
1070     {"space", ' '},
1071     {"tab", '\t'},
1072     {"bang", '!'},
1073     {"colon", ':'},
1074     {"comma", ','},
1075     {"hyphen", '-'},
1076     {"pipe", '|'},
1077     {"semicolon", ';'},
1078     {"slash", '/'},
1079   };
1080 #define SEPARATOR_CNT (sizeof separators / sizeof *separators)
1081
1082 static void
1083 set_quote_list (GtkComboBoxEntry *cb)
1084 {
1085   GtkListStore *list =  gtk_list_store_new (1, G_TYPE_STRING);
1086   GtkTreeIter iter;
1087   gint i;
1088   const gchar *seperator[3] = {"'\"", "\'", "\""};
1089
1090   for (i = 0; i < 3; i++)
1091     {
1092       const gchar *s = seperator[i];
1093
1094       /* Add a new row to the model */
1095       gtk_list_store_append (list, &iter);
1096       gtk_list_store_set (list, &iter,
1097                           0, s,
1098                           -1);
1099
1100     }
1101
1102   gtk_combo_box_set_model (GTK_COMBO_BOX (cb), GTK_TREE_MODEL (list));
1103   g_object_unref (list);
1104
1105   gtk_combo_box_entry_set_text_column (cb, 0);
1106 }
1107
1108 /* Initializes IA's separators substructure. */
1109 static void
1110 init_separators_page (struct import_assistant *ia)
1111 {
1112   GtkBuilder *builder = ia->asst.builder;
1113   struct separators_page *p = &ia->separators;
1114   size_t i;
1115
1116   choose_likely_separators (ia);
1117
1118   p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Separators"),
1119                                    GTK_ASSISTANT_PAGE_CONTENT);
1120   p->custom_cb = get_widget_assert (builder, "custom-cb");
1121   p->custom_entry = get_widget_assert (builder, "custom-entry");
1122   p->quote_combo = get_widget_assert (builder, "quote-combo");
1123   p->quote_entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (p->quote_combo)));
1124   p->quote_cb = get_widget_assert (builder, "quote-cb");
1125   p->escape_cb = get_widget_assert (builder, "escape");
1126
1127   set_separators (ia);
1128   set_quote_list (GTK_COMBO_BOX_ENTRY (p->quote_combo));
1129   p->fields_tree_view = PSPP_SHEET_VIEW (get_widget_assert (builder, "fields"));
1130   g_signal_connect (p->quote_combo, "changed",
1131                     G_CALLBACK (on_quote_combo_change), ia);
1132   g_signal_connect (p->quote_cb, "toggled",
1133                     G_CALLBACK (on_quote_cb_toggle), ia);
1134   g_signal_connect (p->custom_entry, "notify::text",
1135                     G_CALLBACK (on_separators_custom_entry_notify), ia);
1136   g_signal_connect (p->custom_cb, "toggled",
1137                     G_CALLBACK (on_separators_custom_cb_toggle), ia);
1138   for (i = 0; i < SEPARATOR_CNT; i++)
1139     g_signal_connect (get_widget_assert (builder, separators[i].name),
1140                       "toggled", G_CALLBACK (on_separator_toggle), ia);
1141   g_signal_connect (p->escape_cb, "toggled",
1142                     G_CALLBACK (on_separator_toggle), ia);
1143 }
1144
1145 /* Frees IA's separators substructure. */
1146 static void
1147 destroy_separators_page (struct import_assistant *ia)
1148 {
1149   struct separators_page *s = &ia->separators;
1150
1151   ds_destroy (&s->separators);
1152   ds_destroy (&s->quotes);
1153   clear_fields (ia);
1154 }
1155
1156 /* Called just before the separators page becomes visible in the
1157    assistant. */
1158 static void
1159 prepare_separators_page (struct import_assistant *ia)
1160 {
1161   revise_fields_preview (ia);
1162 }
1163
1164 /* Called when the Reset button is clicked on the separators
1165    page, resets the separators to the defaults. */
1166 static void
1167 reset_separators_page (struct import_assistant *ia)
1168 {
1169   choose_likely_separators (ia);
1170   set_separators (ia);
1171 }
1172
1173 /* Frees and clears the column data in IA's separators
1174    substructure. */
1175 static void
1176 clear_fields (struct import_assistant *ia)
1177 {
1178   struct separators_page *s = &ia->separators;
1179
1180   if (s->column_cnt > 0)
1181     {
1182       struct column *col;
1183       size_t row;
1184
1185       for (row = 0; row < ia->file.line_cnt; row++)
1186         {
1187           const struct string *line = &ia->file.lines[row];
1188           const char *line_start = ds_data (line);
1189           const char *line_end = ds_end (line);
1190
1191           for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1192             {
1193               char *s = ss_data (col->contents[row]);
1194               if (!(s >= line_start && s <= line_end))
1195                 ss_dealloc (&col->contents[row]);
1196             }
1197         }
1198
1199       for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1200         {
1201           free (col->name);
1202           free (col->contents);
1203         }
1204
1205       free (s->columns);
1206       s->columns = NULL;
1207       s->column_cnt = 0;
1208     }
1209 }
1210
1211 /* Breaks the file data in IA into columns based on the
1212    separators set in IA's separators substructure. */
1213 static void
1214 split_fields (struct import_assistant *ia)
1215 {
1216   struct separators_page *s = &ia->separators;
1217   size_t columns_allocated;
1218   bool space_sep;
1219   size_t row;
1220
1221   clear_fields (ia);
1222
1223   /* Is space in the set of separators? */
1224   space_sep = ss_find_byte (ds_ss (&s->separators), ' ') != SIZE_MAX;
1225
1226   /* Split all the lines, not just those from
1227      ia->first_line.skip_lines on, so that we split the line that
1228      contains variables names if ia->first_line.variable_names is
1229      true. */
1230   columns_allocated = 0;
1231   for (row = 0; row < ia->file.line_cnt; row++)
1232     {
1233       struct string *line = &ia->file.lines[row];
1234       struct substring text = ds_ss (line);
1235       size_t column_idx;
1236
1237       for (column_idx = 0; ; column_idx++)
1238         {
1239           struct substring field;
1240           struct column *column;
1241
1242           if (space_sep)
1243             ss_ltrim (&text, ss_cstr (" "));
1244           if (ss_is_empty (text))
1245             {
1246               if (column_idx != 0)
1247                 break;
1248               field = text;
1249             }
1250           else if (!ds_is_empty (&s->quotes)
1251                    && ds_find_byte (&s->quotes, text.string[0]) != SIZE_MAX)
1252             {
1253               int quote = ss_get_byte (&text);
1254               if (!s->escape)
1255                 ss_get_until (&text, quote, &field);
1256               else
1257                 {
1258                   struct string s;
1259                   int c;
1260
1261                   ds_init_empty (&s);
1262                   while ((c = ss_get_byte (&text)) != EOF)
1263                     if (c != quote)
1264                       ds_put_byte (&s, c);
1265                     else if (ss_match_byte (&text, quote))
1266                       ds_put_byte (&s, quote);
1267                     else
1268                       break;
1269                   field = ds_ss (&s);
1270                 }
1271             }
1272           else
1273             ss_get_bytes (&text, ss_cspan (text, ds_ss (&s->separators)),
1274                           &field);
1275
1276           if (column_idx >= s->column_cnt)
1277             {
1278               struct column *column;
1279
1280               if (s->column_cnt >= columns_allocated)
1281                 s->columns = x2nrealloc (s->columns, &columns_allocated,
1282                                          sizeof *s->columns);
1283               column = &s->columns[s->column_cnt++];
1284               column->name = NULL;
1285               column->width = 0;
1286               column->contents = xcalloc (ia->file.line_cnt,
1287                                           sizeof *column->contents);
1288             }
1289           column = &s->columns[column_idx];
1290           column->contents[row] = field;
1291           if (ss_length (field) > column->width)
1292             column->width = ss_length (field);
1293
1294           if (space_sep)
1295             ss_ltrim (&text, ss_cstr (" "));
1296           if (ss_is_empty (text))
1297             break;
1298           if (ss_find_byte (ds_ss (&s->separators), ss_first (text))
1299               != SIZE_MAX)
1300             ss_advance (&text, 1);
1301         }
1302     }
1303 }
1304
1305 /* Chooses a name for each column on the separators page */
1306 static void
1307 choose_column_names (struct import_assistant *ia)
1308 {
1309   const struct first_line_page *f = &ia->first_line;
1310   struct separators_page *s = &ia->separators;
1311   struct dictionary *dict;
1312   unsigned long int generated_name_count = 0;
1313   struct column *col;
1314   size_t name_row;
1315
1316   dict = dict_create (get_default_encoding ());
1317   name_row = f->variable_names && f->skip_lines ? f->skip_lines : 0;
1318   for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1319     {
1320       char *hint, *name;
1321
1322       hint = name_row ? ss_xstrdup (col->contents[name_row - 1]) : NULL;
1323       name = dict_make_unique_var_name (dict, hint, &generated_name_count);
1324       free (hint);
1325
1326       col->name = name;
1327       dict_create_var_assert (dict, name, 0);
1328     }
1329   dict_destroy (dict);
1330 }
1331
1332 /* Picks the most likely separator and quote characters based on
1333    IA's file data. */
1334 static void
1335 choose_likely_separators (struct import_assistant *ia)
1336 {
1337   unsigned long int histogram[UCHAR_MAX + 1] = { 0 };
1338   size_t row;
1339
1340   /* Construct a histogram of all the characters used in the
1341      file. */
1342   for (row = 0; row < ia->file.line_cnt; row++)
1343     {
1344       struct substring line = ds_ss (&ia->file.lines[row]);
1345       size_t length = ss_length (line);
1346       size_t i;
1347       for (i = 0; i < length; i++)
1348         histogram[(unsigned char) line.string[i]]++;
1349     }
1350
1351   find_commonest_chars (histogram, "\"'", "", &ia->separators.quotes);
1352   find_commonest_chars (histogram, ",;:/|!\t-", ",",
1353                         &ia->separators.separators);
1354   ia->separators.escape = true;
1355 }
1356
1357 /* Chooses the most common character among those in TARGETS,
1358    based on the frequency data in HISTOGRAM, and stores it in
1359    RESULT.  If there is a tie for the most common character among
1360    those in TARGETS, the earliest character is chosen.  If none
1361    of the TARGETS appear at all, then DEF is used as a
1362    fallback. */
1363 static void
1364 find_commonest_chars (unsigned long int histogram[UCHAR_MAX + 1],
1365                       const char *targets, const char *def,
1366                       struct string *result)
1367 {
1368   unsigned char max = 0;
1369   unsigned long int max_count = 0;
1370
1371   for (; *targets != '\0'; targets++)
1372     {
1373       unsigned char c = *targets;
1374       unsigned long int count = histogram[c];
1375       if (count > max_count)
1376         {
1377           max = c;
1378           max_count = count;
1379         }
1380     }
1381   if (max_count > 0)
1382     {
1383       ds_clear (result);
1384       ds_put_byte (result, max);
1385     }
1386   else
1387     ds_assign_cstr (result, def);
1388 }
1389
1390 /* Revises the contents of the fields tree view based on the
1391    currently chosen set of separators. */
1392 static void
1393 revise_fields_preview (struct import_assistant *ia)
1394 {
1395   GtkWidget *w;
1396
1397   push_watch_cursor (ia);
1398
1399   w = GTK_WIDGET (ia->separators.fields_tree_view);
1400   gtk_widget_destroy (w);
1401   get_separators (ia);
1402   split_fields (ia);
1403   choose_column_names (ia);
1404   ia->separators.fields_tree_view = create_data_tree_view (
1405     true,
1406     GTK_CONTAINER (get_widget_assert (ia->asst.builder, "fields-scroller")),
1407     ia);
1408
1409   pop_watch_cursor (ia);
1410 }
1411
1412 /* Sets the widgets to match IA's separators substructure. */
1413 static void
1414 set_separators (struct import_assistant *ia)
1415 {
1416   struct separators_page *s = &ia->separators;
1417   unsigned int seps;
1418   struct string custom;
1419   bool any_custom;
1420   bool any_quotes;
1421   size_t i;
1422
1423   ds_init_empty (&custom);
1424   seps = 0;
1425   for (i = 0; i < ds_length (&s->separators); i++)
1426     {
1427       unsigned char c = ds_at (&s->separators, i);
1428       int j;
1429
1430       for (j = 0; j < SEPARATOR_CNT; j++)
1431         {
1432           const struct separator *s = &separators[j];
1433           if (s->c == c)
1434             {
1435               seps += 1u << j;
1436               goto next;
1437             }
1438         }
1439
1440       ds_put_byte (&custom, c);
1441     next:;
1442     }
1443
1444   for (i = 0; i < SEPARATOR_CNT; i++)
1445     {
1446       const struct separator *s = &separators[i];
1447       GtkWidget *button = get_widget_assert (ia->asst.builder, s->name);
1448       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
1449                                     (seps & (1u << i)) != 0);
1450     }
1451   any_custom = !ds_is_empty (&custom);
1452   gtk_entry_set_text (GTK_ENTRY (s->custom_entry), ds_cstr (&custom));
1453   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->custom_cb),
1454                                 any_custom);
1455   gtk_widget_set_sensitive (s->custom_entry, any_custom);
1456   ds_destroy (&custom);
1457
1458   any_quotes = !ds_is_empty (&s->quotes);
1459
1460   gtk_entry_set_text (s->quote_entry,
1461                       any_quotes ? ds_cstr (&s->quotes) : "\"");
1462   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->quote_cb),
1463                                 any_quotes);
1464   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->escape_cb),
1465                                 s->escape);
1466   gtk_widget_set_sensitive (s->quote_combo, any_quotes);
1467   gtk_widget_set_sensitive (s->escape_cb, any_quotes);
1468 }
1469
1470 /* Sets IA's separators substructure to match the widgets. */
1471 static void
1472 get_separators (struct import_assistant *ia)
1473 {
1474   struct separators_page *s = &ia->separators;
1475   int i;
1476
1477   ds_clear (&s->separators);
1478   for (i = 0; i < SEPARATOR_CNT; i++)
1479     {
1480       const struct separator *sep = &separators[i];
1481       GtkWidget *button = get_widget_assert (ia->asst.builder, sep->name);
1482       if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
1483         ds_put_byte (&s->separators, sep->c);
1484     }
1485
1486   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->custom_cb)))
1487     ds_put_cstr (&s->separators,
1488                  gtk_entry_get_text (GTK_ENTRY (s->custom_entry)));
1489
1490   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->quote_cb)))
1491     {
1492       gchar *text = gtk_combo_box_get_active_text (
1493                       GTK_COMBO_BOX (s->quote_combo));
1494       ds_assign_cstr (&s->quotes, text);
1495       g_free (text);
1496     }
1497   else
1498     ds_clear (&s->quotes);
1499   s->escape = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->escape_cb));
1500 }
1501
1502 /* Called when the user changes the entry field for custom
1503    separators. */
1504 static void
1505 on_separators_custom_entry_notify (GObject *gobject UNUSED,
1506                                    GParamSpec *arg1 UNUSED,
1507                                    struct import_assistant *ia)
1508 {
1509   revise_fields_preview (ia);
1510 }
1511
1512 /* Called when the user toggles the checkbox that enables custom
1513    separators. */
1514 static void
1515 on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
1516                                 struct import_assistant *ia)
1517 {
1518   bool is_active = gtk_toggle_button_get_active (custom_cb);
1519   gtk_widget_set_sensitive (ia->separators.custom_entry, is_active);
1520   revise_fields_preview (ia);
1521 }
1522
1523 /* Called when the user changes the selection in the combo box
1524    that selects a quote character. */
1525 static void
1526 on_quote_combo_change (GtkComboBox *combo, struct import_assistant *ia)
1527 {
1528   revise_fields_preview (ia);
1529 }
1530
1531 /* Called when the user toggles the checkbox that enables
1532    quoting. */
1533 static void
1534 on_quote_cb_toggle (GtkToggleButton *quote_cb, struct import_assistant *ia)
1535 {
1536   bool is_active = gtk_toggle_button_get_active (quote_cb);
1537   gtk_widget_set_sensitive (ia->separators.quote_combo, is_active);
1538   gtk_widget_set_sensitive (ia->separators.escape_cb, is_active);
1539   revise_fields_preview (ia);
1540 }
1541
1542 /* Called when the user toggles one of the separators
1543    checkboxes. */
1544 static void
1545 on_separator_toggle (GtkToggleButton *toggle UNUSED,
1546                      struct import_assistant *ia)
1547 {
1548   revise_fields_preview (ia);
1549 }
1550
1551 /* Called to render one of the cells in the fields preview tree
1552    view. */
1553 static void
1554 render_input_cell (PsppSheetViewColumn *tree_column, GtkCellRenderer *cell,
1555                    GtkTreeModel *model, GtkTreeIter *iter,
1556                    gpointer ia_)
1557 {
1558   struct import_assistant *ia = ia_;
1559   struct substring field;
1560   size_t row;
1561   gint column;
1562
1563   column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1564                                                "column-number"));
1565   row = empty_list_store_iter_to_row (iter) + ia->first_line.skip_lines;
1566   field = ia->separators.columns[column].contents[row];
1567   if (field.string != NULL)
1568     {
1569       GValue text = {0, };
1570       g_value_init (&text, G_TYPE_STRING);
1571       g_value_take_string (&text, ss_xstrdup (field));
1572       g_object_set_property (G_OBJECT (cell), "text", &text);
1573       g_value_unset (&text);
1574       g_object_set (cell, "background-set", FALSE, (void *) NULL);
1575     }
1576   else
1577     g_object_set (cell,
1578                   "text", "",
1579                   "background", "red",
1580                   "background-set", TRUE,
1581                   (void *) NULL);
1582 }
1583
1584 /* Called to render a tooltip on one of the cells in the fields
1585    preview tree view. */
1586 static gboolean
1587 on_query_input_tooltip (GtkWidget *widget, gint wx, gint wy,
1588                         gboolean keyboard_mode UNUSED,
1589                         GtkTooltip *tooltip, struct import_assistant *ia)
1590 {
1591   size_t row, column;
1592
1593   if (!get_tooltip_location (widget, wx, wy, ia, &row, &column))
1594     return FALSE;
1595
1596   if (ia->separators.columns[column].contents[row].string != NULL)
1597     return FALSE;
1598
1599   gtk_tooltip_set_text (tooltip,
1600                         _("This input line has too few separators "
1601                           "to fill in this field."));
1602   return TRUE;
1603 }
1604 \f
1605 /* The "formats" page of the assistant. */
1606
1607 static void on_variable_change (PsppireDict *dict, int idx,
1608                                 struct import_assistant *);
1609 static void clear_modified_vars (struct import_assistant *);
1610
1611 /* Initializes IA's formats substructure. */
1612 static void
1613 init_formats_page (struct import_assistant *ia)
1614 {
1615   GtkBuilder *builder = ia->asst.builder;
1616   struct formats_page *p = &ia->formats;
1617
1618   p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Formats"),
1619                                    GTK_ASSISTANT_PAGE_CONFIRM);
1620   p->data_tree_view = PSPP_SHEET_VIEW (get_widget_assert (builder, "data"));
1621   p->modified_vars = NULL;
1622   p->modified_var_cnt = 0;
1623   p->dict = NULL;
1624 }
1625
1626 /* Frees IA's formats substructure. */
1627 static void
1628 destroy_formats_page (struct import_assistant *ia)
1629 {
1630   struct formats_page *p = &ia->formats;
1631
1632   if (p->psppire_dict != NULL)
1633     {
1634       dict_destroy (p->psppire_dict->dict);
1635       g_object_unref (p->psppire_dict);
1636     }
1637   clear_modified_vars (ia);
1638 }
1639
1640 /* Called just before the formats page of the assistant is
1641    displayed. */
1642 static void
1643 prepare_formats_page (struct import_assistant *ia)
1644 {
1645   struct dictionary *dict;
1646   PsppireDict *psppire_dict;
1647   GtkBin *vars_scroller;
1648   GtkWidget *old_var_sheet;
1649   PsppireVarSheet *var_sheet;
1650   struct separators_page *s = &ia->separators;
1651   struct formats_page *p = &ia->formats;
1652   struct fmt_guesser *fg;
1653   unsigned long int number = 0;
1654   size_t column_idx;
1655
1656   push_watch_cursor (ia);
1657
1658   dict = dict_create (get_default_encoding ());
1659   fg = fmt_guesser_create ();
1660   for (column_idx = 0; column_idx < s->column_cnt; column_idx++)
1661     {
1662       struct variable *modified_var;
1663
1664       modified_var = (column_idx < p->modified_var_cnt
1665                       ? p->modified_vars[column_idx] : NULL);
1666       if (modified_var == NULL)
1667         {
1668           struct column *column = &s->columns[column_idx];
1669           struct variable *var;
1670           struct fmt_spec format;
1671           char *name;
1672           size_t row;
1673
1674           /* Choose variable name. */
1675           name = dict_make_unique_var_name (dict, column->name, &number);
1676
1677           /* Choose variable format. */
1678           fmt_guesser_clear (fg);
1679           for (row = ia->first_line.skip_lines; row < ia->file.line_cnt; row++)
1680             fmt_guesser_add (fg, column->contents[row]);
1681           fmt_guesser_guess (fg, &format);
1682           fmt_fix_input (&format);
1683
1684           /* Create variable. */
1685           var = dict_create_var_assert (dict, name, fmt_var_width (&format));
1686           var_set_both_formats (var, &format);
1687
1688           free (name);
1689         }
1690       else
1691         {
1692           char *name;
1693
1694           name = dict_make_unique_var_name (dict, var_get_name (modified_var),
1695                                             &number);
1696           dict_clone_var_as_assert (dict, modified_var, name);
1697           free (name);
1698         }
1699     }
1700   fmt_guesser_destroy (fg);
1701
1702   psppire_dict = psppire_dict_new_from_dict (dict);
1703   g_signal_connect (psppire_dict, "variable_changed",
1704                     G_CALLBACK (on_variable_change), ia);
1705   ia->formats.dict = dict;
1706   ia->formats.psppire_dict = psppire_dict;
1707
1708   /* XXX: PsppireVarStore doesn't hold a reference to
1709      psppire_dict for now, but it should.  After it does, we
1710      should g_object_ref the psppire_dict here, since we also
1711      hold a reference via ia->formats.dict. */
1712   var_sheet = PSPPIRE_VAR_SHEET (psppire_var_sheet_new ());
1713   g_object_set (var_sheet,
1714                 "dictionary", psppire_dict,
1715                 "may-create-vars", FALSE,
1716                 "may-delete-vars", FALSE,
1717                 "format-use", FMT_FOR_INPUT,
1718                 "enable-grid-lines", PSPP_SHEET_VIEW_GRID_LINES_BOTH,
1719                 (void *) NULL);
1720
1721   vars_scroller = GTK_BIN (get_widget_assert (ia->asst.builder, "vars-scroller"));
1722   old_var_sheet = gtk_bin_get_child (vars_scroller);
1723   if (old_var_sheet != NULL)
1724     gtk_widget_destroy (old_var_sheet);
1725   gtk_container_add (GTK_CONTAINER (vars_scroller), GTK_WIDGET (var_sheet));
1726   gtk_widget_show (GTK_WIDGET (var_sheet));
1727
1728   gtk_widget_destroy (GTK_WIDGET (ia->formats.data_tree_view));
1729   ia->formats.data_tree_view = create_data_tree_view (
1730     false,
1731     GTK_CONTAINER (get_widget_assert (ia->asst.builder, "data-scroller")),
1732     ia);
1733
1734   pop_watch_cursor (ia);
1735 }
1736
1737 /* Clears the set of user-modified variables from IA's formats
1738    substructure.  This discards user modifications to variable
1739    formats, thereby causing formats to revert to their
1740    defaults.  */
1741 static void
1742 clear_modified_vars (struct import_assistant *ia)
1743 {
1744   struct formats_page *p = &ia->formats;
1745   size_t i;
1746
1747   for (i = 0; i < p->modified_var_cnt; i++)
1748     var_destroy (p->modified_vars[i]);
1749   free (p->modified_vars);
1750   p->modified_vars = NULL;
1751   p->modified_var_cnt = 0;
1752 }
1753
1754 /* Resets the formats page to its defaults, discarding user
1755    modifications. */
1756 static void
1757 reset_formats_page (struct import_assistant *ia)
1758 {
1759   clear_modified_vars (ia);
1760   prepare_formats_page (ia);
1761 }
1762
1763 /* Called when the user changes one of the variables in the
1764    dictionary. */
1765 static void
1766 on_variable_change (PsppireDict *dict, int dict_idx,
1767                     struct import_assistant *ia)
1768 {
1769   struct formats_page *p = &ia->formats;
1770   PsppSheetView *tv = ia->formats.data_tree_view;
1771   gint column_idx = dict_idx + 1;
1772
1773   push_watch_cursor (ia);
1774
1775   /* Remove previous column and replace with new column. */
1776   pspp_sheet_view_remove_column (tv, pspp_sheet_view_get_column (tv, column_idx));
1777   pspp_sheet_view_insert_column (tv, make_data_column (ia, tv, false, dict_idx),
1778                                column_idx);
1779
1780   /* Save a copy of the modified variable in modified_vars, so
1781      that its attributes will be preserved if we back up to the
1782      previous page with the Prev button and then come back
1783      here. */
1784   if (dict_idx >= p->modified_var_cnt)
1785     {
1786       size_t i;
1787       p->modified_vars = xnrealloc (p->modified_vars, dict_idx + 1,
1788                                     sizeof *p->modified_vars);
1789       for (i = 0; i <= dict_idx; i++)
1790         p->modified_vars[i] = NULL;
1791       p->modified_var_cnt = dict_idx + 1;
1792     }
1793   if (p->modified_vars[dict_idx])
1794     var_destroy (p->modified_vars[dict_idx]);
1795   p->modified_vars[dict_idx]
1796     = var_clone (psppire_dict_get_variable (dict, dict_idx));
1797
1798   pop_watch_cursor (ia);
1799 }
1800
1801 /* Parses the contents of the field at (ROW,COLUMN) according to
1802    its variable format.  If OUTPUTP is non-null, then *OUTPUTP
1803    receives the formatted output for that field (which must be
1804    freed with free).  If TOOLTIPP is non-null, then *TOOLTIPP
1805    receives a message suitable for use in a tooltip, if one is
1806    needed, or a null pointer otherwise.  Returns true if a
1807    tooltip message is needed, otherwise false. */
1808 static bool
1809 parse_field (struct import_assistant *ia,
1810              size_t row, size_t column,
1811              char **outputp, char **tooltipp)
1812 {
1813   struct substring field;
1814   union value val;
1815   struct variable *var;
1816   const struct fmt_spec *in;
1817   struct fmt_spec out;
1818   char *tooltip;
1819   bool ok;
1820
1821   field = ia->separators.columns[column].contents[row];
1822   var = dict_get_var (ia->formats.dict, column);
1823   value_init (&val, var_get_width (var));
1824   in = var_get_print_format (var);
1825   out = fmt_for_output_from_input (in);
1826   tooltip = NULL;
1827   if (field.string != NULL)
1828     {
1829       char *error;
1830
1831       error = data_in (field, "UTF-8", in->type, &val, var_get_width (var),
1832                        dict_get_encoding (ia->formats.dict));
1833       if (error != NULL)
1834         {
1835           tooltip = xasprintf (_("Cannot parse field content `%.*s' as "
1836                                  "format %s: %s"),
1837                                (int) field.length, field.string,
1838                                fmt_name (in->type), error);
1839           free (error);
1840         }
1841     }
1842   else
1843     {
1844       tooltip = xstrdup (_("This input line has too few separators "
1845                            "to fill in this field."));
1846       value_set_missing (&val, var_get_width (var));
1847     }
1848   if (outputp != NULL)
1849     {
1850       *outputp = data_out (&val, dict_get_encoding (ia->formats.dict),  &out);
1851     }
1852   value_destroy (&val, var_get_width (var));
1853
1854   ok = tooltip == NULL;
1855   if (tooltipp != NULL)
1856     *tooltipp = tooltip;
1857   else
1858     free (tooltip);
1859   return ok;
1860 }
1861
1862 /* Called to render one of the cells in the data preview tree
1863    view. */
1864 static void
1865 render_output_cell (PsppSheetViewColumn *tree_column,
1866                     GtkCellRenderer *cell,
1867                     GtkTreeModel *model,
1868                     GtkTreeIter *iter,
1869                     gpointer ia_)
1870 {
1871   struct import_assistant *ia = ia_;
1872   char *output;
1873   GValue gvalue = { 0, };
1874   bool ok;
1875
1876   ok = parse_field (ia,
1877                     (empty_list_store_iter_to_row (iter)
1878                      + ia->first_line.skip_lines),
1879                     GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1880                                                         "column-number")),
1881                     &output, NULL);
1882
1883   g_value_init (&gvalue, G_TYPE_STRING);
1884   g_value_take_string (&gvalue, output);
1885   g_object_set_property (G_OBJECT (cell), "text", &gvalue);
1886   g_value_unset (&gvalue);
1887
1888   if (ok)
1889     g_object_set (cell, "background-set", FALSE, (void *) NULL);
1890   else
1891     g_object_set (cell,
1892                   "background", "red",
1893                   "background-set", TRUE,
1894                   (void *) NULL);
1895 }
1896
1897 /* Called to render a tooltip for one of the cells in the data
1898    preview tree view. */
1899 static gboolean
1900 on_query_output_tooltip (GtkWidget *widget, gint wx, gint wy,
1901                         gboolean keyboard_mode UNUSED,
1902                         GtkTooltip *tooltip, struct import_assistant *ia)
1903 {
1904   size_t row, column;
1905   char *text;
1906
1907   if (!get_tooltip_location (widget, wx, wy, ia, &row, &column))
1908     return FALSE;
1909
1910   if (parse_field (ia, row, column, NULL, &text))
1911     return FALSE;
1912
1913   gtk_tooltip_set_text (tooltip, text);
1914   free (text);
1915   return TRUE;
1916 }
1917 \f
1918 /* Utility functions used by multiple pages of the assistant. */
1919
1920 static gboolean
1921 get_tooltip_location (GtkWidget *widget, gint wx, gint wy,
1922                       const struct import_assistant *ia,
1923                       size_t *row, size_t *column)
1924 {
1925   PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1926   gint bx, by;
1927   GtkTreePath *path;
1928   GtkTreeIter iter;
1929   PsppSheetViewColumn *tree_column;
1930   GtkTreeModel *tree_model;
1931   bool ok;
1932
1933   /* Check that WIDGET is really visible on the screen before we
1934      do anything else.  This is a bug fix for a sticky situation:
1935      when text_data_import_assistant() returns, it frees the data
1936      necessary to compose the tool tip message, but there may be
1937      a tool tip under preparation at that point (even if there is
1938      no visible tool tip) that will call back into us a little
1939      bit later.  Perhaps the correct solution to this problem is
1940      to make the data related to the tool tips part of a GObject
1941      that only gets destroyed when all references are released,
1942      but this solution appears to be effective too. */
1943   if (!gtk_widget_get_mapped (widget))
1944     return FALSE;
1945
1946   pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view,
1947                                                      wx, wy, &bx, &by);
1948   if (!pspp_sheet_view_get_path_at_pos (tree_view, bx, by,
1949                                       &path, &tree_column, NULL, NULL))
1950     return FALSE;
1951
1952   *column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1953                                                 "column-number"));
1954
1955   tree_model = pspp_sheet_view_get_model (tree_view);
1956   ok = gtk_tree_model_get_iter (tree_model, &iter, path);
1957   gtk_tree_path_free (path);
1958   if (!ok)
1959     return FALSE;
1960
1961   *row = empty_list_store_iter_to_row (&iter) + ia->first_line.skip_lines;
1962   return TRUE;
1963 }
1964
1965 static void
1966 make_tree_view (const struct import_assistant *ia,
1967                 size_t first_line,
1968                 PsppSheetView **tree_view)
1969 {
1970   GtkTreeModel *model;
1971
1972   *tree_view = PSPP_SHEET_VIEW (pspp_sheet_view_new ());
1973   pspp_sheet_view_set_grid_lines (*tree_view, PSPP_SHEET_VIEW_GRID_LINES_BOTH);
1974   model = GTK_TREE_MODEL (psppire_empty_list_store_new (
1975                             ia->file.line_cnt - first_line));
1976   g_object_set_data (G_OBJECT (model), "lines", ia->file.lines + first_line);
1977   g_object_set_data (G_OBJECT (model), "first-line",
1978                      GINT_TO_POINTER (first_line));
1979   pspp_sheet_view_set_model (*tree_view, model);
1980   g_object_unref (model);
1981
1982   add_line_number_column (ia, *tree_view);
1983 }
1984
1985 static void
1986 render_line_number (PsppSheetViewColumn *tree_column,
1987                     GtkCellRenderer *cell,
1988                     GtkTreeModel *tree_model,
1989                     GtkTreeIter *iter,
1990                     gpointer data)
1991 {
1992   gint row = empty_list_store_iter_to_row (iter);
1993   char s[INT_BUFSIZE_BOUND (int)];
1994   int first_line;
1995
1996   first_line = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_model),
1997                                                    "first-line"));
1998   sprintf (s, "%d", first_line + row);
1999   g_object_set (cell, "text", s, NULL);
2000 }
2001
2002 static void
2003 add_line_number_column (const struct import_assistant *ia,
2004                         PsppSheetView *treeview)
2005 {
2006   PsppSheetViewColumn *column;
2007
2008   column = pspp_sheet_view_column_new_with_attributes (
2009     _("Line"), ia->asst.prop_renderer, (void *) NULL);
2010   pspp_sheet_view_column_set_fixed_width (
2011     column, get_monospace_width (treeview, ia->asst.prop_renderer, 5));
2012   pspp_sheet_view_column_set_resizable (column, TRUE);
2013   pspp_sheet_view_column_set_cell_data_func (column, ia->asst.prop_renderer,
2014                                              render_line_number, NULL, NULL);
2015   pspp_sheet_view_append_column (treeview, column);
2016 }
2017
2018 static gint
2019 get_monospace_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
2020                      size_t char_cnt)
2021 {
2022   struct string s;
2023   gint width;
2024
2025   ds_init_empty (&s);
2026   ds_put_byte_multiple (&s, '0', char_cnt);
2027   ds_put_byte (&s, ' ');
2028   width = get_string_width (treeview, renderer, ds_cstr (&s));
2029   ds_destroy (&s);
2030
2031   return width;
2032 }
2033
2034 static gint
2035 get_string_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
2036                   const char *string)
2037 {
2038   gint width;
2039   g_object_set (G_OBJECT (renderer), "text", string, (void *) NULL);
2040   gtk_cell_renderer_get_size (renderer, GTK_WIDGET (treeview),
2041                               NULL, NULL, NULL, &width, NULL);
2042   return width;
2043 }
2044
2045 static PsppSheetViewColumn *
2046 make_data_column (struct import_assistant *ia, PsppSheetView *tree_view,
2047                   bool input, gint dict_idx)
2048 {
2049   struct variable *var = NULL;
2050   struct column *column = NULL;
2051   size_t char_cnt;
2052   gint content_width, header_width;
2053   PsppSheetViewColumn *tree_column;
2054   char *name;
2055
2056   if (input)
2057     column = &ia->separators.columns[dict_idx];
2058   else
2059     var = dict_get_var (ia->formats.dict, dict_idx);
2060
2061   name = escape_underscores (input ? column->name : var_get_name (var));
2062   char_cnt = input ? column->width : var_get_print_format (var)->w;
2063   content_width = get_monospace_width (tree_view, ia->asst.fixed_renderer,
2064                                        char_cnt);
2065   header_width = get_string_width (tree_view, ia->asst.prop_renderer,
2066                                    name);
2067
2068   tree_column = pspp_sheet_view_column_new ();
2069   g_object_set_data (G_OBJECT (tree_column), "column-number",
2070                      GINT_TO_POINTER (dict_idx));
2071   pspp_sheet_view_column_set_title (tree_column, name);
2072   pspp_sheet_view_column_pack_start (tree_column, ia->asst.fixed_renderer,
2073                                    FALSE);
2074   pspp_sheet_view_column_set_cell_data_func (
2075     tree_column, ia->asst.fixed_renderer,
2076     input ? render_input_cell : render_output_cell, ia, NULL);
2077   pspp_sheet_view_column_set_fixed_width (tree_column, MAX (content_width,
2078                                                           header_width));
2079   pspp_sheet_view_column_set_resizable (tree_column, TRUE);
2080
2081   free (name);
2082
2083   return tree_column;
2084 }
2085
2086 static PsppSheetView *
2087 create_data_tree_view (bool input, GtkContainer *parent,
2088                        struct import_assistant *ia)
2089 {
2090   PsppSheetView *tree_view;
2091   gint i;
2092
2093   make_tree_view (ia, ia->first_line.skip_lines, &tree_view);
2094   pspp_sheet_selection_set_mode (pspp_sheet_view_get_selection (tree_view),
2095                                PSPP_SHEET_SELECTION_NONE);
2096
2097   for (i = 0; i < ia->separators.column_cnt; i++)
2098     pspp_sheet_view_append_column (tree_view,
2099                                  make_data_column (ia, tree_view, input, i));
2100
2101   g_object_set (G_OBJECT (tree_view), "has-tooltip", TRUE, (void *) NULL);
2102   g_signal_connect (tree_view, "query-tooltip",
2103                     G_CALLBACK (input ? on_query_input_tooltip
2104                                 : on_query_output_tooltip), ia);
2105
2106   gtk_container_add (parent, GTK_WIDGET (tree_view));
2107   gtk_widget_show (GTK_WIDGET (tree_view));
2108
2109   return tree_view;
2110 }
2111
2112 /* Increments the "watch cursor" level, setting the cursor for
2113    the assistant window to a watch face to indicate to the user
2114    that the ongoing operation may take some time. */
2115 static void
2116 push_watch_cursor (struct import_assistant *ia)
2117 {
2118   if (++ia->asst.watch_cursor == 1)
2119     {
2120       GtkWidget *widget = GTK_WIDGET (ia->asst.assistant);
2121       GdkDisplay *display = gtk_widget_get_display (widget);
2122       GdkCursor *cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
2123       gdk_window_set_cursor (widget->window, cursor);
2124       gdk_cursor_unref (cursor);
2125       gdk_display_flush (display);
2126     }
2127 }
2128
2129 /* Decrements the "watch cursor" level.  If the level reaches
2130    zero, the cursor is reset to its default shape. */
2131 static void
2132 pop_watch_cursor (struct import_assistant *ia)
2133 {
2134   if (--ia->asst.watch_cursor == 0)
2135     {
2136       GtkWidget *widget = GTK_WIDGET (ia->asst.assistant);
2137       gdk_window_set_cursor (widget->window, NULL);
2138     }
2139 }