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