gui: Redo var sheet, data sheet, text import with PsppSheetView.
[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/gui/psppire-var-store.h"
52 #include "ui/syntax-gen.h"
53
54 #include "gl/error.h"
55 #include "gl/intprops.h"
56 #include "gl/xalloc.h"
57
58 #include "gettext.h"
59 #define _(msgid) gettext (msgid)
60 #define N_(msgid) msgid
61
62 struct import_assistant;
63
64 /* The file to be imported. */
65 struct file
66   {
67     char *file_name;        /* File name. */
68     gchar *encoding;        /* Encoding. */
69     unsigned long int total_lines; /* Number of lines in file. */
70     bool total_is_exact;    /* Is total_lines exact (or an estimate)? */
71
72     /* The first several lines of the file. */
73     struct string *lines;
74     size_t line_cnt;
75   };
76 static bool init_file (struct import_assistant *, GtkWindow *parent);
77 static void destroy_file (struct import_assistant *);
78
79 /* The main body of the GTK+ assistant and related data. */
80 struct assistant
81   {
82     GtkBuilder *builder;
83     GtkAssistant *assistant;
84     GMainLoop *main_loop;
85     GtkWidget *paste_button;
86     GtkWidget *reset_button;
87     int response;
88     int watch_cursor;
89
90     GtkCellRenderer *prop_renderer;
91     GtkCellRenderer *fixed_renderer;
92   };
93 static void init_assistant (struct import_assistant *, GtkWindow *);
94 static void destroy_assistant (struct import_assistant *);
95 static GtkWidget *add_page_to_assistant (struct import_assistant *,
96                                          GtkWidget *page,
97                                          GtkAssistantPageType);
98
99 /* The introduction page of the assistant. */
100 struct intro_page
101   {
102     GtkWidget *page;
103     GtkWidget *all_cases_button;
104     GtkWidget *n_cases_button;
105     GtkWidget *n_cases_spin;
106     GtkWidget *percent_button;
107     GtkWidget *percent_spin;
108   };
109 static void init_intro_page (struct import_assistant *);
110 static void reset_intro_page (struct import_assistant *);
111
112 /* Page where the user chooses the first line of data. */
113 struct first_line_page
114   {
115     int skip_lines;    /* Number of initial lines to skip? */
116     bool variable_names; /* Variable names above first line of data? */
117
118     GtkWidget *page;
119     PsppSheetView *tree_view;
120     GtkWidget *variable_names_cb;
121   };
122 static void init_first_line_page (struct import_assistant *);
123 static void reset_first_line_page (struct import_assistant *);
124
125 /* Page where the user chooses field separators. */
126 struct separators_page
127   {
128     /* How to break lines into columns. */
129     struct string separators;   /* Field separators. */
130     struct string quotes;       /* Quote characters. */
131     bool escape;                /* Doubled quotes yield a quote mark? */
132
133     /* The columns produced thereby. */
134     struct column *columns;     /* Information about each column. */
135     size_t column_cnt;          /* Number of columns. */
136
137     GtkWidget *page;
138     GtkWidget *custom_cb;
139     GtkWidget *custom_entry;
140     GtkWidget *quote_cb;
141     GtkWidget *quote_combo;
142     GtkEntry *quote_entry;
143     GtkWidget *escape_cb;
144     PsppSheetView *fields_tree_view;
145   };
146 /* The columns that the separators divide the data into. */
147 struct column
148   {
149     /* Variable name for this column.  This is the variable name
150        used on the separators page; it can be overridden by the
151        user on the formats page. */
152     char *name;
153
154     /* Maximum length of any row in this column. */
155     size_t width;
156
157     /* Contents of this column: contents[row] is the contents for
158        the given row.
159
160        A null substring indicates a missing column for that row
161        (because the line contains an insufficient number of
162        separators).
163
164        contents[] elements may be substrings of the lines[]
165        strings that represent the whole lines of the file, to
166        save memory.  Other elements are dynamically allocated
167        with ss_alloc_substring. */
168     struct substring *contents;
169   };
170 static void init_separators_page (struct import_assistant *);
171 static void destroy_separators_page (struct import_assistant *);
172 static void prepare_separators_page (struct import_assistant *);
173 static void reset_separators_page (struct import_assistant *);
174
175 /* Page where the user verifies and adjusts input formats. */
176 struct formats_page
177   {
178     struct dictionary *dict;
179
180     GtkWidget *page;
181     PsppSheetView *data_tree_view;
182     PsppireDict *psppire_dict;
183     struct variable **modified_vars;
184     size_t modified_var_cnt;
185   };
186 static void init_formats_page (struct import_assistant *);
187 static void destroy_formats_page (struct import_assistant *);
188 static void prepare_formats_page (struct import_assistant *);
189 static void reset_formats_page (struct import_assistant *);
190
191 struct import_assistant
192   {
193     struct file file;
194     struct assistant asst;
195     struct intro_page intro;
196     struct first_line_page first_line;
197     struct separators_page separators;
198     struct formats_page formats;
199   };
200
201 static void apply_dict (const struct dictionary *, struct string *);
202 static char *generate_syntax (const struct import_assistant *);
203
204 static gboolean get_tooltip_location (GtkWidget *widget, gint wx, gint wy,
205                                       const struct import_assistant *,
206                                       size_t *row, size_t *column);
207 static void make_tree_view (const struct import_assistant *ia,
208                             size_t first_line,
209                             PsppSheetView **tree_view);
210 static void add_line_number_column (const struct import_assistant *,
211                                     PsppSheetView *);
212 static gint get_monospace_width (PsppSheetView *, GtkCellRenderer *,
213                                  size_t char_cnt);
214 static gint get_string_width (PsppSheetView *, GtkCellRenderer *,
215                               const char *string);
216 static PsppSheetViewColumn *make_data_column (struct import_assistant *,
217                                             PsppSheetView *, bool input,
218                                             gint column_idx);
219 static PsppSheetView *create_data_tree_view (bool input, GtkContainer *parent,
220                                            struct import_assistant *);
221 static void push_watch_cursor (struct import_assistant *);
222 static void pop_watch_cursor (struct import_assistant *);
223
224 /* Pops up the Text Data Import assistant. */
225 void
226 text_data_import_assistant (PsppireDataWindow *dw)
227 {
228   GtkWindow *parent_window = GTK_WINDOW (dw);
229   struct import_assistant *ia;
230
231   ia = xzalloc (sizeof *ia);
232   if (!init_file (ia, parent_window))
233     {
234       free (ia);
235       return;
236     }
237
238   init_assistant (ia, parent_window);
239   init_intro_page (ia);
240   init_first_line_page (ia);
241   init_separators_page (ia);
242   init_formats_page (ia);
243
244   gtk_widget_show_all (GTK_WIDGET (ia->asst.assistant));
245
246   ia->asst.main_loop = g_main_loop_new (NULL, false);
247   g_main_loop_run (ia->asst.main_loop);
248   g_main_loop_unref (ia->asst.main_loop);
249
250   switch (ia->asst.response)
251     {
252     case GTK_RESPONSE_APPLY:
253       free (execute_syntax_string (dw, generate_syntax (ia)));
254       break;
255     case PSPPIRE_RESPONSE_PASTE:
256       free (paste_syntax_to_window (generate_syntax (ia)));
257       break;
258     default:
259       break;
260     }
261
262   destroy_formats_page (ia);
263   destroy_separators_page (ia);
264   destroy_assistant (ia);
265   destroy_file (ia);
266   free (ia);
267 }
268
269 /* Emits PSPP syntax to S that applies the dictionary attributes
270    (such as missing values and value labels) of the variables in
271    DICT.  */
272 static void
273 apply_dict (const struct dictionary *dict, struct string *s)
274 {
275   size_t var_cnt = dict_get_var_cnt (dict);
276   size_t i;
277
278   for (i = 0; i < var_cnt; i++)
279     {
280       struct variable *var = dict_get_var (dict, i);
281       const char *name = var_get_name (var);
282       enum val_type type = var_get_type (var);
283       int width = var_get_width (var);
284       enum measure measure = var_get_measure (var);
285       enum alignment alignment = var_get_alignment (var);
286       const struct fmt_spec *format = var_get_print_format (var);
287
288       if (var_has_missing_values (var))
289         {
290           const struct missing_values *mv = var_get_missing_values (var);
291           size_t j;
292
293           syntax_gen_pspp (s, "MISSING VALUES %ss (", name);
294           for (j = 0; j < mv_n_values (mv); j++)
295             {
296               if (j)
297                 ds_put_cstr (s, ", ");
298               syntax_gen_value (s, mv_get_value (mv, j), width, format);
299             }
300
301           if (mv_has_range (mv))
302             {
303               double low, high;
304               if (mv_has_value (mv))
305                 ds_put_cstr (s, ", ");
306               mv_get_range (mv, &low, &high);
307               syntax_gen_num_range (s, low, high, format);
308             }
309           ds_put_cstr (s, ").\n");
310         }
311       if (var_has_value_labels (var))
312         {
313           const struct val_labs *vls = var_get_value_labels (var);
314           const struct val_lab **labels = val_labs_sorted (vls);
315           size_t n_labels = val_labs_count (vls);
316           size_t i;
317
318           syntax_gen_pspp (s, "VALUE LABELS %ss", name);
319           for (i = 0; i < n_labels; i++)
320             {
321               const struct val_lab *vl = labels[i];
322               ds_put_cstr (s, "\n  ");
323               syntax_gen_value (s, &vl->value, width, format);
324               ds_put_byte (s, ' ');
325               syntax_gen_string (s, ss_cstr (val_lab_get_escaped_label (vl)));
326             }
327           free (labels);
328           ds_put_cstr (s, ".\n");
329         }
330       if (var_has_label (var))
331         syntax_gen_pspp (s, "VARIABLE LABELS %ss %sq.\n",
332                          name, var_get_label (var));
333       if (measure != var_default_measure (type))
334         syntax_gen_pspp (s, "VARIABLE LEVEL %ss (%ss).\n",
335                          name,
336                          (measure == MEASURE_NOMINAL ? "NOMINAL"
337                           : measure == MEASURE_ORDINAL ? "ORDINAL"
338                           : "SCALE"));
339       if (alignment != var_default_alignment (type))
340         syntax_gen_pspp (s, "VARIABLE ALIGNMENT %ss (%ss).\n",
341                          name,
342                          (alignment == ALIGN_LEFT ? "LEFT"
343                           : alignment == ALIGN_CENTRE ? "CENTER"
344                           : "RIGHT"));
345       if (var_get_display_width (var) != var_default_display_width (width))
346         syntax_gen_pspp (s, "VARIABLE WIDTH %ss (%d).\n",
347                          name, var_get_display_width (var));
348     }
349 }
350
351 /* Generates and returns PSPP syntax to execute the import
352    operation described by IA.  The caller must free the syntax
353    with free(). */
354 static char *
355 generate_syntax (const struct import_assistant *ia)
356 {
357   struct string s = DS_EMPTY_INITIALIZER;
358   size_t var_cnt;
359   size_t i;
360
361   syntax_gen_pspp (&s,
362                    "GET DATA\n"
363                    "  /TYPE=TXT\n"
364                    "  /FILE=%sq\n",
365                    ia->file.file_name);
366   if (ia->file.encoding && strcmp (ia->file.encoding, "Auto"))
367     syntax_gen_pspp (&s, "  /ENCODING=%sq\n", ia->file.encoding);
368   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (
369                                       ia->intro.n_cases_button)))
370     ds_put_format (&s, "  /IMPORTCASES=FIRST %d\n",
371                    gtk_spin_button_get_value_as_int (
372                      GTK_SPIN_BUTTON (ia->intro.n_cases_spin)));
373   else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (
374                                            ia->intro.percent_button)))
375     ds_put_format (&s, "  /IMPORTCASES=PERCENT %d\n",
376                    gtk_spin_button_get_value_as_int (
377                      GTK_SPIN_BUTTON (ia->intro.percent_spin)));
378   else
379     ds_put_cstr (&s, "  /IMPORTCASES=ALL\n");
380   ds_put_cstr (&s,
381                "  /ARRANGEMENT=DELIMITED\n"
382                "  /DELCASE=LINE\n");
383   if (ia->first_line.skip_lines > 0)
384     ds_put_format (&s, "  /FIRSTCASE=%d\n", ia->first_line.skip_lines + 1);
385   ds_put_cstr (&s, "  /DELIMITERS=\"");
386   if (ds_find_byte (&ia->separators.separators, '\t') != SIZE_MAX)
387     ds_put_cstr (&s, "\\t");
388   if (ds_find_byte (&ia->separators.separators, '\\') != SIZE_MAX)
389     ds_put_cstr (&s, "\\\\");
390   for (i = 0; i < ds_length (&ia->separators.separators); i++)
391     {
392       char c = ds_at (&ia->separators.separators, i);
393       if (c == '"')
394         ds_put_cstr (&s, "\"\"");
395       else if (c != '\t' && c != '\\')
396         ds_put_byte (&s, c);
397     }
398   ds_put_cstr (&s, "\"\n");
399   if (!ds_is_empty (&ia->separators.quotes))
400     syntax_gen_pspp (&s, "  /QUALIFIER=%sq\n", ds_cstr (&ia->separators.quotes));
401   if (!ds_is_empty (&ia->separators.quotes) && ia->separators.escape)
402     ds_put_cstr (&s, "  /ESCAPE\n");
403   ds_put_cstr (&s, "  /VARIABLES=\n");
404
405   var_cnt = dict_get_var_cnt (ia->formats.dict);
406   for (i = 0; i < var_cnt; i++)
407     {
408       struct variable *var = dict_get_var (ia->formats.dict, i);
409       char format_string[FMT_STRING_LEN_MAX + 1];
410       fmt_to_string (var_get_print_format (var), format_string);
411       ds_put_format (&s, "    %s %s%s\n",
412                      var_get_name (var), format_string,
413                      i == var_cnt - 1 ? "." : "");
414     }
415
416   apply_dict (ia->formats.dict, &s);
417
418   return ds_cstr (&s);
419 }
420 \f
421 /* Choosing a file and reading it. */
422
423 static char *choose_file (GtkWindow *parent_window, gchar **encodingp);
424
425 /* Obtains the file to import from the user and initializes IA's
426    file substructure.  PARENT_WINDOW must be the window to use
427    as the file chooser window's parent.
428
429    Returns true if successful, false if the file name could not
430    be obtained or the file could not be read. */
431 static bool
432 init_file (struct import_assistant *ia, GtkWindow *parent_window)
433 {
434   struct file *file = &ia->file;
435   enum { MAX_PREVIEW_LINES = 1000 }; /* Max number of lines to read. */
436   enum { MAX_LINE_LEN = 16384 }; /* Max length of an acceptable line. */
437   struct line_reader *reader;
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   file->lines = xnmalloc (MAX_PREVIEW_LINES, sizeof *file->lines);
452   for (; file->line_cnt < MAX_PREVIEW_LINES; file->line_cnt++)
453     {
454       struct string *line = &file->lines[file->line_cnt];
455
456       ds_init_empty (line);
457       if (!line_reader_read (reader, line, MAX_LINE_LEN + 1)
458           || ds_length (line) > MAX_LINE_LEN)
459         {
460           if (line_reader_eof (reader))
461             break;
462           else if (line_reader_error (reader))
463             msg (ME, _("Error reading `%s': %s"),
464                  file->file_name, strerror (line_reader_error (reader)));
465           else
466             msg (ME, _("Failed to read `%s', because it contains a line "
467                        "over %d bytes long and therefore appears not to be "
468                        "a text file."),
469                  file->file_name, MAX_LINE_LEN);
470           line_reader_close (reader);
471           destroy_file (ia);
472           return false;
473         }
474     }
475
476   if (file->line_cnt == 0)
477     {
478       msg (ME, _("`%s' is empty."), file->file_name);
479       line_reader_close (reader);
480       destroy_file (ia);
481       return false;
482     }
483
484   /* Estimate the number of lines in the file. */
485   if (file->line_cnt < MAX_PREVIEW_LINES)
486     file->total_lines = file->line_cnt;
487   else
488     {
489       struct stat s;
490       off_t position = line_reader_tell (reader);
491       if (fstat (line_reader_fileno (reader), &s) == 0 && position > 0)
492         file->total_lines = (double) file->line_cnt / position * s.st_size;
493       else
494         file->total_lines = 0;
495     }
496
497   line_reader_close (reader);
498
499   return true;
500 }
501
502 /* Frees IA's file substructure. */
503 static void
504 destroy_file (struct import_assistant *ia)
505 {
506   struct file *f = &ia->file;
507   size_t i;
508
509   for (i = 0; i < f->line_cnt; i++)
510     ds_destroy (&f->lines[i]);
511   free (f->lines);
512   g_free (f->file_name);
513   g_free (f->encoding);
514 }
515
516 /* Obtains the file to read from the user.  If successful, returns the name of
517    the file and stores the user's chosen encoding for the file into *ENCODINGP.
518    The caller must free each of these strings with g_free().
519
520    On failure, stores a null pointer and stores NULL in *ENCODINGP.
521
522    PARENT_WINDOW must be the window to use as the file chooser window's
523    parent. */
524 static char *
525 choose_file (GtkWindow *parent_window, gchar **encodingp)
526 {
527   char *file_name;
528   GtkFileFilter *filter = NULL;
529
530   GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Import Delimited Text Data"),
531                                         parent_window,
532                                         GTK_FILE_CHOOSER_ACTION_OPEN,
533                                         GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
534                                         GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
535                                         NULL);
536
537   g_object_set (dialog, "local-only", FALSE, NULL);
538
539   filter = gtk_file_filter_new ();
540   gtk_file_filter_set_name (filter, _("Text files"));
541   gtk_file_filter_add_mime_type (filter, "text/*");
542   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
543
544   filter = gtk_file_filter_new ();
545   gtk_file_filter_set_name (filter, _("Text (*.txt) Files"));
546   gtk_file_filter_add_pattern (filter, "*.txt");
547   gtk_file_filter_add_pattern (filter, "*.TXT");
548   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
549
550   filter = gtk_file_filter_new ();
551   gtk_file_filter_set_name (filter, _("Plain Text (ASCII) Files"));
552   gtk_file_filter_add_mime_type (filter, "text/plain");
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, _("Comma Separated Value Files"));
557   gtk_file_filter_add_mime_type (filter, "text/csv");
558   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
559
560   /* I've never encountered one of these, but it's listed here:
561      http://www.iana.org/assignments/media-types/text/tab-separated-values  */
562   filter = gtk_file_filter_new ();
563   gtk_file_filter_set_name (filter, _("Tab Separated Value Files"));
564   gtk_file_filter_add_mime_type (filter, "text/tab-separated-values");
565   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
566
567   gtk_file_chooser_set_extra_widget (
568     GTK_FILE_CHOOSER (dialog), psppire_encoding_selector_new ("Auto", true));
569
570   filter = gtk_file_filter_new ();
571   gtk_file_filter_set_name (filter, _("All Files"));
572   gtk_file_filter_add_pattern (filter, "*");
573   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
574
575   switch (gtk_dialog_run (GTK_DIALOG (dialog)))
576     {
577     case GTK_RESPONSE_ACCEPT:
578       file_name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
579       *encodingp = psppire_encoding_selector_get_encoding (
580         gtk_file_chooser_get_extra_widget (GTK_FILE_CHOOSER (dialog)));
581       break;
582     default:
583       file_name = NULL;
584       *encodingp = NULL;
585       break;
586     }
587   gtk_widget_destroy (dialog);
588
589   return file_name;
590 }
591 \f
592 /* Assistant. */
593
594 static void close_assistant (struct import_assistant *, int response);
595 static void on_prepare (GtkAssistant *assistant, GtkWidget *page,
596                         struct import_assistant *);
597 static void on_cancel (GtkAssistant *assistant, struct import_assistant *);
598 static void on_close (GtkAssistant *assistant, struct import_assistant *);
599 static void on_paste (GtkButton *button, struct import_assistant *);
600 static void on_reset (GtkButton *button, struct import_assistant *);
601 static void close_assistant (struct import_assistant *, int response);
602
603 /* Initializes IA's asst substructure.  PARENT_WINDOW must be the
604    window to use as the assistant window's parent.  */
605 static void
606 init_assistant (struct import_assistant *ia, GtkWindow *parent_window)
607 {
608   struct assistant *a = &ia->asst;
609
610   a->builder = builder_new ("text-data-import.ui");
611   a->assistant = GTK_ASSISTANT (gtk_assistant_new ());
612   g_signal_connect (a->assistant, "prepare", G_CALLBACK (on_prepare), ia);
613   g_signal_connect (a->assistant, "cancel", G_CALLBACK (on_cancel), ia);
614   g_signal_connect (a->assistant, "close", G_CALLBACK (on_close), ia);
615   a->paste_button = gtk_button_new_from_stock (GTK_STOCK_PASTE);
616   gtk_assistant_add_action_widget (a->assistant, a->paste_button);
617   g_signal_connect (a->paste_button, "clicked", G_CALLBACK (on_paste), ia);
618   a->reset_button = gtk_button_new_from_stock ("pspp-stock-reset");
619   gtk_assistant_add_action_widget (a->assistant, a->reset_button);
620   g_signal_connect (a->reset_button, "clicked", G_CALLBACK (on_reset), ia);
621   gtk_window_set_title (GTK_WINDOW (a->assistant),
622                         _("Importing Delimited Text Data"));
623   gtk_window_set_transient_for (GTK_WINDOW (a->assistant), parent_window);
624   gtk_window_set_icon_name (GTK_WINDOW (a->assistant), "pspp");
625
626   a->prop_renderer = gtk_cell_renderer_text_new ();
627   g_object_ref_sink (a->prop_renderer);
628   a->fixed_renderer = gtk_cell_renderer_text_new ();
629   g_object_ref_sink (a->fixed_renderer);
630   g_object_set (G_OBJECT (a->fixed_renderer),
631                 "family", "Monospace",
632                 (void *) NULL);
633 }
634
635 /* Frees IA's asst substructure. */
636 static void
637 destroy_assistant (struct import_assistant *ia)
638 {
639   struct assistant *a = &ia->asst;
640
641   g_object_unref (a->prop_renderer);
642   g_object_unref (a->fixed_renderer);
643   g_object_unref (a->builder);
644 }
645
646 /* Appends a page of the given TYPE, with PAGE as its content, to
647    the GtkAssistant encapsulated by IA.  Returns the GtkWidget
648    that represents the page. */
649 static GtkWidget *
650 add_page_to_assistant (struct import_assistant *ia,
651                        GtkWidget *page, GtkAssistantPageType type)
652 {
653   const char *title;
654   char *title_copy;
655   GtkWidget *content;
656
657   title = gtk_window_get_title (GTK_WINDOW (page));
658   title_copy = xstrdup (title ? title : "");
659
660   content = gtk_bin_get_child (GTK_BIN (page));
661   assert (content);
662   g_object_ref (content);
663   gtk_container_remove (GTK_CONTAINER (page), content);
664
665   gtk_widget_destroy (page);
666
667   gtk_assistant_append_page (ia->asst.assistant, content);
668   gtk_assistant_set_page_type (ia->asst.assistant, content, type);
669   gtk_assistant_set_page_title (ia->asst.assistant, content, title_copy);
670   gtk_assistant_set_page_complete (ia->asst.assistant, content, true);
671
672   free (title_copy);
673
674   return content;
675 }
676
677 /* Called just before PAGE is displayed as the current page of
678    ASSISTANT, this updates IA content according to the new
679    page. */
680 static void
681 on_prepare (GtkAssistant *assistant, GtkWidget *page,
682             struct import_assistant *ia)
683 {
684
685   if (gtk_assistant_get_page_type (assistant, page)
686       == GTK_ASSISTANT_PAGE_CONFIRM)
687     gtk_widget_grab_focus (assistant->apply);
688   else
689     gtk_widget_grab_focus (assistant->forward);
690
691   if (page == ia->separators.page)
692     prepare_separators_page (ia);
693   else if (page == ia->formats.page)
694     prepare_formats_page (ia);
695
696   gtk_widget_show (ia->asst.reset_button);
697   if (page == ia->formats.page)
698     gtk_widget_show (ia->asst.paste_button);
699   else
700     gtk_widget_hide (ia->asst.paste_button);
701 }
702
703 /* Called when the Cancel button in the assistant is clicked. */
704 static void
705 on_cancel (GtkAssistant *assistant, struct import_assistant *ia)
706 {
707   close_assistant (ia, GTK_RESPONSE_CANCEL);
708 }
709
710 /* Called when the Apply button on the last page of the assistant
711    is clicked. */
712 static void
713 on_close (GtkAssistant *assistant, struct import_assistant *ia)
714 {
715   close_assistant (ia, GTK_RESPONSE_APPLY);
716 }
717
718 /* Called when the Paste button on the last page of the assistant
719    is clicked. */
720 static void
721 on_paste (GtkButton *button, struct import_assistant *ia)
722 {
723   close_assistant (ia, PSPPIRE_RESPONSE_PASTE);
724 }
725
726 /* Called when the Reset button is clicked. */
727 static void
728 on_reset (GtkButton *button, struct import_assistant *ia)
729 {
730   gint page_num = gtk_assistant_get_current_page (ia->asst.assistant);
731   GtkWidget *page = gtk_assistant_get_nth_page (ia->asst.assistant, page_num);
732
733   if (page == ia->intro.page)
734     reset_intro_page (ia);
735   else if (page == ia->first_line.page)
736     reset_first_line_page (ia);
737   else if (page == ia->separators.page)
738     reset_separators_page (ia);
739   else if (page == ia->formats.page)
740     reset_formats_page (ia);
741 }
742
743 /* Causes the assistant to close, returning RESPONSE for
744    interpretation by text_data_import_assistant. */
745 static void
746 close_assistant (struct import_assistant *ia, int response)
747 {
748   ia->asst.response = response;
749   g_main_loop_quit (ia->asst.main_loop);
750   gtk_widget_hide (GTK_WIDGET (ia->asst.assistant));
751 }
752 \f
753 /* The "intro" page of the assistant. */
754
755 static void on_intro_amount_changed (struct import_assistant *);
756
757 /* Initializes IA's intro substructure. */
758 static void
759 init_intro_page (struct import_assistant *ia)
760 {
761   GtkBuilder *builder = ia->asst.builder;
762   struct intro_page *p = &ia->intro;
763   struct string s;
764   GtkWidget *hbox_n_cases ;
765   GtkWidget *hbox_percent ;
766   GtkWidget *table ;
767
768
769   p->n_cases_spin = gtk_spin_button_new_with_range (0, INT_MAX, 100);
770
771   hbox_n_cases = psppire_scanf_new (_("Only the first %4d cases"), &p->n_cases_spin);
772
773   table  = get_widget_assert (builder, "button-table");
774
775   gtk_table_attach_defaults (GTK_TABLE (table), hbox_n_cases,
776                     1, 2,
777                     1, 2);
778
779   p->percent_spin = gtk_spin_button_new_with_range (0, 100, 10);
780
781   hbox_percent = psppire_scanf_new (_("Only the first %3d %% of file (approximately)"), &p->percent_spin);
782
783   gtk_table_attach_defaults (GTK_TABLE (table), hbox_percent,
784                              1, 2,
785                              2, 3);
786
787   p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Intro"),
788                                    GTK_ASSISTANT_PAGE_INTRO);
789
790   p->all_cases_button = get_widget_assert (builder, "import-all-cases");
791
792   p->n_cases_button = get_widget_assert (builder, "import-n-cases");
793
794   p->percent_button = get_widget_assert (builder, "import-percent");
795
796   g_signal_connect_swapped (p->all_cases_button, "toggled",
797                     G_CALLBACK (on_intro_amount_changed), ia);
798   g_signal_connect_swapped (p->n_cases_button, "toggled",
799                     G_CALLBACK (on_intro_amount_changed), ia);
800   g_signal_connect_swapped (p->percent_button, "toggled",
801                     G_CALLBACK (on_intro_amount_changed), ia);
802
803   on_intro_amount_changed (ia);
804
805   ds_init_empty (&s);
806   ds_put_cstr (&s, _("This assistant will guide you through the process of "
807                      "importing data into PSPP from a text file with one line "
808                      "per case,  in which fields are separated by tabs, "
809                      "commas, or other delimiters.\n\n"));
810   if (ia->file.total_is_exact)
811     ds_put_format (
812       &s, ngettext ("The selected file contains %zu line of text.  ",
813                     "The selected file contains %zu lines of text.  ",
814                     ia->file.line_cnt),
815       ia->file.line_cnt);
816   else if (ia->file.total_lines > 0)
817     {
818       ds_put_format (
819         &s, ngettext (
820           "The selected file contains approximately %lu line of text.  ",
821           "The selected file contains approximately %lu lines of text.  ",
822           ia->file.total_lines),
823         ia->file.total_lines);
824       ds_put_format (
825         &s, ngettext (
826           "Only the first %zu line of the file will be shown for "
827           "preview purposes in the following screens.  ",
828           "Only the first %zu lines of the file will be shown for "
829           "preview purposes in the following screens.  ",
830           ia->file.line_cnt),
831         ia->file.line_cnt);
832     }
833   ds_put_cstr (&s, _("You may choose below how much of the file should "
834                      "actually be imported."));
835   gtk_label_set_text (GTK_LABEL (get_widget_assert (builder, "intro-label")),
836                       ds_cstr (&s));
837   ds_destroy (&s);
838 }
839
840 /* Resets IA's intro page to its initial state. */
841 static void
842 reset_intro_page (struct import_assistant *ia)
843 {
844   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->intro.all_cases_button),
845                                 true);
846 }
847
848 /* Called when one of the radio buttons is clicked. */
849 static void
850 on_intro_amount_changed (struct import_assistant *ia)
851 {
852   struct intro_page *p = &ia->intro;
853
854   gtk_widget_set_sensitive (p->n_cases_spin,
855                             gtk_toggle_button_get_active (
856                               GTK_TOGGLE_BUTTON (p->n_cases_button)));
857
858   gtk_widget_set_sensitive (p->percent_spin,
859                             gtk_toggle_button_get_active (
860                               GTK_TOGGLE_BUTTON (p->percent_button)));
861 }
862 \f
863 /* The "first line" page of the assistant. */
864
865 static PsppSheetView *create_lines_tree_view (GtkContainer *parent_window,
866                                             struct import_assistant *);
867 static void on_first_line_change (PsppSheetSelection *,
868                                   struct import_assistant *);
869 static void on_variable_names_cb_toggle (GtkToggleButton *,
870                                          struct import_assistant *);
871 static void set_first_line (struct import_assistant *);
872 static void get_first_line (struct import_assistant *);
873
874 /* Initializes IA's first_line substructure. */
875 static void
876 init_first_line_page (struct import_assistant *ia)
877 {
878   struct first_line_page *p = &ia->first_line;
879   GtkBuilder *builder = ia->asst.builder;
880
881   p->page = add_page_to_assistant (ia, get_widget_assert (builder, "FirstLine"),
882                                    GTK_ASSISTANT_PAGE_CONTENT);
883   gtk_widget_destroy (get_widget_assert (builder, "first-line"));
884   p->tree_view = create_lines_tree_view (
885     GTK_CONTAINER (get_widget_assert (builder, "first-line-scroller")), ia);
886   p->variable_names_cb = get_widget_assert (builder, "variable-names");
887   pspp_sheet_selection_set_mode (
888     pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (p->tree_view)),
889     PSPP_SHEET_SELECTION_BROWSE);
890   pspp_sheet_view_set_rubber_banding (PSPP_SHEET_VIEW (p->tree_view), TRUE);
891   set_first_line (ia);
892   g_signal_connect (pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (p->tree_view)),
893                     "changed", G_CALLBACK (on_first_line_change), ia);
894   g_signal_connect (p->variable_names_cb, "toggled",
895                     G_CALLBACK (on_variable_names_cb_toggle), ia);
896 }
897
898 /* Resets the first_line page to its initial content. */
899 static void
900 reset_first_line_page (struct import_assistant *ia)
901 {
902   ia->first_line.skip_lines = 0;
903   ia->first_line.variable_names = false;
904   set_first_line (ia);
905 }
906
907 static void
908 render_line (PsppSheetViewColumn *tree_column,
909              GtkCellRenderer *cell,
910              GtkTreeModel *tree_model,
911              GtkTreeIter *iter,
912              gpointer data)
913 {
914   gint row = empty_list_store_iter_to_row (iter);
915   struct string *lines;
916
917   lines = g_object_get_data (G_OBJECT (tree_model), "lines");
918   g_return_if_fail (lines != NULL);
919
920   g_object_set (cell, "text", ds_cstr (&lines[row]), NULL);
921 }
922
923
924 /* Creates and returns a tree view that contains each of the
925    lines in IA's file as a row. */
926 static PsppSheetView *
927 create_lines_tree_view (GtkContainer *parent, struct import_assistant *ia)
928 {
929   PsppSheetView *tree_view;
930   PsppSheetViewColumn *column;
931   size_t max_line_length;
932   gint content_width, header_width;
933   size_t i;
934   const gchar *title = _("Text");
935
936   make_tree_view (ia, 0, &tree_view);
937
938   column = pspp_sheet_view_column_new_with_attributes (
939     title, ia->asst.fixed_renderer, (void *) NULL);
940   pspp_sheet_view_column_set_cell_data_func (column, ia->asst.fixed_renderer,
941                                            render_line, NULL, NULL);
942   pspp_sheet_view_column_set_resizable (column, TRUE);
943
944   max_line_length = 0;
945   for (i = 0; i < ia->file.line_cnt; i++)
946     {
947       size_t w = ds_length (&ia->file.lines[i]);
948       max_line_length = MAX (max_line_length, w);
949     }
950
951   content_width = get_monospace_width (tree_view, ia->asst.fixed_renderer,
952                                        max_line_length);
953   header_width = get_string_width (tree_view, ia->asst.prop_renderer, title);
954   pspp_sheet_view_column_set_fixed_width (column, MAX (content_width,
955                                                      header_width));
956   pspp_sheet_view_append_column (tree_view, column);
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 (PsppSheetSelection *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   pspp_sheet_view_set_cursor (PSPP_SHEET_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   PsppSheetSelection *selection;
1005   GtkTreeIter iter;
1006   GtkTreeModel *model;
1007
1008   selection = pspp_sheet_view_get_selection (ia->first_line.tree_view);
1009   if (pspp_sheet_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 (PsppSheetViewColumn *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 = PSPP_SHEET_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 (PsppSheetViewColumn *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 = PSPP_SHEET_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       /* This destroys p->dict also. */
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   GtkBin *vars_scroller;
1643   GtkWidget *old_var_sheet;
1644   PsppireVarSheet *var_sheet;
1645   struct separators_page *s = &ia->separators;
1646   struct formats_page *p = &ia->formats;
1647   struct fmt_guesser *fg;
1648   unsigned long int number = 0;
1649   size_t column_idx;
1650
1651   push_watch_cursor (ia);
1652
1653   dict = dict_create (get_default_encoding ());
1654   fg = fmt_guesser_create ();
1655   for (column_idx = 0; column_idx < s->column_cnt; column_idx++)
1656     {
1657       struct variable *modified_var;
1658
1659       modified_var = (column_idx < p->modified_var_cnt
1660                       ? p->modified_vars[column_idx] : NULL);
1661       if (modified_var == NULL)
1662         {
1663           struct column *column = &s->columns[column_idx];
1664           struct variable *var;
1665           struct fmt_spec format;
1666           char *name;
1667           size_t row;
1668
1669           /* Choose variable name. */
1670           name = dict_make_unique_var_name (dict, column->name, &number);
1671
1672           /* Choose variable format. */
1673           fmt_guesser_clear (fg);
1674           for (row = ia->first_line.skip_lines; row < ia->file.line_cnt; row++)
1675             fmt_guesser_add (fg, column->contents[row]);
1676           fmt_guesser_guess (fg, &format);
1677           fmt_fix_input (&format);
1678
1679           /* Create variable. */
1680           var = dict_create_var_assert (dict, name, fmt_var_width (&format));
1681           var_set_both_formats (var, &format);
1682
1683           free (name);
1684         }
1685       else
1686         {
1687           char *name;
1688
1689           name = dict_make_unique_var_name (dict, var_get_name (modified_var),
1690                                             &number);
1691           dict_clone_var_as_assert (dict, modified_var, name);
1692           free (name);
1693         }
1694     }
1695   fmt_guesser_destroy (fg);
1696
1697   psppire_dict = psppire_dict_new_from_dict (dict);
1698   g_signal_connect (psppire_dict, "variable_changed",
1699                     G_CALLBACK (on_variable_change), ia);
1700   ia->formats.dict = dict;
1701   ia->formats.psppire_dict = psppire_dict;
1702
1703   /* XXX: PsppireVarStore doesn't hold a reference to
1704      psppire_dict for now, but it should.  After it does, we
1705      should g_object_ref the psppire_dict here, since we also
1706      hold a reference via ia->formats.dict. */
1707   var_sheet = PSPPIRE_VAR_SHEET (psppire_var_sheet_new ());
1708   g_object_set (var_sheet,
1709                 "dictionary", psppire_dict,
1710                 "may-create-vars", FALSE,
1711                 "may-delete-vars", FALSE,
1712                 "format-use", FMT_FOR_INPUT,
1713                 "enable-grid-lines", PSPP_SHEET_VIEW_GRID_LINES_BOTH,
1714                 (void *) NULL);
1715
1716   vars_scroller = GTK_BIN (get_widget_assert (ia->asst.builder, "vars-scroller"));
1717   old_var_sheet = gtk_bin_get_child (vars_scroller);
1718   if (old_var_sheet != NULL)
1719     gtk_widget_destroy (old_var_sheet);
1720   gtk_container_add (GTK_CONTAINER (vars_scroller), GTK_WIDGET (var_sheet));
1721   gtk_widget_show (GTK_WIDGET (var_sheet));
1722
1723   gtk_widget_destroy (GTK_WIDGET (ia->formats.data_tree_view));
1724   ia->formats.data_tree_view = create_data_tree_view (
1725     false,
1726     GTK_CONTAINER (get_widget_assert (ia->asst.builder, "data-scroller")),
1727     ia);
1728
1729   pop_watch_cursor (ia);
1730 }
1731
1732 /* Clears the set of user-modified variables from IA's formats
1733    substructure.  This discards user modifications to variable
1734    formats, thereby causing formats to revert to their
1735    defaults.  */
1736 static void
1737 clear_modified_vars (struct import_assistant *ia)
1738 {
1739   struct formats_page *p = &ia->formats;
1740   size_t i;
1741
1742   for (i = 0; i < p->modified_var_cnt; i++)
1743     var_destroy (p->modified_vars[i]);
1744   free (p->modified_vars);
1745   p->modified_vars = NULL;
1746   p->modified_var_cnt = 0;
1747 }
1748
1749 /* Resets the formats page to its defaults, discarding user
1750    modifications. */
1751 static void
1752 reset_formats_page (struct import_assistant *ia)
1753 {
1754   clear_modified_vars (ia);
1755   prepare_formats_page (ia);
1756 }
1757
1758 /* Called when the user changes one of the variables in the
1759    dictionary. */
1760 static void
1761 on_variable_change (PsppireDict *dict, int dict_idx,
1762                     struct import_assistant *ia)
1763 {
1764   struct formats_page *p = &ia->formats;
1765   PsppSheetView *tv = ia->formats.data_tree_view;
1766   gint column_idx = dict_idx + 1;
1767
1768   push_watch_cursor (ia);
1769
1770   /* Remove previous column and replace with new column. */
1771   pspp_sheet_view_remove_column (tv, pspp_sheet_view_get_column (tv, column_idx));
1772   pspp_sheet_view_insert_column (tv, make_data_column (ia, tv, false, dict_idx),
1773                                column_idx);
1774
1775   /* Save a copy of the modified variable in modified_vars, so
1776      that its attributes will be preserved if we back up to the
1777      previous page with the Prev button and then come back
1778      here. */
1779   if (dict_idx >= p->modified_var_cnt)
1780     {
1781       size_t i;
1782       p->modified_vars = xnrealloc (p->modified_vars, dict_idx + 1,
1783                                     sizeof *p->modified_vars);
1784       for (i = 0; i <= dict_idx; i++)
1785         p->modified_vars[i] = NULL;
1786       p->modified_var_cnt = dict_idx + 1;
1787     }
1788   if (p->modified_vars[dict_idx])
1789     var_destroy (p->modified_vars[dict_idx]);
1790   p->modified_vars[dict_idx]
1791     = var_clone (psppire_dict_get_variable (dict, dict_idx));
1792
1793   pop_watch_cursor (ia);
1794 }
1795
1796 /* Parses the contents of the field at (ROW,COLUMN) according to
1797    its variable format.  If OUTPUTP is non-null, then *OUTPUTP
1798    receives the formatted output for that field (which must be
1799    freed with free).  If TOOLTIPP is non-null, then *TOOLTIPP
1800    receives a message suitable for use in a tooltip, if one is
1801    needed, or a null pointer otherwise.  Returns true if a
1802    tooltip message is needed, otherwise false. */
1803 static bool
1804 parse_field (struct import_assistant *ia,
1805              size_t row, size_t column,
1806              char **outputp, char **tooltipp)
1807 {
1808   struct substring field;
1809   union value val;
1810   struct variable *var;
1811   const struct fmt_spec *in;
1812   struct fmt_spec out;
1813   char *tooltip;
1814   bool ok;
1815
1816   field = ia->separators.columns[column].contents[row];
1817   var = dict_get_var (ia->formats.dict, column);
1818   value_init (&val, var_get_width (var));
1819   in = var_get_print_format (var);
1820   out = fmt_for_output_from_input (in);
1821   tooltip = NULL;
1822   if (field.string != NULL)
1823     {
1824       char *error;
1825
1826       error = data_in (field, "UTF-8", in->type, &val, var_get_width (var),
1827                        dict_get_encoding (ia->formats.dict));
1828       if (error != NULL)
1829         {
1830           tooltip = xasprintf (_("Cannot parse field content `%.*s' as "
1831                                  "format %s: %s"),
1832                                (int) field.length, field.string,
1833                                fmt_name (in->type), error);
1834           free (error);
1835         }
1836     }
1837   else
1838     {
1839       tooltip = xstrdup (_("This input line has too few separators "
1840                            "to fill in this field."));
1841       value_set_missing (&val, var_get_width (var));
1842     }
1843   if (outputp != NULL)
1844     {
1845       *outputp = data_out (&val, dict_get_encoding (ia->formats.dict),  &out);
1846     }
1847   value_destroy (&val, var_get_width (var));
1848
1849   ok = tooltip == NULL;
1850   if (tooltipp != NULL)
1851     *tooltipp = tooltip;
1852   else
1853     free (tooltip);
1854   return ok;
1855 }
1856
1857 /* Called to render one of the cells in the data preview tree
1858    view. */
1859 static void
1860 render_output_cell (PsppSheetViewColumn *tree_column,
1861                     GtkCellRenderer *cell,
1862                     GtkTreeModel *model,
1863                     GtkTreeIter *iter,
1864                     gpointer ia_)
1865 {
1866   struct import_assistant *ia = ia_;
1867   char *output;
1868   GValue gvalue = { 0, };
1869   bool ok;
1870
1871   ok = parse_field (ia,
1872                     (empty_list_store_iter_to_row (iter)
1873                      + ia->first_line.skip_lines),
1874                     GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1875                                                         "column-number")),
1876                     &output, NULL);
1877
1878   g_value_init (&gvalue, G_TYPE_STRING);
1879   g_value_take_string (&gvalue, output);
1880   g_object_set_property (G_OBJECT (cell), "text", &gvalue);
1881   g_value_unset (&gvalue);
1882
1883   if (ok)
1884     g_object_set (cell, "background-set", FALSE, (void *) NULL);
1885   else
1886     g_object_set (cell,
1887                   "background", "red",
1888                   "background-set", TRUE,
1889                   (void *) NULL);
1890 }
1891
1892 /* Called to render a tooltip for one of the cells in the data
1893    preview tree view. */
1894 static gboolean
1895 on_query_output_tooltip (GtkWidget *widget, gint wx, gint wy,
1896                         gboolean keyboard_mode UNUSED,
1897                         GtkTooltip *tooltip, struct import_assistant *ia)
1898 {
1899   size_t row, column;
1900   char *text;
1901
1902   if (!get_tooltip_location (widget, wx, wy, ia, &row, &column))
1903     return FALSE;
1904
1905   if (parse_field (ia, row, column, NULL, &text))
1906     return FALSE;
1907
1908   gtk_tooltip_set_text (tooltip, text);
1909   free (text);
1910   return TRUE;
1911 }
1912 \f
1913 /* Utility functions used by multiple pages of the assistant. */
1914
1915 static gboolean
1916 get_tooltip_location (GtkWidget *widget, gint wx, gint wy,
1917                       const struct import_assistant *ia,
1918                       size_t *row, size_t *column)
1919 {
1920   PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1921   gint bx, by;
1922   GtkTreePath *path;
1923   GtkTreeIter iter;
1924   PsppSheetViewColumn *tree_column;
1925   GtkTreeModel *tree_model;
1926   bool ok;
1927
1928   /* Check that WIDGET is really visible on the screen before we
1929      do anything else.  This is a bug fix for a sticky situation:
1930      when text_data_import_assistant() returns, it frees the data
1931      necessary to compose the tool tip message, but there may be
1932      a tool tip under preparation at that point (even if there is
1933      no visible tool tip) that will call back into us a little
1934      bit later.  Perhaps the correct solution to this problem is
1935      to make the data related to the tool tips part of a GObject
1936      that only gets destroyed when all references are released,
1937      but this solution appears to be effective too. */
1938   if (!gtk_widget_get_mapped (widget))
1939     return FALSE;
1940
1941   pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view,
1942                                                      wx, wy, &bx, &by);
1943   if (!pspp_sheet_view_get_path_at_pos (tree_view, bx, by,
1944                                       &path, &tree_column, NULL, NULL))
1945     return FALSE;
1946
1947   *column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1948                                                 "column-number"));
1949
1950   tree_model = pspp_sheet_view_get_model (tree_view);
1951   ok = gtk_tree_model_get_iter (tree_model, &iter, path);
1952   gtk_tree_path_free (path);
1953   if (!ok)
1954     return FALSE;
1955
1956   *row = empty_list_store_iter_to_row (&iter) + ia->first_line.skip_lines;
1957   return TRUE;
1958 }
1959
1960 static void
1961 make_tree_view (const struct import_assistant *ia,
1962                 size_t first_line,
1963                 PsppSheetView **tree_view)
1964 {
1965   GtkTreeModel *model;
1966
1967   *tree_view = PSPP_SHEET_VIEW (pspp_sheet_view_new ());
1968   pspp_sheet_view_set_grid_lines (*tree_view, PSPP_SHEET_VIEW_GRID_LINES_BOTH);
1969   model = GTK_TREE_MODEL (psppire_empty_list_store_new (
1970                             ia->file.line_cnt - first_line));
1971   g_object_set_data (G_OBJECT (model), "lines", ia->file.lines + first_line);
1972   g_object_set_data (G_OBJECT (model), "first-line",
1973                      GINT_TO_POINTER (first_line));
1974   pspp_sheet_view_set_model (*tree_view, model);
1975   g_object_unref (model);
1976
1977   add_line_number_column (ia, *tree_view);
1978 }
1979
1980 static void
1981 render_line_number (PsppSheetViewColumn *tree_column,
1982                     GtkCellRenderer *cell,
1983                     GtkTreeModel *tree_model,
1984                     GtkTreeIter *iter,
1985                     gpointer data)
1986 {
1987   gint row = empty_list_store_iter_to_row (iter);
1988   char s[INT_BUFSIZE_BOUND (int)];
1989   int first_line;
1990
1991   first_line = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_model),
1992                                                    "first-line"));
1993   sprintf (s, "%d", first_line + row);
1994   g_object_set (cell, "text", s, NULL);
1995 }
1996
1997 static void
1998 add_line_number_column (const struct import_assistant *ia,
1999                         PsppSheetView *treeview)
2000 {
2001   PsppSheetViewColumn *column;
2002
2003   column = pspp_sheet_view_column_new_with_attributes (
2004     _("Line"), ia->asst.prop_renderer, (void *) NULL);
2005   pspp_sheet_view_column_set_fixed_width (
2006     column, get_monospace_width (treeview, ia->asst.prop_renderer, 5));
2007   pspp_sheet_view_column_set_resizable (column, TRUE);
2008   pspp_sheet_view_column_set_cell_data_func (column, ia->asst.prop_renderer,
2009                                              render_line_number, NULL, NULL);
2010   pspp_sheet_view_append_column (treeview, column);
2011 }
2012
2013 static gint
2014 get_monospace_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
2015                      size_t char_cnt)
2016 {
2017   struct string s;
2018   gint width;
2019
2020   ds_init_empty (&s);
2021   ds_put_byte_multiple (&s, '0', char_cnt);
2022   ds_put_byte (&s, ' ');
2023   width = get_string_width (treeview, renderer, ds_cstr (&s));
2024   ds_destroy (&s);
2025
2026   return width;
2027 }
2028
2029 static gint
2030 get_string_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
2031                   const char *string)
2032 {
2033   gint width;
2034   g_object_set (G_OBJECT (renderer), "text", string, (void *) NULL);
2035   gtk_cell_renderer_get_size (renderer, GTK_WIDGET (treeview),
2036                               NULL, NULL, NULL, &width, NULL);
2037   return width;
2038 }
2039
2040 static PsppSheetViewColumn *
2041 make_data_column (struct import_assistant *ia, PsppSheetView *tree_view,
2042                   bool input, gint dict_idx)
2043 {
2044   struct variable *var = NULL;
2045   struct column *column = NULL;
2046   size_t char_cnt;
2047   gint content_width, header_width;
2048   PsppSheetViewColumn *tree_column;
2049   char *name;
2050
2051   if (input)
2052     column = &ia->separators.columns[dict_idx];
2053   else
2054     var = dict_get_var (ia->formats.dict, dict_idx);
2055
2056   name = escape_underscores (input ? column->name : var_get_name (var));
2057   char_cnt = input ? column->width : var_get_print_format (var)->w;
2058   content_width = get_monospace_width (tree_view, ia->asst.fixed_renderer,
2059                                        char_cnt);
2060   header_width = get_string_width (tree_view, ia->asst.prop_renderer,
2061                                    name);
2062
2063   tree_column = pspp_sheet_view_column_new ();
2064   g_object_set_data (G_OBJECT (tree_column), "column-number",
2065                      GINT_TO_POINTER (dict_idx));
2066   pspp_sheet_view_column_set_title (tree_column, name);
2067   pspp_sheet_view_column_pack_start (tree_column, ia->asst.fixed_renderer,
2068                                    FALSE);
2069   pspp_sheet_view_column_set_cell_data_func (
2070     tree_column, ia->asst.fixed_renderer,
2071     input ? render_input_cell : render_output_cell, ia, NULL);
2072   pspp_sheet_view_column_set_fixed_width (tree_column, MAX (content_width,
2073                                                           header_width));
2074   pspp_sheet_view_column_set_resizable (tree_column, TRUE);
2075
2076   free (name);
2077
2078   return tree_column;
2079 }
2080
2081 static PsppSheetView *
2082 create_data_tree_view (bool input, GtkContainer *parent,
2083                        struct import_assistant *ia)
2084 {
2085   PsppSheetView *tree_view;
2086   gint i;
2087
2088   make_tree_view (ia, ia->first_line.skip_lines, &tree_view);
2089   pspp_sheet_selection_set_mode (pspp_sheet_view_get_selection (tree_view),
2090                                PSPP_SHEET_SELECTION_NONE);
2091
2092   for (i = 0; i < ia->separators.column_cnt; i++)
2093     pspp_sheet_view_append_column (tree_view,
2094                                  make_data_column (ia, tree_view, input, i));
2095
2096   g_object_set (G_OBJECT (tree_view), "has-tooltip", TRUE, (void *) NULL);
2097   g_signal_connect (tree_view, "query-tooltip",
2098                     G_CALLBACK (input ? on_query_input_tooltip
2099                                 : on_query_output_tooltip), ia);
2100
2101   gtk_container_add (parent, GTK_WIDGET (tree_view));
2102   gtk_widget_show (GTK_WIDGET (tree_view));
2103
2104   return tree_view;
2105 }
2106
2107 /* Increments the "watch cursor" level, setting the cursor for
2108    the assistant window to a watch face to indicate to the user
2109    that the ongoing operation may take some time. */
2110 static void
2111 push_watch_cursor (struct import_assistant *ia)
2112 {
2113   if (++ia->asst.watch_cursor == 1)
2114     {
2115       GtkWidget *widget = GTK_WIDGET (ia->asst.assistant);
2116       GdkDisplay *display = gtk_widget_get_display (widget);
2117       GdkCursor *cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
2118       gdk_window_set_cursor (widget->window, cursor);
2119       gdk_cursor_unref (cursor);
2120       gdk_display_flush (display);
2121     }
2122 }
2123
2124 /* Decrements the "watch cursor" level.  If the level reaches
2125    zero, the cursor is reset to its default shape. */
2126 static void
2127 pop_watch_cursor (struct import_assistant *ia)
2128 {
2129   if (--ia->asst.watch_cursor == 0)
2130     {
2131       GtkWidget *widget = GTK_WIDGET (ia->asst.assistant);
2132       gdk_window_set_cursor (widget->window, NULL);
2133     }
2134 }