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