text-data-import-dialog: Convert input lines to UTF-8 upon read.
[pspp] / src / ui / gui / text-data-import-dialog.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2008, 2009, 2010, 2011, 2012  Free Software Foundation
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18
19 #include "ui/gui/text-data-import-dialog.h"
20
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <gtk-contrib/psppire-sheet.h>
24 #include <gtk/gtk.h>
25 #include <limits.h>
26 #include <stdlib.h>
27 #include <sys/stat.h>
28
29 #include "data/data-in.h"
30 #include "data/data-out.h"
31 #include "data/format-guesser.h"
32 #include "data/value-labels.h"
33 #include "language/data-io/data-parser.h"
34 #include "language/lexer/lexer.h"
35 #include "libpspp/assertion.h"
36 #include "libpspp/i18n.h"
37 #include "libpspp/line-reader.h"
38 #include "libpspp/message.h"
39 #include "ui/gui/checkbox-treeview.h"
40 #include "ui/gui/dialog-common.h"
41 #include "ui/gui/executor.h"
42 #include "ui/gui/helper.h"
43 #include "ui/gui/builder-wrapper.h"
44 #include "ui/gui/psppire-data-window.h"
45 #include "ui/gui/psppire-dialog.h"
46 #include "ui/gui/psppire-encoding-selector.h"
47 #include "ui/gui/psppire-empty-list-store.h"
48 #include "ui/gui/psppire-var-sheet.h"
49 #include "ui/gui/psppire-var-store.h"
50 #include "ui/gui/psppire-scanf.h"
51 #include "ui/syntax-gen.h"
52
53 #include "gl/error.h"
54 #include "gl/intprops.h"
55 #include "gl/xalloc.h"
56
57 #include "gettext.h"
58 #define _(msgid) gettext (msgid)
59 #define N_(msgid) msgid
60
61 struct import_assistant;
62
63 /* The file to be imported. */
64 struct file
65   {
66     char *file_name;        /* File name. */
67     gchar *encoding;        /* Encoding. */
68     unsigned long int total_lines; /* Number of lines in file. */
69     bool total_is_exact;    /* Is total_lines exact (or an estimate)? */
70
71     /* The first several lines of the file. */
72     struct string *lines;
73     size_t line_cnt;
74   };
75 static bool init_file (struct import_assistant *, GtkWindow *parent);
76 static void destroy_file (struct import_assistant *);
77
78 /* The main body of the GTK+ assistant and related data. */
79 struct assistant
80   {
81     GtkBuilder *builder;
82     GtkAssistant *assistant;
83     GMainLoop *main_loop;
84     GtkWidget *paste_button;
85     GtkWidget *reset_button;
86     int response;
87     int watch_cursor;
88
89     GtkCellRenderer *prop_renderer;
90     GtkCellRenderer *fixed_renderer;
91   };
92 static void init_assistant (struct import_assistant *, GtkWindow *);
93 static void destroy_assistant (struct import_assistant *);
94 static GtkWidget *add_page_to_assistant (struct import_assistant *,
95                                          GtkWidget *page,
96                                          GtkAssistantPageType);
97
98 /* The introduction page of the assistant. */
99 struct intro_page
100   {
101     GtkWidget *page;
102     GtkWidget *all_cases_button;
103     GtkWidget *n_cases_button;
104     GtkWidget *n_cases_spin;
105     GtkWidget *percent_button;
106     GtkWidget *percent_spin;
107   };
108 static void init_intro_page (struct import_assistant *);
109 static void reset_intro_page (struct import_assistant *);
110
111 /* Page where the user chooses the first line of data. */
112 struct first_line_page
113   {
114     int skip_lines;    /* Number of initial lines to skip? */
115     bool variable_names; /* Variable names above first line of data? */
116
117     GtkWidget *page;
118     GtkTreeView *tree_view;
119     GtkWidget *variable_names_cb;
120   };
121 static void init_first_line_page (struct import_assistant *);
122 static void reset_first_line_page (struct import_assistant *);
123
124 /* Page where the user chooses field separators. */
125 struct separators_page
126   {
127     /* How to break lines into columns. */
128     struct string separators;   /* Field separators. */
129     struct string quotes;       /* Quote characters. */
130     bool escape;                /* Doubled quotes yield a quote mark? */
131
132     /* The columns produced thereby. */
133     struct column *columns;     /* Information about each column. */
134     size_t column_cnt;          /* Number of columns. */
135
136     GtkWidget *page;
137     GtkWidget *custom_cb;
138     GtkWidget *custom_entry;
139     GtkWidget *quote_cb;
140     GtkWidget *quote_combo;
141     GtkEntry *quote_entry;
142     GtkWidget *escape_cb;
143     GtkTreeView *fields_tree_view;
144   };
145 /* The columns that the separators divide the data into. */
146 struct column
147   {
148     /* Variable name for this column.  This is the variable name
149        used on the separators page; it can be overridden by the
150        user on the formats page. */
151     char *name;
152
153     /* Maximum length of any row in this column. */
154     size_t width;
155
156     /* Contents of this column: contents[row] is the contents for
157        the given row.
158
159        A null substring indicates a missing column for that row
160        (because the line contains an insufficient number of
161        separators).
162
163        contents[] elements may be substrings of the lines[]
164        strings that represent the whole lines of the file, to
165        save memory.  Other elements are dynamically allocated
166        with ss_alloc_substring. */
167     struct substring *contents;
168   };
169 static void init_separators_page (struct import_assistant *);
170 static void destroy_separators_page (struct import_assistant *);
171 static void prepare_separators_page (struct import_assistant *);
172 static void reset_separators_page (struct import_assistant *);
173
174 /* Page where the user verifies and adjusts input formats. */
175 struct formats_page
176   {
177     struct dictionary *dict;
178
179     GtkWidget *page;
180     GtkTreeView *data_tree_view;
181     PsppireDict *psppire_dict;
182     struct variable **modified_vars;
183     size_t modified_var_cnt;
184   };
185 static void init_formats_page (struct import_assistant *);
186 static void destroy_formats_page (struct import_assistant *);
187 static void prepare_formats_page (struct import_assistant *);
188 static void reset_formats_page (struct import_assistant *);
189
190 struct import_assistant
191   {
192     struct file file;
193     struct assistant asst;
194     struct intro_page intro;
195     struct first_line_page first_line;
196     struct separators_page separators;
197     struct formats_page formats;
198   };
199
200 static void apply_dict (const struct dictionary *, struct string *);
201 static char *generate_syntax (const struct import_assistant *);
202
203 static gboolean get_tooltip_location (GtkWidget *widget, gint wx, gint wy,
204                                       const struct import_assistant *,
205                                       size_t *row, size_t *column);
206 static void make_tree_view (const struct import_assistant *ia,
207                             size_t first_line,
208                             GtkTreeView **tree_view);
209 static void add_line_number_column (const struct import_assistant *,
210                                     GtkTreeView *);
211 static gint get_monospace_width (GtkTreeView *, GtkCellRenderer *,
212                                  size_t char_cnt);
213 static gint get_string_width (GtkTreeView *, GtkCellRenderer *,
214                               const char *string);
215 static GtkTreeViewColumn *make_data_column (struct import_assistant *,
216                                             GtkTreeView *, bool input,
217                                             gint column_idx);
218 static GtkTreeView *create_data_tree_view (bool input, GtkContainer *parent,
219                                            struct import_assistant *);
220 static void push_watch_cursor (struct import_assistant *);
221 static void pop_watch_cursor (struct import_assistant *);
222
223 /* Pops up the Text Data Import assistant. */
224 void
225 text_data_import_assistant (PsppireDataWindow *dw)
226 {
227   GtkWindow *parent_window = GTK_WINDOW (dw);
228   struct import_assistant *ia;
229
230   ia = xzalloc (sizeof *ia);
231   if (!init_file (ia, parent_window))
232     {
233       free (ia);
234       return;
235     }
236
237   init_assistant (ia, parent_window);
238   init_intro_page (ia);
239   init_first_line_page (ia);
240   init_separators_page (ia);
241   init_formats_page (ia);
242
243   gtk_widget_show_all (GTK_WIDGET (ia->asst.assistant));
244
245   ia->asst.main_loop = g_main_loop_new (NULL, false);
246   g_main_loop_run (ia->asst.main_loop);
247   g_main_loop_unref (ia->asst.main_loop);
248
249   switch (ia->asst.response)
250     {
251     case GTK_RESPONSE_APPLY:
252       free (execute_syntax_string (dw, generate_syntax (ia)));
253       break;
254     case PSPPIRE_RESPONSE_PASTE:
255       free (paste_syntax_to_window (generate_syntax (ia)));
256       break;
257     default:
258       break;
259     }
260
261   destroy_formats_page (ia);
262   destroy_separators_page (ia);
263   destroy_assistant (ia);
264   destroy_file (ia);
265   free (ia);
266 }
267
268 /* Emits PSPP syntax to S that applies the dictionary attributes
269    (such as missing values and value labels) of the variables in
270    DICT.  */
271 static void
272 apply_dict (const struct dictionary *dict, struct string *s)
273 {
274   size_t var_cnt = dict_get_var_cnt (dict);
275   size_t i;
276
277   for (i = 0; i < var_cnt; i++)
278     {
279       struct variable *var = dict_get_var (dict, i);
280       const char *name = var_get_name (var);
281       enum val_type type = var_get_type (var);
282       int width = var_get_width (var);
283       enum measure measure = var_get_measure (var);
284       enum alignment alignment = var_get_alignment (var);
285       const struct fmt_spec *format = var_get_print_format (var);
286
287       if (var_has_missing_values (var))
288         {
289           const struct missing_values *mv = var_get_missing_values (var);
290           size_t j;
291
292           syntax_gen_pspp (s, "MISSING VALUES %ss (", name);
293           for (j = 0; j < mv_n_values (mv); j++)
294             {
295               if (j)
296                 ds_put_cstr (s, ", ");
297               syntax_gen_value (s, mv_get_value (mv, j), width, format);
298             }
299
300           if (mv_has_range (mv))
301             {
302               double low, high;
303               if (mv_has_value (mv))
304                 ds_put_cstr (s, ", ");
305               mv_get_range (mv, &low, &high);
306               syntax_gen_num_range (s, low, high, format);
307             }
308           ds_put_cstr (s, ").\n");
309         }
310       if (var_has_value_labels (var))
311         {
312           const struct val_labs *vls = var_get_value_labels (var);
313           const struct val_lab **labels = val_labs_sorted (vls);
314           size_t n_labels = val_labs_count (vls);
315           size_t i;
316
317           syntax_gen_pspp (s, "VALUE LABELS %ss", name);
318           for (i = 0; i < n_labels; i++)
319             {
320               const struct val_lab *vl = labels[i];
321               ds_put_cstr (s, "\n  ");
322               syntax_gen_value (s, &vl->value, width, format);
323               ds_put_byte (s, ' ');
324               syntax_gen_string (s, ss_cstr (val_lab_get_escaped_label (vl)));
325             }
326           free (labels);
327           ds_put_cstr (s, ".\n");
328         }
329       if (var_has_label (var))
330         syntax_gen_pspp (s, "VARIABLE LABELS %ss %sq.\n",
331                          name, var_get_label (var));
332       if (measure != var_default_measure (type))
333         syntax_gen_pspp (s, "VARIABLE LEVEL %ss (%ss).\n",
334                          name,
335                          (measure == MEASURE_NOMINAL ? "NOMINAL"
336                           : measure == MEASURE_ORDINAL ? "ORDINAL"
337                           : "SCALE"));
338       if (alignment != var_default_alignment (type))
339         syntax_gen_pspp (s, "VARIABLE ALIGNMENT %ss (%ss).\n",
340                          name,
341                          (alignment == ALIGN_LEFT ? "LEFT"
342                           : alignment == ALIGN_CENTRE ? "CENTER"
343                           : "RIGHT"));
344       if (var_get_display_width (var) != var_default_display_width (width))
345         syntax_gen_pspp (s, "VARIABLE WIDTH %ss (%d).\n",
346                          name, var_get_display_width (var));
347     }
348 }
349
350 /* Generates and returns PSPP syntax to execute the import
351    operation described by IA.  The caller must free the syntax
352    with free(). */
353 static char *
354 generate_syntax (const struct import_assistant *ia)
355 {
356   struct string s = DS_EMPTY_INITIALIZER;
357   size_t var_cnt;
358   size_t i;
359
360   syntax_gen_pspp (&s,
361                    "GET DATA\n"
362                    "  /TYPE=TXT\n"
363                    "  /FILE=%sq\n",
364                    ia->file.file_name);
365   if (ia->file.encoding && strcmp (ia->file.encoding, "Auto"))
366     syntax_gen_pspp (&s, "  /ENCODING=%sq\n", ia->file.encoding);
367   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (
368                                       ia->intro.n_cases_button)))
369     ds_put_format (&s, "  /IMPORTCASES=FIRST %d\n",
370                    gtk_spin_button_get_value_as_int (
371                      GTK_SPIN_BUTTON (ia->intro.n_cases_spin)));
372   else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (
373                                            ia->intro.percent_button)))
374     ds_put_format (&s, "  /IMPORTCASES=PERCENT %d\n",
375                    gtk_spin_button_get_value_as_int (
376                      GTK_SPIN_BUTTON (ia->intro.percent_spin)));
377   else
378     ds_put_cstr (&s, "  /IMPORTCASES=ALL\n");
379   ds_put_cstr (&s,
380                "  /ARRANGEMENT=DELIMITED\n"
381                "  /DELCASE=LINE\n");
382   if (ia->first_line.skip_lines > 0)
383     ds_put_format (&s, "  /FIRSTCASE=%d\n", ia->first_line.skip_lines + 1);
384   ds_put_cstr (&s, "  /DELIMITERS=\"");
385   if (ds_find_byte (&ia->separators.separators, '\t') != SIZE_MAX)
386     ds_put_cstr (&s, "\\t");
387   if (ds_find_byte (&ia->separators.separators, '\\') != SIZE_MAX)
388     ds_put_cstr (&s, "\\\\");
389   for (i = 0; i < ds_length (&ia->separators.separators); i++)
390     {
391       char c = ds_at (&ia->separators.separators, i);
392       if (c == '"')
393         ds_put_cstr (&s, "\"\"");
394       else if (c != '\t' && c != '\\')
395         ds_put_byte (&s, c);
396     }
397   ds_put_cstr (&s, "\"\n");
398   if (!ds_is_empty (&ia->separators.quotes))
399     syntax_gen_pspp (&s, "  /QUALIFIER=%sq\n", ds_cstr (&ia->separators.quotes));
400   if (!ds_is_empty (&ia->separators.quotes) && ia->separators.escape)
401     ds_put_cstr (&s, "  /ESCAPE\n");
402   ds_put_cstr (&s, "  /VARIABLES=\n");
403
404   var_cnt = dict_get_var_cnt (ia->formats.dict);
405   for (i = 0; i < var_cnt; i++)
406     {
407       struct variable *var = dict_get_var (ia->formats.dict, i);
408       char format_string[FMT_STRING_LEN_MAX + 1];
409       fmt_to_string (var_get_print_format (var), format_string);
410       ds_put_format (&s, "    %s %s%s\n",
411                      var_get_name (var), format_string,
412                      i == var_cnt - 1 ? "." : "");
413     }
414
415   apply_dict (ia->formats.dict, &s);
416
417   return ds_cstr (&s);
418 }
419 \f
420 /* Choosing a file and reading it. */
421
422 static char *choose_file (GtkWindow *parent_window, gchar **encodingp);
423
424 /* Obtains the file to import from the user and initializes IA's
425    file substructure.  PARENT_WINDOW must be the window to use
426    as the file chooser window's parent.
427
428    Returns true if successful, false if the file name could not
429    be obtained or the file could not be read. */
430 static bool
431 init_file (struct import_assistant *ia, GtkWindow *parent_window)
432 {
433   struct file *file = &ia->file;
434   enum { MAX_PREVIEW_LINES = 1000 }; /* Max number of lines to read. */
435   enum { MAX_LINE_LEN = 16384 }; /* Max length of an acceptable line. */
436   struct line_reader *reader;
437   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 GtkTreeView *create_lines_tree_view (GtkContainer *parent_window,
871                                             struct import_assistant *);
872 static void on_first_line_change (GtkTreeSelection *,
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   gtk_tree_selection_set_mode (
893     gtk_tree_view_get_selection (GTK_TREE_VIEW (p->tree_view)),
894     GTK_SELECTION_BROWSE);
895   set_first_line (ia);
896   g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (p->tree_view)),
897                     "changed", G_CALLBACK (on_first_line_change), ia);
898   g_signal_connect (p->variable_names_cb, "toggled",
899                     G_CALLBACK (on_variable_names_cb_toggle), ia);
900 }
901
902 /* Resets the first_line page to its initial content. */
903 static void
904 reset_first_line_page (struct import_assistant *ia)
905 {
906   ia->first_line.skip_lines = 0;
907   ia->first_line.variable_names = false;
908   set_first_line (ia);
909 }
910
911 static void
912 render_line (GtkTreeViewColumn *tree_column,
913              GtkCellRenderer *cell,
914              GtkTreeModel *tree_model,
915              GtkTreeIter *iter,
916              gpointer data)
917 {
918   gint row = empty_list_store_iter_to_row (iter);
919   struct string *lines;
920
921   lines = g_object_get_data (G_OBJECT (tree_model), "lines");
922   g_return_if_fail (lines != NULL);
923
924   g_object_set (cell, "text", ds_cstr (&lines[row]), NULL);
925 }
926
927
928 /* Creates and returns a tree view that contains each of the
929    lines in IA's file as a row. */
930 static GtkTreeView *
931 create_lines_tree_view (GtkContainer *parent, struct import_assistant *ia)
932 {
933   GtkTreeView *tree_view;
934   GtkTreeViewColumn *column;
935   size_t max_line_length;
936   gint content_width, header_width;
937   size_t i;
938   const gchar *title = _("Text");
939
940   make_tree_view (ia, 0, &tree_view);
941
942   column = gtk_tree_view_column_new_with_attributes (
943      title, ia->asst.fixed_renderer, (void *) NULL);
944   gtk_tree_view_column_set_cell_data_func (column, ia->asst.fixed_renderer,
945                                            render_line, NULL, NULL);
946   gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
947
948   max_line_length = 0;
949   for (i = 0; i < ia->file.line_cnt; i++)
950     {
951       size_t w = ds_length (&ia->file.lines[i]);
952       max_line_length = MAX (max_line_length, w);
953     }
954
955   content_width = get_monospace_width (tree_view, ia->asst.fixed_renderer,
956                                        max_line_length);
957   header_width = get_string_width (tree_view, ia->asst.prop_renderer, title);
958   gtk_tree_view_column_set_fixed_width (column, MAX (content_width,
959                                                      header_width));
960   gtk_tree_view_append_column (tree_view, column);
961
962   gtk_tree_view_set_fixed_height_mode (tree_view, true);
963
964   gtk_container_add (parent, GTK_WIDGET (tree_view));
965   gtk_widget_show (GTK_WIDGET (tree_view));
966
967   return tree_view;
968 }
969
970 /* Called when the line selected in the first_line tree view
971    changes. */
972 static void
973 on_first_line_change (GtkTreeSelection *selection UNUSED,
974                       struct import_assistant *ia)
975 {
976   get_first_line (ia);
977 }
978
979 /* Called when the checkbox that indicates whether variable
980    names are in the row above the first line is toggled. */
981 static void
982 on_variable_names_cb_toggle (GtkToggleButton *variable_names_cb UNUSED,
983                              struct import_assistant *ia)
984 {
985   get_first_line (ia);
986 }
987
988 /* Sets the widgets to match IA's first_line substructure. */
989 static void
990 set_first_line (struct import_assistant *ia)
991 {
992   GtkTreePath *path;
993
994   path = gtk_tree_path_new_from_indices (ia->first_line.skip_lines, -1);
995   gtk_tree_view_set_cursor (GTK_TREE_VIEW (ia->first_line.tree_view),
996                             path, NULL, false);
997   gtk_tree_path_free (path);
998
999   gtk_toggle_button_set_active (
1000     GTK_TOGGLE_BUTTON (ia->first_line.variable_names_cb),
1001     ia->first_line.variable_names);
1002   gtk_widget_set_sensitive (ia->first_line.variable_names_cb,
1003                             ia->first_line.skip_lines > 0);
1004 }
1005
1006 /* Sets IA's first_line substructure to match the widgets. */
1007 static void
1008 get_first_line (struct import_assistant *ia)
1009 {
1010   GtkTreeSelection *selection;
1011   GtkTreeIter iter;
1012   GtkTreeModel *model;
1013
1014   selection = gtk_tree_view_get_selection (ia->first_line.tree_view);
1015   if (gtk_tree_selection_get_selected (selection, &model, &iter))
1016     {
1017       GtkTreePath *path = gtk_tree_model_get_path (model, &iter);
1018       int row = gtk_tree_path_get_indices (path)[0];
1019       gtk_tree_path_free (path);
1020
1021       ia->first_line.skip_lines = row;
1022       ia->first_line.variable_names =
1023         (ia->first_line.skip_lines > 0
1024          && gtk_toggle_button_get_active (
1025            GTK_TOGGLE_BUTTON (ia->first_line.variable_names_cb)));
1026     }
1027   gtk_widget_set_sensitive (ia->first_line.variable_names_cb,
1028                             ia->first_line.skip_lines > 0);
1029 }
1030 \f
1031 /* The "separators" page of the assistant. */
1032
1033 static void revise_fields_preview (struct import_assistant *ia);
1034 static void choose_likely_separators (struct import_assistant *ia);
1035 static void find_commonest_chars (unsigned long int histogram[UCHAR_MAX + 1],
1036                                   const char *targets, const char *def,
1037                                   struct string *result);
1038 static void clear_fields (struct import_assistant *ia);
1039 static void revise_fields_preview (struct import_assistant *);
1040 static void set_separators (struct import_assistant *);
1041 static void get_separators (struct import_assistant *);
1042 static void on_separators_custom_entry_notify (GObject *UNUSED,
1043                                                GParamSpec *UNUSED,
1044                                                struct import_assistant *);
1045 static void on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
1046                                             struct import_assistant *);
1047 static void on_quote_combo_change (GtkComboBox *combo,
1048                                    struct import_assistant *);
1049 static void on_quote_cb_toggle (GtkToggleButton *quote_cb,
1050                                 struct import_assistant *);
1051 static void on_separator_toggle (GtkToggleButton *, struct import_assistant *);
1052 static void render_input_cell (GtkTreeViewColumn *tree_column,
1053                                GtkCellRenderer *cell,
1054                                GtkTreeModel *model, GtkTreeIter *iter,
1055                                gpointer ia);
1056 static gboolean on_query_input_tooltip (GtkWidget *widget, gint wx, gint wy,
1057                                         gboolean keyboard_mode UNUSED,
1058                                         GtkTooltip *tooltip,
1059                                         struct import_assistant *);
1060
1061 /* A common field separator and its identifying name. */
1062 struct separator
1063   {
1064     const char *name;           /* Name (for use with get_widget_assert). */
1065     int c;                      /* Separator character. */
1066   };
1067
1068 /* All the separators in the dialog box. */
1069 static const struct separator separators[] =
1070   {
1071     {"space", ' '},
1072     {"tab", '\t'},
1073     {"bang", '!'},
1074     {"colon", ':'},
1075     {"comma", ','},
1076     {"hyphen", '-'},
1077     {"pipe", '|'},
1078     {"semicolon", ';'},
1079     {"slash", '/'},
1080   };
1081 #define SEPARATOR_CNT (sizeof separators / sizeof *separators)
1082
1083 static void
1084 set_quote_list (GtkComboBoxEntry *cb)
1085 {
1086   GtkListStore *list =  gtk_list_store_new (1, G_TYPE_STRING);
1087   GtkTreeIter iter;
1088   gint i;
1089   const gchar *seperator[3] = {"'\"", "\'", "\""};
1090
1091   for (i = 0; i < 3; i++)
1092     {
1093       const gchar *s = seperator[i];
1094
1095       /* Add a new row to the model */
1096       gtk_list_store_append (list, &iter);
1097       gtk_list_store_set (list, &iter,
1098                           0, s,
1099                           -1);
1100
1101     }
1102
1103   gtk_combo_box_set_model (GTK_COMBO_BOX (cb), GTK_TREE_MODEL (list));
1104   g_object_unref (list);
1105
1106   gtk_combo_box_entry_set_text_column (cb, 0);
1107 }
1108
1109 /* Initializes IA's separators substructure. */
1110 static void
1111 init_separators_page (struct import_assistant *ia)
1112 {
1113   GtkBuilder *builder = ia->asst.builder;
1114   struct separators_page *p = &ia->separators;
1115   size_t i;
1116
1117   choose_likely_separators (ia);
1118
1119   p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Separators"),
1120                                    GTK_ASSISTANT_PAGE_CONTENT);
1121   p->custom_cb = get_widget_assert (builder, "custom-cb");
1122   p->custom_entry = get_widget_assert (builder, "custom-entry");
1123   p->quote_combo = get_widget_assert (builder, "quote-combo");
1124   p->quote_entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (p->quote_combo)));
1125   p->quote_cb = get_widget_assert (builder, "quote-cb");
1126   p->escape_cb = get_widget_assert (builder, "escape");
1127
1128   set_separators (ia);
1129   set_quote_list (GTK_COMBO_BOX_ENTRY (p->quote_combo));
1130   p->fields_tree_view = GTK_TREE_VIEW (get_widget_assert (builder, "fields"));
1131   g_signal_connect (p->quote_combo, "changed",
1132                     G_CALLBACK (on_quote_combo_change), ia);
1133   g_signal_connect (p->quote_cb, "toggled",
1134                     G_CALLBACK (on_quote_cb_toggle), ia);
1135   g_signal_connect (p->custom_entry, "notify::text",
1136                     G_CALLBACK (on_separators_custom_entry_notify), ia);
1137   g_signal_connect (p->custom_cb, "toggled",
1138                     G_CALLBACK (on_separators_custom_cb_toggle), ia);
1139   for (i = 0; i < SEPARATOR_CNT; i++)
1140     g_signal_connect (get_widget_assert (builder, separators[i].name),
1141                       "toggled", G_CALLBACK (on_separator_toggle), ia);
1142   g_signal_connect (p->escape_cb, "toggled",
1143                     G_CALLBACK (on_separator_toggle), ia);
1144 }
1145
1146 /* Frees IA's separators substructure. */
1147 static void
1148 destroy_separators_page (struct import_assistant *ia)
1149 {
1150   struct separators_page *s = &ia->separators;
1151
1152   ds_destroy (&s->separators);
1153   ds_destroy (&s->quotes);
1154   clear_fields (ia);
1155 }
1156
1157 /* Called just before the separators page becomes visible in the
1158    assistant. */
1159 static void
1160 prepare_separators_page (struct import_assistant *ia)
1161 {
1162   revise_fields_preview (ia);
1163 }
1164
1165 /* Called when the Reset button is clicked on the separators
1166    page, resets the separators to the defaults. */
1167 static void
1168 reset_separators_page (struct import_assistant *ia)
1169 {
1170   choose_likely_separators (ia);
1171   set_separators (ia);
1172 }
1173
1174 /* Frees and clears the column data in IA's separators
1175    substructure. */
1176 static void
1177 clear_fields (struct import_assistant *ia)
1178 {
1179   struct separators_page *s = &ia->separators;
1180
1181   if (s->column_cnt > 0)
1182     {
1183       struct column *col;
1184       size_t row;
1185
1186       for (row = 0; row < ia->file.line_cnt; row++)
1187         {
1188           const struct string *line = &ia->file.lines[row];
1189           const char *line_start = ds_data (line);
1190           const char *line_end = ds_end (line);
1191
1192           for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1193             {
1194               char *s = ss_data (col->contents[row]);
1195               if (!(s >= line_start && s <= line_end))
1196                 ss_dealloc (&col->contents[row]);
1197             }
1198         }
1199
1200       for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1201         {
1202           free (col->name);
1203           free (col->contents);
1204         }
1205
1206       free (s->columns);
1207       s->columns = NULL;
1208       s->column_cnt = 0;
1209     }
1210 }
1211
1212 /* Breaks the file data in IA into columns based on the
1213    separators set in IA's separators substructure. */
1214 static void
1215 split_fields (struct import_assistant *ia)
1216 {
1217   struct separators_page *s = &ia->separators;
1218   size_t columns_allocated;
1219   bool space_sep;
1220   size_t row;
1221
1222   clear_fields (ia);
1223
1224   /* Is space in the set of separators? */
1225   space_sep = ss_find_byte (ds_ss (&s->separators), ' ') != SIZE_MAX;
1226
1227   /* Split all the lines, not just those from
1228      ia->first_line.skip_lines on, so that we split the line that
1229      contains variables names if ia->first_line.variable_names is
1230      true. */
1231   columns_allocated = 0;
1232   for (row = 0; row < ia->file.line_cnt; row++)
1233     {
1234       struct string *line = &ia->file.lines[row];
1235       struct substring text = ds_ss (line);
1236       size_t column_idx;
1237
1238       for (column_idx = 0; ; column_idx++)
1239         {
1240           struct substring field;
1241           struct column *column;
1242
1243           if (space_sep)
1244             ss_ltrim (&text, ss_cstr (" "));
1245           if (ss_is_empty (text))
1246             {
1247               if (column_idx != 0)
1248                 break;
1249               field = text;
1250             }
1251           else if (!ds_is_empty (&s->quotes)
1252                    && ds_find_byte (&s->quotes, text.string[0]) != SIZE_MAX)
1253             {
1254               int quote = ss_get_byte (&text);
1255               if (!s->escape)
1256                 ss_get_until (&text, quote, &field);
1257               else
1258                 {
1259                   struct string s;
1260                   int c;
1261
1262                   ds_init_empty (&s);
1263                   while ((c = ss_get_byte (&text)) != EOF)
1264                     if (c != quote)
1265                       ds_put_byte (&s, c);
1266                     else if (ss_match_byte (&text, quote))
1267                       ds_put_byte (&s, quote);
1268                     else
1269                       break;
1270                   field = ds_ss (&s);
1271                 }
1272             }
1273           else
1274             ss_get_bytes (&text, ss_cspan (text, ds_ss (&s->separators)),
1275                           &field);
1276
1277           if (column_idx >= s->column_cnt)
1278             {
1279               struct column *column;
1280
1281               if (s->column_cnt >= columns_allocated)
1282                 s->columns = x2nrealloc (s->columns, &columns_allocated,
1283                                          sizeof *s->columns);
1284               column = &s->columns[s->column_cnt++];
1285               column->name = NULL;
1286               column->width = 0;
1287               column->contents = xcalloc (ia->file.line_cnt,
1288                                           sizeof *column->contents);
1289             }
1290           column = &s->columns[column_idx];
1291           column->contents[row] = field;
1292           if (ss_length (field) > column->width)
1293             column->width = ss_length (field);
1294
1295           if (space_sep)
1296             ss_ltrim (&text, ss_cstr (" "));
1297           if (ss_is_empty (text))
1298             break;
1299           if (ss_find_byte (ds_ss (&s->separators), ss_first (text))
1300               != SIZE_MAX)
1301             ss_advance (&text, 1);
1302         }
1303     }
1304 }
1305
1306 /* Chooses a name for each column on the separators page */
1307 static void
1308 choose_column_names (struct import_assistant *ia)
1309 {
1310   const struct first_line_page *f = &ia->first_line;
1311   struct separators_page *s = &ia->separators;
1312   struct dictionary *dict;
1313   unsigned long int generated_name_count = 0;
1314   struct column *col;
1315   size_t name_row;
1316
1317   dict = dict_create (get_default_encoding ());
1318   name_row = f->variable_names && f->skip_lines ? f->skip_lines : 0;
1319   for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1320     {
1321       char *hint, *name;
1322
1323       hint = name_row ? ss_xstrdup (col->contents[name_row - 1]) : NULL;
1324       name = dict_make_unique_var_name (dict, hint, &generated_name_count);
1325       free (hint);
1326
1327       col->name = name;
1328       dict_create_var_assert (dict, name, 0);
1329     }
1330   dict_destroy (dict);
1331 }
1332
1333 /* Picks the most likely separator and quote characters based on
1334    IA's file data. */
1335 static void
1336 choose_likely_separators (struct import_assistant *ia)
1337 {
1338   unsigned long int histogram[UCHAR_MAX + 1] = { 0 };
1339   size_t row;
1340
1341   /* Construct a histogram of all the characters used in the
1342      file. */
1343   for (row = 0; row < ia->file.line_cnt; row++)
1344     {
1345       struct substring line = ds_ss (&ia->file.lines[row]);
1346       size_t length = ss_length (line);
1347       size_t i;
1348       for (i = 0; i < length; i++)
1349         histogram[(unsigned char) line.string[i]]++;
1350     }
1351
1352   find_commonest_chars (histogram, "\"'", "", &ia->separators.quotes);
1353   find_commonest_chars (histogram, ",;:/|!\t-", ",",
1354                         &ia->separators.separators);
1355   ia->separators.escape = true;
1356 }
1357
1358 /* Chooses the most common character among those in TARGETS,
1359    based on the frequency data in HISTOGRAM, and stores it in
1360    RESULT.  If there is a tie for the most common character among
1361    those in TARGETS, the earliest character is chosen.  If none
1362    of the TARGETS appear at all, then DEF is used as a
1363    fallback. */
1364 static void
1365 find_commonest_chars (unsigned long int histogram[UCHAR_MAX + 1],
1366                       const char *targets, const char *def,
1367                       struct string *result)
1368 {
1369   unsigned char max = 0;
1370   unsigned long int max_count = 0;
1371
1372   for (; *targets != '\0'; targets++)
1373     {
1374       unsigned char c = *targets;
1375       unsigned long int count = histogram[c];
1376       if (count > max_count)
1377         {
1378           max = c;
1379           max_count = count;
1380         }
1381     }
1382   if (max_count > 0)
1383     {
1384       ds_clear (result);
1385       ds_put_byte (result, max);
1386     }
1387   else
1388     ds_assign_cstr (result, def);
1389 }
1390
1391 /* Revises the contents of the fields tree view based on the
1392    currently chosen set of separators. */
1393 static void
1394 revise_fields_preview (struct import_assistant *ia)
1395 {
1396   GtkWidget *w;
1397
1398   push_watch_cursor (ia);
1399
1400   w = GTK_WIDGET (ia->separators.fields_tree_view);
1401   gtk_widget_destroy (w);
1402   get_separators (ia);
1403   split_fields (ia);
1404   choose_column_names (ia);
1405   ia->separators.fields_tree_view = create_data_tree_view (
1406     true,
1407     GTK_CONTAINER (get_widget_assert (ia->asst.builder, "fields-scroller")),
1408     ia);
1409
1410   pop_watch_cursor (ia);
1411 }
1412
1413 /* Sets the widgets to match IA's separators substructure. */
1414 static void
1415 set_separators (struct import_assistant *ia)
1416 {
1417   struct separators_page *s = &ia->separators;
1418   unsigned int seps;
1419   struct string custom;
1420   bool any_custom;
1421   bool any_quotes;
1422   size_t i;
1423
1424   ds_init_empty (&custom);
1425   seps = 0;
1426   for (i = 0; i < ds_length (&s->separators); i++)
1427     {
1428       unsigned char c = ds_at (&s->separators, i);
1429       int j;
1430
1431       for (j = 0; j < SEPARATOR_CNT; j++)
1432         {
1433           const struct separator *s = &separators[j];
1434           if (s->c == c)
1435             {
1436               seps += 1u << j;
1437               goto next;
1438             }
1439         }
1440
1441       ds_put_byte (&custom, c);
1442     next:;
1443     }
1444
1445   for (i = 0; i < SEPARATOR_CNT; i++)
1446     {
1447       const struct separator *s = &separators[i];
1448       GtkWidget *button = get_widget_assert (ia->asst.builder, s->name);
1449       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
1450                                     (seps & (1u << i)) != 0);
1451     }
1452   any_custom = !ds_is_empty (&custom);
1453   gtk_entry_set_text (GTK_ENTRY (s->custom_entry), ds_cstr (&custom));
1454   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->custom_cb),
1455                                 any_custom);
1456   gtk_widget_set_sensitive (s->custom_entry, any_custom);
1457   ds_destroy (&custom);
1458
1459   any_quotes = !ds_is_empty (&s->quotes);
1460
1461   gtk_entry_set_text (s->quote_entry,
1462                       any_quotes ? ds_cstr (&s->quotes) : "\"");
1463   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->quote_cb),
1464                                 any_quotes);
1465   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->escape_cb),
1466                                 s->escape);
1467   gtk_widget_set_sensitive (s->quote_combo, any_quotes);
1468   gtk_widget_set_sensitive (s->escape_cb, any_quotes);
1469 }
1470
1471 /* Sets IA's separators substructure to match the widgets. */
1472 static void
1473 get_separators (struct import_assistant *ia)
1474 {
1475   struct separators_page *s = &ia->separators;
1476   int i;
1477
1478   ds_clear (&s->separators);
1479   for (i = 0; i < SEPARATOR_CNT; i++)
1480     {
1481       const struct separator *sep = &separators[i];
1482       GtkWidget *button = get_widget_assert (ia->asst.builder, sep->name);
1483       if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
1484         ds_put_byte (&s->separators, sep->c);
1485     }
1486
1487   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->custom_cb)))
1488     ds_put_cstr (&s->separators,
1489                  gtk_entry_get_text (GTK_ENTRY (s->custom_entry)));
1490
1491   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->quote_cb)))
1492     {
1493       gchar *text = gtk_combo_box_get_active_text (
1494                       GTK_COMBO_BOX (s->quote_combo));
1495       ds_assign_cstr (&s->quotes, text);
1496       g_free (text);
1497     }
1498   else
1499     ds_clear (&s->quotes);
1500   s->escape = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->escape_cb));
1501 }
1502
1503 /* Called when the user changes the entry field for custom
1504    separators. */
1505 static void
1506 on_separators_custom_entry_notify (GObject *gobject UNUSED,
1507                                    GParamSpec *arg1 UNUSED,
1508                                    struct import_assistant *ia)
1509 {
1510   revise_fields_preview (ia);
1511 }
1512
1513 /* Called when the user toggles the checkbox that enables custom
1514    separators. */
1515 static void
1516 on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
1517                                 struct import_assistant *ia)
1518 {
1519   bool is_active = gtk_toggle_button_get_active (custom_cb);
1520   gtk_widget_set_sensitive (ia->separators.custom_entry, is_active);
1521   revise_fields_preview (ia);
1522 }
1523
1524 /* Called when the user changes the selection in the combo box
1525    that selects a quote character. */
1526 static void
1527 on_quote_combo_change (GtkComboBox *combo, struct import_assistant *ia)
1528 {
1529   revise_fields_preview (ia);
1530 }
1531
1532 /* Called when the user toggles the checkbox that enables
1533    quoting. */
1534 static void
1535 on_quote_cb_toggle (GtkToggleButton *quote_cb, struct import_assistant *ia)
1536 {
1537   bool is_active = gtk_toggle_button_get_active (quote_cb);
1538   gtk_widget_set_sensitive (ia->separators.quote_combo, is_active);
1539   gtk_widget_set_sensitive (ia->separators.escape_cb, is_active);
1540   revise_fields_preview (ia);
1541 }
1542
1543 /* Called when the user toggles one of the separators
1544    checkboxes. */
1545 static void
1546 on_separator_toggle (GtkToggleButton *toggle UNUSED,
1547                      struct import_assistant *ia)
1548 {
1549   revise_fields_preview (ia);
1550 }
1551
1552 /* Called to render one of the cells in the fields preview tree
1553    view. */
1554 static void
1555 render_input_cell (GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1556                    GtkTreeModel *model, GtkTreeIter *iter,
1557                    gpointer ia_)
1558 {
1559   struct import_assistant *ia = ia_;
1560   struct substring field;
1561   size_t row;
1562   gint column;
1563
1564   column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1565                                                "column-number"));
1566   row = empty_list_store_iter_to_row (iter) + ia->first_line.skip_lines;
1567   field = ia->separators.columns[column].contents[row];
1568   if (field.string != NULL)
1569     {
1570       GValue text = {0, };
1571       g_value_init (&text, G_TYPE_STRING);
1572       g_value_take_string (&text, ss_xstrdup (field));
1573       g_object_set_property (G_OBJECT (cell), "text", &text);
1574       g_value_unset (&text);
1575       g_object_set (cell, "background-set", FALSE, (void *) NULL);
1576     }
1577   else
1578     g_object_set (cell,
1579                   "text", "",
1580                   "background", "red",
1581                   "background-set", TRUE,
1582                   (void *) NULL);
1583 }
1584
1585 /* Called to render a tooltip on one of the cells in the fields
1586    preview tree view. */
1587 static gboolean
1588 on_query_input_tooltip (GtkWidget *widget, gint wx, gint wy,
1589                         gboolean keyboard_mode UNUSED,
1590                         GtkTooltip *tooltip, struct import_assistant *ia)
1591 {
1592   size_t row, column;
1593
1594   if (!get_tooltip_location (widget, wx, wy, ia, &row, &column))
1595     return FALSE;
1596
1597   if (ia->separators.columns[column].contents[row].string != NULL)
1598     return FALSE;
1599
1600   gtk_tooltip_set_text (tooltip,
1601                         _("This input line has too few separators "
1602                           "to fill in this field."));
1603   return TRUE;
1604 }
1605 \f
1606 /* The "formats" page of the assistant. */
1607
1608 static void on_variable_change (PsppireDict *dict, int idx,
1609                                 struct import_assistant *);
1610 static void clear_modified_vars (struct import_assistant *);
1611
1612 /* Initializes IA's formats substructure. */
1613 static void
1614 init_formats_page (struct import_assistant *ia)
1615 {
1616   GtkBuilder *builder = ia->asst.builder;
1617   struct formats_page *p = &ia->formats;
1618
1619   p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Formats"),
1620                                    GTK_ASSISTANT_PAGE_CONFIRM);
1621   p->data_tree_view = GTK_TREE_VIEW (get_widget_assert (builder, "data"));
1622   p->modified_vars = NULL;
1623   p->modified_var_cnt = 0;
1624   p->dict = NULL;
1625 }
1626
1627 /* Frees IA's formats substructure. */
1628 static void
1629 destroy_formats_page (struct import_assistant *ia)
1630 {
1631   struct formats_page *p = &ia->formats;
1632
1633   if (p->psppire_dict != NULL)
1634     {
1635       dict_destroy (p->psppire_dict->dict);
1636       g_object_unref (p->psppire_dict);
1637     }
1638   clear_modified_vars (ia);
1639 }
1640
1641 /* Called just before the formats page of the assistant is
1642    displayed. */
1643 static void
1644 prepare_formats_page (struct import_assistant *ia)
1645 {
1646   struct dictionary *dict;
1647   PsppireDict *psppire_dict;
1648   PsppireVarStore *var_store;
1649   GtkBin *vars_scroller;
1650   GtkWidget *old_var_sheet;
1651   PsppireVarSheet *var_sheet;
1652   struct separators_page *s = &ia->separators;
1653   struct formats_page *p = &ia->formats;
1654   struct fmt_guesser *fg;
1655   unsigned long int number = 0;
1656   size_t column_idx;
1657
1658   push_watch_cursor (ia);
1659
1660   dict = dict_create (get_default_encoding ());
1661   fg = fmt_guesser_create ();
1662   for (column_idx = 0; column_idx < s->column_cnt; column_idx++)
1663     {
1664       struct variable *modified_var;
1665
1666       modified_var = (column_idx < p->modified_var_cnt
1667                       ? p->modified_vars[column_idx] : NULL);
1668       if (modified_var == NULL)
1669         {
1670           struct column *column = &s->columns[column_idx];
1671           struct variable *var;
1672           struct fmt_spec format;
1673           char *name;
1674           size_t row;
1675
1676           /* Choose variable name. */
1677           name = dict_make_unique_var_name (dict, column->name, &number);
1678
1679           /* Choose variable format. */
1680           fmt_guesser_clear (fg);
1681           for (row = ia->first_line.skip_lines; row < ia->file.line_cnt; row++)
1682             fmt_guesser_add (fg, column->contents[row]);
1683           fmt_guesser_guess (fg, &format);
1684           fmt_fix_input (&format);
1685
1686           /* Create variable. */
1687           var = dict_create_var_assert (dict, name, fmt_var_width (&format));
1688           var_set_both_formats (var, &format);
1689
1690           free (name);
1691         }
1692       else
1693         {
1694           char *name;
1695
1696           name = dict_make_unique_var_name (dict, var_get_name (modified_var),
1697                                             &number);
1698           dict_clone_var_as_assert (dict, modified_var, name);
1699           free (name);
1700         }
1701     }
1702   fmt_guesser_destroy (fg);
1703
1704   psppire_dict = psppire_dict_new_from_dict (dict);
1705   g_signal_connect (psppire_dict, "variable_changed",
1706                     G_CALLBACK (on_variable_change), ia);
1707   ia->formats.dict = dict;
1708   ia->formats.psppire_dict = psppire_dict;
1709
1710   /* XXX: PsppireVarStore doesn't hold a reference to
1711      psppire_dict for now, but it should.  After it does, we
1712      should g_object_ref the psppire_dict here, since we also
1713      hold a reference via ia->formats.dict. */
1714   var_store = psppire_var_store_new (psppire_dict);
1715   g_object_set (var_store,
1716                 "format-type", PSPPIRE_VAR_STORE_INPUT_FORMATS,
1717                 (void *) NULL);
1718   var_sheet = PSPPIRE_VAR_SHEET (psppire_var_sheet_new ());
1719   g_object_set (var_sheet,
1720                 "model", var_store,
1721                 "may-create-vars", FALSE,
1722                 (void *) NULL);
1723
1724   vars_scroller = GTK_BIN (get_widget_assert (ia->asst.builder, "vars-scroller"));
1725   old_var_sheet = gtk_bin_get_child (vars_scroller);
1726   if (old_var_sheet != NULL)
1727     gtk_widget_destroy (old_var_sheet);
1728   gtk_container_add (GTK_CONTAINER (vars_scroller), GTK_WIDGET (var_sheet));
1729   gtk_widget_show (GTK_WIDGET (var_sheet));
1730
1731   gtk_widget_destroy (GTK_WIDGET (ia->formats.data_tree_view));
1732   ia->formats.data_tree_view = create_data_tree_view (
1733     false,
1734     GTK_CONTAINER (get_widget_assert (ia->asst.builder, "data-scroller")),
1735     ia);
1736
1737   pop_watch_cursor (ia);
1738 }
1739
1740 /* Clears the set of user-modified variables from IA's formats
1741    substructure.  This discards user modifications to variable
1742    formats, thereby causing formats to revert to their
1743    defaults.  */
1744 static void
1745 clear_modified_vars (struct import_assistant *ia)
1746 {
1747   struct formats_page *p = &ia->formats;
1748   size_t i;
1749
1750   for (i = 0; i < p->modified_var_cnt; i++)
1751     var_destroy (p->modified_vars[i]);
1752   free (p->modified_vars);
1753   p->modified_vars = NULL;
1754   p->modified_var_cnt = 0;
1755 }
1756
1757 /* Resets the formats page to its defaults, discarding user
1758    modifications. */
1759 static void
1760 reset_formats_page (struct import_assistant *ia)
1761 {
1762   clear_modified_vars (ia);
1763   prepare_formats_page (ia);
1764 }
1765
1766 /* Called when the user changes one of the variables in the
1767    dictionary. */
1768 static void
1769 on_variable_change (PsppireDict *dict, int dict_idx,
1770                     struct import_assistant *ia)
1771 {
1772   struct formats_page *p = &ia->formats;
1773   GtkTreeView *tv = ia->formats.data_tree_view;
1774   gint column_idx = dict_idx + 1;
1775
1776   push_watch_cursor (ia);
1777
1778   /* Remove previous column and replace with new column. */
1779   gtk_tree_view_remove_column (tv, gtk_tree_view_get_column (tv, column_idx));
1780   gtk_tree_view_insert_column (tv, make_data_column (ia, tv, false, dict_idx),
1781                                column_idx);
1782
1783   /* Save a copy of the modified variable in modified_vars, so
1784      that its attributes will be preserved if we back up to the
1785      previous page with the Prev button and then come back
1786      here. */
1787   if (dict_idx >= p->modified_var_cnt)
1788     {
1789       size_t i;
1790       p->modified_vars = xnrealloc (p->modified_vars, dict_idx + 1,
1791                                     sizeof *p->modified_vars);
1792       for (i = 0; i <= dict_idx; i++)
1793         p->modified_vars[i] = NULL;
1794       p->modified_var_cnt = dict_idx + 1;
1795     }
1796   if (p->modified_vars[dict_idx])
1797     var_destroy (p->modified_vars[dict_idx]);
1798   p->modified_vars[dict_idx]
1799     = var_clone (psppire_dict_get_variable (dict, dict_idx));
1800
1801   pop_watch_cursor (ia);
1802 }
1803
1804 /* Parses the contents of the field at (ROW,COLUMN) according to
1805    its variable format.  If OUTPUTP is non-null, then *OUTPUTP
1806    receives the formatted output for that field (which must be
1807    freed with free).  If TOOLTIPP is non-null, then *TOOLTIPP
1808    receives a message suitable for use in a tooltip, if one is
1809    needed, or a null pointer otherwise.  Returns true if a
1810    tooltip message is needed, otherwise false. */
1811 static bool
1812 parse_field (struct import_assistant *ia,
1813              size_t row, size_t column,
1814              char **outputp, char **tooltipp)
1815 {
1816   struct substring field;
1817   union value val;
1818   struct variable *var;
1819   const struct fmt_spec *in;
1820   struct fmt_spec out;
1821   char *tooltip;
1822   bool ok;
1823
1824   field = ia->separators.columns[column].contents[row];
1825   var = dict_get_var (ia->formats.dict, column);
1826   value_init (&val, var_get_width (var));
1827   in = var_get_print_format (var);
1828   out = fmt_for_output_from_input (in);
1829   tooltip = NULL;
1830   if (field.string != NULL)
1831     {
1832       char *error;
1833
1834       error = data_in (field, "UTF-8", in->type, &val, var_get_width (var),
1835                        dict_get_encoding (ia->formats.dict));
1836       if (error != NULL)
1837         {
1838           tooltip = xasprintf (_("Cannot parse field content `%.*s' as "
1839                                  "format %s: %s"),
1840                                (int) field.length, field.string,
1841                                fmt_name (in->type), error);
1842           free (error);
1843         }
1844     }
1845   else
1846     {
1847       tooltip = xstrdup (_("This input line has too few separators "
1848                            "to fill in this field."));
1849       value_set_missing (&val, var_get_width (var));
1850     }
1851   if (outputp != NULL)
1852     {
1853       *outputp = data_out (&val, dict_get_encoding (ia->formats.dict),  &out);
1854     }
1855   value_destroy (&val, var_get_width (var));
1856
1857   ok = tooltip == NULL;
1858   if (tooltipp != NULL)
1859     *tooltipp = tooltip;
1860   else
1861     free (tooltip);
1862   return ok;
1863 }
1864
1865 /* Called to render one of the cells in the data preview tree
1866    view. */
1867 static void
1868 render_output_cell (GtkTreeViewColumn *tree_column,
1869                     GtkCellRenderer *cell,
1870                     GtkTreeModel *model,
1871                     GtkTreeIter *iter,
1872                     gpointer ia_)
1873 {
1874   struct import_assistant *ia = ia_;
1875   char *output;
1876   GValue gvalue = { 0, };
1877   bool ok;
1878
1879   ok = parse_field (ia,
1880                     (empty_list_store_iter_to_row (iter)
1881                      + ia->first_line.skip_lines),
1882                     GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1883                                                         "column-number")),
1884                     &output, NULL);
1885
1886   g_value_init (&gvalue, G_TYPE_STRING);
1887   g_value_take_string (&gvalue, output);
1888   g_object_set_property (G_OBJECT (cell), "text", &gvalue);
1889   g_value_unset (&gvalue);
1890
1891   if (ok)
1892     g_object_set (cell, "background-set", FALSE, (void *) NULL);
1893   else
1894     g_object_set (cell,
1895                   "background", "red",
1896                   "background-set", TRUE,
1897                   (void *) NULL);
1898 }
1899
1900 /* Called to render a tooltip for one of the cells in the data
1901    preview tree view. */
1902 static gboolean
1903 on_query_output_tooltip (GtkWidget *widget, gint wx, gint wy,
1904                         gboolean keyboard_mode UNUSED,
1905                         GtkTooltip *tooltip, struct import_assistant *ia)
1906 {
1907   size_t row, column;
1908   char *text;
1909
1910   if (!get_tooltip_location (widget, wx, wy, ia, &row, &column))
1911     return FALSE;
1912
1913   if (parse_field (ia, row, column, NULL, &text))
1914     return FALSE;
1915
1916   gtk_tooltip_set_text (tooltip, text);
1917   free (text);
1918   return TRUE;
1919 }
1920 \f
1921 /* Utility functions used by multiple pages of the assistant. */
1922
1923 static gboolean
1924 get_tooltip_location (GtkWidget *widget, gint wx, gint wy,
1925                       const struct import_assistant *ia,
1926                       size_t *row, size_t *column)
1927 {
1928   GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
1929   gint bx, by;
1930   GtkTreePath *path;
1931   GtkTreeIter iter;
1932   GtkTreeViewColumn *tree_column;
1933   GtkTreeModel *tree_model;
1934   bool ok;
1935
1936   /* Check that WIDGET is really visible on the screen before we
1937      do anything else.  This is a bug fix for a sticky situation:
1938      when text_data_import_assistant() returns, it frees the data
1939      necessary to compose the tool tip message, but there may be
1940      a tool tip under preparation at that point (even if there is
1941      no visible tool tip) that will call back into us a little
1942      bit later.  Perhaps the correct solution to this problem is
1943      to make the data related to the tool tips part of a GObject
1944      that only gets destroyed when all references are released,
1945      but this solution appears to be effective too. */
1946   if (!gtk_widget_get_mapped (widget))
1947     return FALSE;
1948
1949   gtk_tree_view_convert_widget_to_bin_window_coords (tree_view,
1950                                                      wx, wy, &bx, &by);
1951   if (!gtk_tree_view_get_path_at_pos (tree_view, bx, by,
1952                                       &path, &tree_column, NULL, NULL))
1953     return FALSE;
1954
1955   *column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1956                                                 "column-number"));
1957
1958   tree_model = gtk_tree_view_get_model (tree_view);
1959   ok = gtk_tree_model_get_iter (tree_model, &iter, path);
1960   gtk_tree_path_free (path);
1961   if (!ok)
1962     return FALSE;
1963
1964   *row = empty_list_store_iter_to_row (&iter) + ia->first_line.skip_lines;
1965   return TRUE;
1966 }
1967
1968 static void
1969 make_tree_view (const struct import_assistant *ia,
1970                 size_t first_line,
1971                 GtkTreeView **tree_view)
1972 {
1973   GtkTreeModel *model;
1974
1975   *tree_view = GTK_TREE_VIEW (gtk_tree_view_new ());
1976   model = GTK_TREE_MODEL (psppire_empty_list_store_new (
1977                             ia->file.line_cnt - first_line));
1978   g_object_set_data (G_OBJECT (model), "lines", ia->file.lines + first_line);
1979   g_object_set_data (G_OBJECT (model), "first-line",
1980                      GINT_TO_POINTER (first_line));
1981   gtk_tree_view_set_model (*tree_view, model);
1982   g_object_unref (model);
1983
1984   add_line_number_column (ia, *tree_view);
1985 }
1986
1987 static void
1988 render_line_number (GtkTreeViewColumn *tree_column,
1989                     GtkCellRenderer *cell,
1990                     GtkTreeModel *tree_model,
1991                     GtkTreeIter *iter,
1992                     gpointer data)
1993 {
1994   gint row = empty_list_store_iter_to_row (iter);
1995   char s[INT_BUFSIZE_BOUND (int)];
1996   int first_line;
1997
1998   first_line = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_model),
1999                                                    "first-line"));
2000   sprintf (s, "%d", first_line + row);
2001   g_object_set (cell, "text", s, NULL);
2002 }
2003
2004 static void
2005 add_line_number_column (const struct import_assistant *ia,
2006                         GtkTreeView *treeview)
2007 {
2008   GtkTreeViewColumn *column;
2009
2010   column = gtk_tree_view_column_new_with_attributes (
2011     _("Line"), ia->asst.prop_renderer, (void *) NULL);
2012   gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
2013   gtk_tree_view_column_set_fixed_width (
2014     column, get_monospace_width (treeview, ia->asst.prop_renderer, 5));
2015   gtk_tree_view_column_set_resizable (column, TRUE);
2016   gtk_tree_view_column_set_cell_data_func (column, ia->asst.prop_renderer,
2017                                            render_line_number, NULL, NULL);
2018   gtk_tree_view_append_column (treeview, column);
2019 }
2020
2021 static gint
2022 get_monospace_width (GtkTreeView *treeview, GtkCellRenderer *renderer,
2023                      size_t char_cnt)
2024 {
2025   struct string s;
2026   gint width;
2027
2028   ds_init_empty (&s);
2029   ds_put_byte_multiple (&s, '0', char_cnt);
2030   ds_put_byte (&s, ' ');
2031   width = get_string_width (treeview, renderer, ds_cstr (&s));
2032   ds_destroy (&s);
2033
2034   return width;
2035 }
2036
2037 static gint
2038 get_string_width (GtkTreeView *treeview, GtkCellRenderer *renderer,
2039                   const char *string)
2040 {
2041   gint width;
2042   g_object_set (G_OBJECT (renderer), "text", string, (void *) NULL);
2043   gtk_cell_renderer_get_size (renderer, GTK_WIDGET (treeview),
2044                               NULL, NULL, NULL, &width, NULL);
2045   return width;
2046 }
2047
2048 static GtkTreeViewColumn *
2049 make_data_column (struct import_assistant *ia, GtkTreeView *tree_view,
2050                   bool input, gint dict_idx)
2051 {
2052   struct variable *var = NULL;
2053   struct column *column = NULL;
2054   size_t char_cnt;
2055   gint content_width, header_width;
2056   GtkTreeViewColumn *tree_column;
2057   char *name;
2058
2059   if (input)
2060     column = &ia->separators.columns[dict_idx];
2061   else
2062     var = dict_get_var (ia->formats.dict, dict_idx);
2063
2064   name = escape_underscores (input ? column->name : var_get_name (var));
2065   char_cnt = input ? column->width : var_get_print_format (var)->w;
2066   content_width = get_monospace_width (tree_view, ia->asst.fixed_renderer,
2067                                        char_cnt);
2068   header_width = get_string_width (tree_view, ia->asst.prop_renderer,
2069                                    name);
2070
2071   tree_column = gtk_tree_view_column_new ();
2072   g_object_set_data (G_OBJECT (tree_column), "column-number",
2073                      GINT_TO_POINTER (dict_idx));
2074   gtk_tree_view_column_set_title (tree_column, name);
2075   gtk_tree_view_column_pack_start (tree_column, ia->asst.fixed_renderer,
2076                                    FALSE);
2077   gtk_tree_view_column_set_cell_data_func (
2078     tree_column, ia->asst.fixed_renderer,
2079     input ? render_input_cell : render_output_cell, ia, NULL);
2080   gtk_tree_view_column_set_sizing (tree_column, GTK_TREE_VIEW_COLUMN_FIXED);
2081   gtk_tree_view_column_set_fixed_width (tree_column, MAX (content_width,
2082                                                           header_width));
2083
2084   free (name);
2085
2086   return tree_column;
2087 }
2088
2089 static GtkTreeView *
2090 create_data_tree_view (bool input, GtkContainer *parent,
2091                        struct import_assistant *ia)
2092 {
2093   GtkTreeView *tree_view;
2094   gint i;
2095
2096   make_tree_view (ia, ia->first_line.skip_lines, &tree_view);
2097   gtk_tree_selection_set_mode (gtk_tree_view_get_selection (tree_view),
2098                                GTK_SELECTION_NONE);
2099
2100   for (i = 0; i < ia->separators.column_cnt; i++)
2101     gtk_tree_view_append_column (tree_view,
2102                                  make_data_column (ia, tree_view, input, i));
2103
2104   g_object_set (G_OBJECT (tree_view), "has-tooltip", TRUE, (void *) NULL);
2105   g_signal_connect (tree_view, "query-tooltip",
2106                     G_CALLBACK (input ? on_query_input_tooltip
2107                                 : on_query_output_tooltip), ia);
2108   gtk_tree_view_set_fixed_height_mode (tree_view, true);
2109
2110   gtk_container_add (parent, GTK_WIDGET (tree_view));
2111   gtk_widget_show (GTK_WIDGET (tree_view));
2112
2113   return tree_view;
2114 }
2115
2116 /* Increments the "watch cursor" level, setting the cursor for
2117    the assistant window to a watch face to indicate to the user
2118    that the ongoing operation may take some time. */
2119 static void
2120 push_watch_cursor (struct import_assistant *ia)
2121 {
2122   if (++ia->asst.watch_cursor == 1)
2123     {
2124       GtkWidget *widget = GTK_WIDGET (ia->asst.assistant);
2125       GdkDisplay *display = gtk_widget_get_display (widget);
2126       GdkCursor *cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
2127       gdk_window_set_cursor (widget->window, cursor);
2128       gdk_cursor_unref (cursor);
2129       gdk_display_flush (display);
2130     }
2131 }
2132
2133 /* Decrements the "watch cursor" level.  If the level reaches
2134    zero, the cursor is reset to its default shape. */
2135 static void
2136 pop_watch_cursor (struct import_assistant *ia)
2137 {
2138   if (--ia->asst.watch_cursor == 0)
2139     {
2140       GtkWidget *widget = GTK_WIDGET (ia->asst.assistant);
2141       gdk_window_set_cursor (widget->window, NULL);
2142     }
2143 }