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