1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2008, 2009, 2010 Free Software Foundation
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.
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.
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/>. */
23 #include "checkbox-treeview.h"
24 #include "descriptives-dialog.h"
28 #include <gtk-contrib/psppire-sheet.h>
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>
54 #define _(msgid) gettext (msgid)
55 #define N_(msgid) msgid
58 /* TextImportModel, a GtkTreeModel used by the text data import
62 TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER, /* 1-based line number in file */
63 TEXT_IMPORT_MODEL_COLUMN_LINE, /* The line from the file. */
65 typedef struct TextImportModel TextImportModel;
66 typedef struct TextImportModelClass TextImportModelClass;
68 TextImportModel *text_import_model_new (struct string *lines, size_t line_cnt,
70 gint text_import_model_iter_to_row (const GtkTreeIter *);
72 struct import_assistant;
74 /* The file to be imported. */
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)? */
81 /* The first several lines of the file. */
85 static bool init_file (struct import_assistant *, GtkWindow *parent);
86 static void destroy_file (struct import_assistant *);
88 /* The main body of the GTK+ assistant and related data. */
92 GtkAssistant *assistant;
94 GtkWidget *paste_button;
95 GtkWidget *reset_button;
99 GtkCellRenderer *prop_renderer;
100 GtkCellRenderer *fixed_renderer;
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 *,
106 GtkAssistantPageType);
108 /* The introduction page of the assistant. */
112 GtkWidget *all_cases_button;
113 GtkWidget *n_cases_button;
114 GtkWidget *n_cases_spin;
115 GtkWidget *percent_button;
116 GtkWidget *percent_spin;
118 static void init_intro_page (struct import_assistant *);
119 static void reset_intro_page (struct import_assistant *);
121 /* Page where the user chooses the first line of data. */
122 struct first_line_page
124 int skip_lines; /* Number of initial lines to skip? */
125 bool variable_names; /* Variable names above first line of data? */
128 GtkTreeView *tree_view;
129 GtkWidget *variable_names_cb;
131 static void init_first_line_page (struct import_assistant *);
132 static void reset_first_line_page (struct import_assistant *);
134 /* Page where the user chooses field separators. */
135 struct separators_page
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? */
142 /* The columns produced thereby. */
143 struct column *columns; /* Information about each column. */
144 size_t column_cnt; /* Number of columns. */
147 GtkWidget *custom_cb;
148 GtkWidget *custom_entry;
150 GtkWidget *quote_combo;
151 GtkEntry *quote_entry;
152 GtkWidget *escape_cb;
153 GtkTreeView *fields_tree_view;
155 /* The columns that the separators divide the data into. */
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. */
163 /* Maximum length of any row in this column. */
166 /* Contents of this column: contents[row] is the contents for
169 A null substring indicates a missing column for that row
170 (because the line contains an insufficient number of
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;
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 *);
184 /* Page where the user verifies and adjusts input formats. */
187 struct dictionary *dict;
190 GtkTreeView *data_tree_view;
191 PsppireDict *psppire_dict;
192 struct variable **modified_vars;
193 size_t modified_var_cnt;
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 *);
200 struct import_assistant
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;
210 static void apply_dict (const struct dictionary *, struct string *);
211 static char *generate_syntax (const struct import_assistant *);
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,
218 GtkTreeView **tree_view);
219 static void add_line_number_column (const struct import_assistant *,
221 static gint get_monospace_width (GtkTreeView *, GtkCellRenderer *,
223 static gint get_string_width (GtkTreeView *, GtkCellRenderer *,
225 static GtkTreeViewColumn *make_data_column (struct import_assistant *,
226 GtkTreeView *, bool input,
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 *);
234 /* Pops up the Text Data Import assistant. */
236 text_data_import_assistant (GtkWindow *parent_window)
238 struct import_assistant *ia;
240 ia = xzalloc (sizeof *ia);
241 if (!init_file (ia, parent_window))
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);
253 gtk_widget_show_all (GTK_WIDGET (ia->asst.assistant));
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);
259 switch (ia->asst.response)
261 case GTK_RESPONSE_APPLY:
263 char *syntax = generate_syntax (ia);
264 execute_syntax (create_syntax_string_source (syntax));
268 case PSPPIRE_RESPONSE_PASTE:
270 char *syntax = generate_syntax (ia);
271 paste_syntax_to_window (syntax);
279 destroy_formats_page (ia);
280 destroy_separators_page (ia);
281 destroy_assistant (ia);
286 /* Emits PSPP syntax to S that applies the dictionary attributes
287 (such as missing values and value labels) of the variables in
290 apply_dict (const struct dictionary *dict, struct string *s)
292 size_t var_cnt = dict_get_var_cnt (dict);
295 for (i = 0; i < var_cnt; i++)
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);
305 if (var_has_missing_values (var))
307 const struct missing_values *mv = var_get_missing_values (var);
310 syntax_gen_pspp (s, "MISSING VALUES %ss (", name);
311 for (j = 0; j < mv_n_values (mv); j++)
314 ds_put_cstr (s, ", ");
315 syntax_gen_value (s, mv_get_value (mv, j), width, format);
318 if (mv_has_range (mv))
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);
326 ds_put_cstr (s, ").\n");
328 if (var_has_value_labels (var))
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);
335 syntax_gen_pspp (s, "VALUE LABELS %ss", name);
336 for (i = 0; i < n_labels; i++)
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)));
345 ds_put_cstr (s, ".\n");
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",
353 (measure == MEASURE_NOMINAL ? "NOMINAL"
354 : measure == MEASURE_ORDINAL ? "ORDINAL"
356 if (alignment != var_default_alignment (type))
357 syntax_gen_pspp (s, "VARIABLE ALIGNMENT %ss (%ss).\n",
359 (alignment == ALIGN_LEFT ? "LEFT"
360 : alignment == ALIGN_CENTRE ? "CENTER"
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));
368 /* Generates and returns PSPP syntax to execute the import
369 operation described by IA. The caller must free the syntax
372 generate_syntax (const struct import_assistant *ia)
374 struct string s = DS_EMPTY_INITIALIZER;
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)));
394 ds_put_cstr (&s, " /IMPORTCASES=ALL\n");
396 " /ARRANGEMENT=DELIMITED\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++)
407 char c = ds_at (&ia->separators.separators, i);
409 ds_put_cstr (&s, "\"\"");
410 else if (c != '\t' && c != '\\')
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");
420 var_cnt = dict_get_var_cnt (ia->formats.dict);
421 for (i = 0; i < var_cnt; i++)
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 ? "." : "");
431 apply_dict (ia->formats.dict, &s);
436 /* Choosing a file and reading it. */
438 static char *choose_file (GtkWindow *parent_window);
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.
444 Returns true if successful, false if the file name could not
445 be obtained or the file could not be read. */
447 init_file (struct import_assistant *ia, GtkWindow *parent_window)
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. */
454 file->file_name = choose_file (parent_window);
455 if (file->file_name == NULL)
458 stream = fopen (file->file_name, "r");
461 msg (ME, _("Could not open \"%s\": %s"),
462 file->file_name, strerror (errno));
466 file->lines = xnmalloc (MAX_PREVIEW_LINES, sizeof *file->lines);
467 for (; file->line_cnt < MAX_PREVIEW_LINES; file->line_cnt++)
469 struct string *line = &file->lines[file->line_cnt];
471 ds_init_empty (line);
472 if (!ds_read_line (line, stream, MAX_LINE_LEN))
476 else if (ferror (stream))
477 msg (ME, _("Error reading \"%s\": %s"),
478 file->file_name, strerror (errno));
480 msg (ME, _("Failed to read \"%s\", because it contains a line "
481 "over %d bytes long and therefore appears not to be "
483 file->file_name, MAX_LINE_LEN);
488 ds_chomp (line, '\n');
489 ds_chomp (line, '\r');
492 if (file->line_cnt == 0)
494 msg (ME, _("\"%s\" is empty."), file->file_name);
500 /* Estimate the number of lines in the file. */
501 if (file->line_cnt < MAX_PREVIEW_LINES)
502 file->total_lines = file->line_cnt;
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;
510 file->total_lines = 0;
516 /* Frees IA's file substructure. */
518 destroy_file (struct import_assistant *ia)
520 struct file *f = &ia->file;
523 for (i = 0; i < f->line_cnt; i++)
524 ds_destroy (&f->lines[i]);
526 g_free (f->file_name);
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. */
534 choose_file (GtkWindow *parent_window)
539 dialog = gtk_file_chooser_dialog_new (_("Import Delimited Text Data"),
541 GTK_FILE_CHOOSER_ACTION_OPEN,
542 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
543 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
546 switch (gtk_dialog_run (GTK_DIALOG (dialog)))
548 case GTK_RESPONSE_ACCEPT:
549 file_name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
555 gtk_widget_destroy (dialog);
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);
571 /* Initializes IA's asst substructure. PARENT_WINDOW must be the
572 window to use as the assistant window's parent. */
574 init_assistant (struct import_assistant *ia, GtkWindow *parent_window)
576 struct assistant *a = &ia->asst;
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");
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",
603 /* Frees IA's asst substructure. */
605 destroy_assistant (struct import_assistant *ia)
607 struct assistant *a = &ia->asst;
609 g_object_unref (a->prop_renderer);
610 g_object_unref (a->fixed_renderer);
611 g_object_unref (a->builder);
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. */
618 add_page_to_assistant (struct import_assistant *ia,
619 GtkWidget *page, GtkAssistantPageType type)
625 title = gtk_window_get_title (GTK_WINDOW (page));
626 title_copy = xstrdup (title ? title : "");
628 content = gtk_bin_get_child (GTK_BIN (page));
630 g_object_ref (content);
631 gtk_container_remove (GTK_CONTAINER (page), content);
633 gtk_widget_destroy (page);
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);
645 /* Called just before PAGE is displayed as the current page of
646 ASSISTANT, this updates IA content according to the new
649 on_prepare (GtkAssistant *assistant, GtkWidget *page,
650 struct import_assistant *ia)
653 if (gtk_assistant_get_page_type (assistant, page)
654 == GTK_ASSISTANT_PAGE_CONFIRM)
655 gtk_widget_grab_focus (assistant->apply);
657 gtk_widget_grab_focus (assistant->forward);
659 if (page == ia->separators.page)
660 prepare_separators_page (ia);
661 else if (page == ia->formats.page)
662 prepare_formats_page (ia);
664 gtk_widget_show (ia->asst.reset_button);
665 if (page == ia->formats.page)
666 gtk_widget_show (ia->asst.paste_button);
668 gtk_widget_hide (ia->asst.paste_button);
671 /* Called when the Cancel button in the assistant is clicked. */
673 on_cancel (GtkAssistant *assistant, struct import_assistant *ia)
675 close_assistant (ia, GTK_RESPONSE_CANCEL);
678 /* Called when the Apply button on the last page of the assistant
681 on_close (GtkAssistant *assistant, struct import_assistant *ia)
683 close_assistant (ia, GTK_RESPONSE_APPLY);
686 /* Called when the Paste button on the last page of the assistant
689 on_paste (GtkButton *button, struct import_assistant *ia)
691 close_assistant (ia, PSPPIRE_RESPONSE_PASTE);
694 /* Called when the Reset button is clicked. */
696 on_reset (GtkButton *button, struct import_assistant *ia)
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);
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);
711 /* Causes the assistant to close, returning RESPONSE for
712 interpretation by text_data_import_assistant. */
714 close_assistant (struct import_assistant *ia, int response)
716 ia->asst.response = response;
717 g_main_loop_quit (ia->asst.main_loop);
718 gtk_widget_hide (GTK_WIDGET (ia->asst.assistant));
721 /* The "intro" page of the assistant. */
723 static void on_intro_amount_changed (GtkToggleButton *button,
724 struct import_assistant *);
726 /* Initializes IA's intro substructure. */
728 init_intro_page (struct import_assistant *ia)
730 GtkBuilder *builder = ia->asst.builder;
731 struct intro_page *p = &ia->intro;
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);
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)
755 &s, ngettext ("The selected file contains %zu line of text. ",
756 "The selected file contains %zu lines of text. ",
759 else if (ia->file.total_lines > 0)
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);
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. ",
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")),
783 /* Resets IA's intro page to its initial state. */
785 reset_intro_page (struct import_assistant *ia)
787 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->intro.all_cases_button),
791 /* Called when one of the radio buttons is clicked. */
793 on_intro_amount_changed (GtkToggleButton *button UNUSED,
794 struct import_assistant *ia)
796 struct intro_page *p = &ia->intro;
798 gtk_widget_set_sensitive (p->n_cases_spin,
799 gtk_toggle_button_get_active (
800 GTK_TOGGLE_BUTTON (p->n_cases_button)));
802 gtk_widget_set_sensitive (ia->intro.percent_spin,
803 gtk_toggle_button_get_active (
804 GTK_TOGGLE_BUTTON (p->percent_button)));
807 /* The "first line" page of the assistant. */
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 *);
818 /* Initializes IA's first_line substructure. */
820 init_first_line_page (struct import_assistant *ia)
822 struct first_line_page *p = &ia->first_line;
823 GtkBuilder *builder = ia->asst.builder;
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);
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);
841 /* Resets the first_line page to its initial content. */
843 reset_first_line_page (struct import_assistant *ia)
845 ia->first_line.skip_lines = 0;
846 ia->first_line.variable_names = false;
850 /* Creates and returns a tree view that contains each of the
851 lines in IA's file as a row. */
853 create_lines_tree_view (GtkContainer *parent, struct import_assistant *ia)
855 GtkTreeView *tree_view;
856 GtkTreeViewColumn *column;
857 size_t max_line_length;
858 gint content_width, header_width;
861 make_tree_view (ia, 0, &tree_view);
863 column = gtk_tree_view_column_new_with_attributes (
864 "Text", ia->asst.fixed_renderer,
865 "text", TEXT_IMPORT_MODEL_COLUMN_LINE,
867 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
870 for (i = 0; i < ia->file.line_cnt; i++)
872 size_t w = ds_length (&ia->file.lines[i]);
873 max_line_length = MAX (max_line_length, w);
876 content_width = get_monospace_width (tree_view, ia->asst.fixed_renderer,
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,
881 gtk_tree_view_append_column (tree_view, column);
883 gtk_tree_view_set_fixed_height_mode (tree_view, true);
885 gtk_container_add (parent, GTK_WIDGET (tree_view));
886 gtk_widget_show (GTK_WIDGET (tree_view));
891 /* Called when the line selected in the first_line tree view
894 on_first_line_change (GtkTreeSelection *selection UNUSED,
895 struct import_assistant *ia)
900 /* Called when the checkbox that indicates whether variable
901 names are in the row above the first line is toggled. */
903 on_variable_names_cb_toggle (GtkToggleButton *variable_names_cb UNUSED,
904 struct import_assistant *ia)
909 /* Sets the widgets to match IA's first_line substructure. */
911 set_first_line (struct import_assistant *ia)
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),
918 gtk_tree_path_free (path);
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);
927 /* Sets IA's first_line substructure to match the widgets. */
929 get_first_line (struct import_assistant *ia)
931 GtkTreeSelection *selection;
935 selection = gtk_tree_view_get_selection (ia->first_line.tree_view);
936 if (gtk_tree_selection_get_selected (selection, &model, &iter))
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);
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)));
948 gtk_widget_set_sensitive (ia->first_line.variable_names_cb,
949 ia->first_line.skip_lines > 0);
952 /* The "separators" page of the assistant. */
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,
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,
977 static gboolean on_query_input_tooltip (GtkWidget *widget, gint wx, gint wy,
978 gboolean keyboard_mode UNUSED,
980 struct import_assistant *);
982 /* A common field separator and its identifying name. */
985 const char *name; /* Name (for use with get_widget_assert). */
986 int c; /* Separator character. */
989 /* All the separators in the dialog box. */
990 static const struct separator separators[] =
1002 #define SEPARATOR_CNT (sizeof separators / sizeof *separators)
1005 set_quote_list (GtkComboBoxEntry *cb)
1007 GtkListStore *list = gtk_list_store_new (1, G_TYPE_STRING);
1010 const gchar *seperator[3] = {"'\"", "\'", "\""};
1012 for (i = 0; i < 3; i++)
1014 const gchar *s = seperator[i];
1016 /* Add a new row to the model */
1017 gtk_list_store_append (list, &iter);
1018 gtk_list_store_set (list, &iter,
1024 gtk_combo_box_set_model (GTK_COMBO_BOX (cb), GTK_TREE_MODEL (list));
1026 gtk_combo_box_entry_set_text_column (cb, 0);
1029 /* Initializes IA's separators substructure. */
1031 init_separators_page (struct import_assistant *ia)
1033 GtkBuilder *builder = ia->asst.builder;
1034 struct separators_page *p = &ia->separators;
1037 choose_likely_separators (ia);
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");
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);
1066 /* Frees IA's separators substructure. */
1068 destroy_separators_page (struct import_assistant *ia)
1070 struct separators_page *s = &ia->separators;
1072 ds_destroy (&s->separators);
1073 ds_destroy (&s->quotes);
1077 /* Called just before the separators page becomes visible in the
1080 prepare_separators_page (struct import_assistant *ia)
1082 revise_fields_preview (ia);
1085 /* Called when the Reset button is clicked on the separators
1086 page, resets the separators to the defaults. */
1088 reset_separators_page (struct import_assistant *ia)
1090 choose_likely_separators (ia);
1091 set_separators (ia);
1094 /* Frees and clears the column data in IA's separators
1097 clear_fields (struct import_assistant *ia)
1099 struct separators_page *s = &ia->separators;
1101 if (s->column_cnt > 0)
1106 for (row = 0; row < ia->file.line_cnt; row++)
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);
1112 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1114 char *s = ss_data (col->contents[row]);
1115 if (!(s >= line_start && s <= line_end))
1116 ss_dealloc (&col->contents[row]);
1120 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1123 free (col->contents);
1132 /* Breaks the file data in IA into columns based on the
1133 separators set in IA's separators substructure. */
1135 split_fields (struct import_assistant *ia)
1137 struct separators_page *s = &ia->separators;
1138 size_t columns_allocated;
1144 /* Is space in the set of separators? */
1145 space_sep = ss_find_char (ds_ss (&s->separators), ' ') != SIZE_MAX;
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
1151 columns_allocated = 0;
1152 for (row = 0; row < ia->file.line_cnt; row++)
1154 struct string *line = &ia->file.lines[row];
1155 struct substring text = ds_ss (line);
1158 for (column_idx = 0; ; column_idx++)
1160 struct substring field;
1161 struct column *column;
1164 ss_ltrim (&text, ss_cstr (" "));
1165 if (ss_is_empty (text))
1167 if (column_idx != 0)
1171 else if (!ds_is_empty (&s->quotes)
1172 && ds_find_char (&s->quotes, text.string[0]) != SIZE_MAX)
1174 int quote = ss_get_char (&text);
1176 ss_get_until (&text, quote, &field);
1183 while ((c = ss_get_char (&text)) != EOF)
1185 ds_put_char (&s, c);
1186 else if (ss_match_char (&text, quote))
1187 ds_put_char (&s, quote);
1194 ss_get_chars (&text, ss_cspan (text, ds_ss (&s->separators)),
1197 if (column_idx >= s->column_cnt)
1199 struct column *column;
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;
1207 column->contents = xcalloc (ia->file.line_cnt,
1208 sizeof *column->contents);
1210 column = &s->columns[column_idx];
1211 column->contents[row] = field;
1212 if (ss_length (field) > column->width)
1213 column->width = ss_length (field);
1216 ss_ltrim (&text, ss_cstr (" "));
1217 if (ss_is_empty (text))
1219 if (ss_find_char (ds_ss (&s->separators), ss_first (text))
1221 ss_advance (&text, 1);
1226 /* Chooses a name for each column on the separators page */
1228 choose_column_names (struct import_assistant *ia)
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;
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++)
1241 char name[VAR_NAME_LEN + 1];
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))
1249 col->name = xstrdup (name);
1250 dict_create_var_assert (dict, name, 0);
1252 dict_destroy (dict);
1255 /* Picks the most likely separator and quote characters based on
1258 choose_likely_separators (struct import_assistant *ia)
1260 unsigned long int histogram[UCHAR_MAX + 1] = { 0 };
1263 /* Construct a histogram of all the characters used in the
1265 for (row = 0; row < ia->file.line_cnt; row++)
1267 struct substring line = ds_ss (&ia->file.lines[row]);
1268 size_t length = ss_length (line);
1270 for (i = 0; i < length; i++)
1271 histogram[(unsigned char) line.string[i]]++;
1274 find_commonest_chars (histogram, "\"'", "", &ia->separators.quotes);
1275 find_commonest_chars (histogram, ",;:/|!\t-", ",",
1276 &ia->separators.separators);
1277 ia->separators.escape = true;
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
1287 find_commonest_chars (unsigned long int histogram[UCHAR_MAX + 1],
1288 const char *targets, const char *def,
1289 struct string *result)
1291 unsigned char max = 0;
1292 unsigned long int max_count = 0;
1294 for (; *targets != '\0'; targets++)
1296 unsigned char c = *targets;
1297 unsigned long int count = histogram[c];
1298 if (count > max_count)
1307 ds_put_char (result, max);
1310 ds_assign_cstr (result, def);
1313 /* Revises the contents of the fields tree view based on the
1314 currently chosen set of separators. */
1316 revise_fields_preview (struct import_assistant *ia)
1320 push_watch_cursor (ia);
1322 w = GTK_WIDGET (ia->separators.fields_tree_view);
1323 gtk_widget_destroy (w);
1324 get_separators (ia);
1326 choose_column_names (ia);
1327 ia->separators.fields_tree_view = create_data_tree_view (
1329 GTK_CONTAINER (get_widget_assert (ia->asst.builder, "fields-scroller")),
1332 pop_watch_cursor (ia);
1335 /* Sets the widgets to match IA's separators substructure. */
1337 set_separators (struct import_assistant *ia)
1339 struct separators_page *s = &ia->separators;
1341 struct string custom;
1346 ds_init_empty (&custom);
1348 for (i = 0; i < ds_length (&s->separators); i++)
1350 unsigned char c = ds_at (&s->separators, i);
1353 for (j = 0; j < SEPARATOR_CNT; j++)
1355 const struct separator *s = &separators[j];
1363 ds_put_char (&custom, c);
1367 for (i = 0; i < SEPARATOR_CNT; i++)
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);
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),
1378 gtk_widget_set_sensitive (s->custom_entry, any_custom);
1379 ds_destroy (&custom);
1381 any_quotes = !ds_is_empty (&s->quotes);
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),
1387 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->escape_cb),
1389 gtk_widget_set_sensitive (s->quote_combo, any_quotes);
1390 gtk_widget_set_sensitive (s->escape_cb, any_quotes);
1393 /* Sets IA's separators substructure to match the widgets. */
1395 get_separators (struct import_assistant *ia)
1397 struct separators_page *s = &ia->separators;
1400 ds_clear (&s->separators);
1401 for (i = 0; i < SEPARATOR_CNT; i++)
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);
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)));
1413 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->quote_cb)))
1415 gchar *text = gtk_combo_box_get_active_text (
1416 GTK_COMBO_BOX (s->quote_combo));
1417 ds_assign_cstr (&s->quotes, text);
1421 ds_clear (&s->quotes);
1422 s->escape = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->escape_cb));
1425 /* Called when the user changes the entry field for custom
1428 on_separators_custom_entry_notify (GObject *gobject UNUSED,
1429 GParamSpec *arg1 UNUSED,
1430 struct import_assistant *ia)
1432 revise_fields_preview (ia);
1435 /* Called when the user toggles the checkbox that enables custom
1438 on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
1439 struct import_assistant *ia)
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);
1446 /* Called when the user changes the selection in the combo box
1447 that selects a quote character. */
1449 on_quote_combo_change (GtkComboBox *combo, struct import_assistant *ia)
1451 revise_fields_preview (ia);
1454 /* Called when the user toggles the checkbox that enables
1457 on_quote_cb_toggle (GtkToggleButton *quote_cb, struct import_assistant *ia)
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);
1465 /* Called when the user toggles one of the separators
1468 on_separator_toggle (GtkToggleButton *toggle UNUSED,
1469 struct import_assistant *ia)
1471 revise_fields_preview (ia);
1474 /* Called to render one of the cells in the fields preview tree
1477 render_input_cell (GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1478 GtkTreeModel *model, GtkTreeIter *iter,
1481 struct import_assistant *ia = ia_;
1482 struct substring field;
1486 column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
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)
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);
1502 "background", "red",
1503 "background-set", TRUE,
1507 /* Called to render a tooltip on one of the cells in the fields
1508 preview tree view. */
1510 on_query_input_tooltip (GtkWidget *widget, gint wx, gint wy,
1511 gboolean keyboard_mode UNUSED,
1512 GtkTooltip *tooltip, struct import_assistant *ia)
1516 if (!get_tooltip_location (widget, wx, wy, ia, &row, &column))
1519 if (ia->separators.columns[column].contents[row].string != NULL)
1522 gtk_tooltip_set_text (tooltip,
1523 _("This input line has too few separators "
1524 "to fill in this field."));
1528 /* The "formats" page of the assistant. */
1530 static void on_variable_change (PsppireDict *dict, int idx,
1531 struct import_assistant *);
1532 static void clear_modified_vars (struct import_assistant *);
1534 /* Initializes IA's formats substructure. */
1536 init_formats_page (struct import_assistant *ia)
1538 GtkBuilder *builder = ia->asst.builder;
1539 struct formats_page *p = &ia->formats;
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;
1549 /* Frees IA's formats substructure. */
1551 destroy_formats_page (struct import_assistant *ia)
1553 struct formats_page *p = &ia->formats;
1555 if (p->psppire_dict != NULL)
1557 /* This destroys p->dict also. */
1558 g_object_unref (p->psppire_dict);
1560 clear_modified_vars (ia);
1563 /* Called just before the formats page of the assistant is
1566 prepare_formats_page (struct import_assistant *ia)
1568 struct dictionary *dict;
1569 PsppireDict *psppire_dict;
1570 PsppireVarStore *var_store;
1571 GtkBin *vars_scroller;
1572 GtkWidget *old_var_sheet;
1573 PsppireVarSheet *var_sheet;
1574 struct separators_page *s = &ia->separators;
1575 struct formats_page *p = &ia->formats;
1576 struct fmt_guesser *fg;
1577 unsigned long int number = 0;
1580 push_watch_cursor (ia);
1582 dict = dict_create ();
1583 fg = fmt_guesser_create ();
1584 for (column_idx = 0; column_idx < s->column_cnt; column_idx++)
1586 struct variable *modified_var;
1587 char name[VAR_NAME_LEN + 1];
1589 modified_var = (column_idx < p->modified_var_cnt
1590 ? p->modified_vars[column_idx] : NULL);
1591 if (modified_var == NULL)
1593 struct column *column = &s->columns[column_idx];
1594 struct variable *var;
1595 struct fmt_spec format;
1598 /* Choose variable name. */
1599 if (!dict_make_unique_var_name (dict, column->name, &number, name))
1602 /* Choose variable format. */
1603 fmt_guesser_clear (fg);
1604 for (row = ia->first_line.skip_lines; row < ia->file.line_cnt; row++)
1605 fmt_guesser_add (fg, column->contents[row]);
1606 fmt_guesser_guess (fg, &format);
1607 fmt_fix_input (&format);
1609 /* Create variable. */
1610 var = dict_create_var_assert (dict, name, fmt_var_width (&format));
1611 var_set_both_formats (var, &format);
1615 if (!dict_make_unique_var_name (dict, var_get_name (modified_var),
1618 dict_clone_var_as_assert (dict, modified_var, name);
1621 fmt_guesser_destroy (fg);
1623 psppire_dict = psppire_dict_new_from_dict (dict);
1624 g_signal_connect (psppire_dict, "variable_changed",
1625 G_CALLBACK (on_variable_change), ia);
1626 ia->formats.dict = dict;
1627 ia->formats.psppire_dict = psppire_dict;
1629 /* XXX: PsppireVarStore doesn't hold a reference to
1630 psppire_dict for now, but it should. After it does, we
1631 should g_object_ref the psppire_dict here, since we also
1632 hold a reference via ia->formats.dict. */
1633 var_store = psppire_var_store_new (psppire_dict);
1634 g_object_set (var_store,
1635 "format-type", PSPPIRE_VAR_STORE_INPUT_FORMATS,
1637 var_sheet = PSPPIRE_VAR_SHEET (psppire_var_sheet_new ());
1638 g_object_set (var_sheet,
1640 "may-create-vars", FALSE,
1643 vars_scroller = GTK_BIN (get_widget_assert (ia->asst.builder, "vars-scroller"));
1644 old_var_sheet = gtk_bin_get_child (vars_scroller);
1645 if (old_var_sheet != NULL)
1646 gtk_widget_destroy (old_var_sheet);
1647 gtk_container_add (GTK_CONTAINER (vars_scroller), GTK_WIDGET (var_sheet));
1648 gtk_widget_show (GTK_WIDGET (var_sheet));
1650 gtk_widget_destroy (GTK_WIDGET (ia->formats.data_tree_view));
1651 ia->formats.data_tree_view = create_data_tree_view (
1653 GTK_CONTAINER (get_widget_assert (ia->asst.builder, "data-scroller")),
1656 pop_watch_cursor (ia);
1659 /* Clears the set of user-modified variables from IA's formats
1660 substructure. This discards user modifications to variable
1661 formats, thereby causing formats to revert to their
1664 clear_modified_vars (struct import_assistant *ia)
1666 struct formats_page *p = &ia->formats;
1669 for (i = 0; i < p->modified_var_cnt; i++)
1670 var_destroy (p->modified_vars[i]);
1671 free (p->modified_vars);
1672 p->modified_vars = NULL;
1673 p->modified_var_cnt = 0;
1676 /* Resets the formats page to its defaults, discarding user
1679 reset_formats_page (struct import_assistant *ia)
1681 clear_modified_vars (ia);
1682 prepare_formats_page (ia);
1685 /* Called when the user changes one of the variables in the
1688 on_variable_change (PsppireDict *dict, int dict_idx,
1689 struct import_assistant *ia)
1691 struct formats_page *p = &ia->formats;
1692 GtkTreeView *tv = ia->formats.data_tree_view;
1693 gint column_idx = dict_idx + 1;
1695 push_watch_cursor (ia);
1697 /* Remove previous column and replace with new column. */
1698 gtk_tree_view_remove_column (tv, gtk_tree_view_get_column (tv, column_idx));
1699 gtk_tree_view_insert_column (tv, make_data_column (ia, tv, false, dict_idx),
1702 /* Save a copy of the modified variable in modified_vars, so
1703 that its attributes will be preserved if we back up to the
1704 previous page with the Prev button and then come back
1706 if (dict_idx >= p->modified_var_cnt)
1709 p->modified_vars = xnrealloc (p->modified_vars, dict_idx + 1,
1710 sizeof *p->modified_vars);
1711 for (i = 0; i <= dict_idx; i++)
1712 p->modified_vars[i] = NULL;
1713 p->modified_var_cnt = dict_idx + 1;
1715 if (p->modified_vars[dict_idx])
1716 var_destroy (p->modified_vars[dict_idx]);
1717 p->modified_vars[dict_idx]
1718 = var_clone (psppire_dict_get_variable (dict, dict_idx));
1720 pop_watch_cursor (ia);
1723 /* Parses the contents of the field at (ROW,COLUMN) according to
1724 its variable format. If OUTPUTP is non-null, then *OUTPUTP
1725 receives the formatted output for that field (which must be
1726 freed with free). If TOOLTIPP is non-null, then *TOOLTIPP
1727 receives a message suitable for use in a tooltip, if one is
1728 needed, or a null pointer otherwise. Returns true if a
1729 tooltip message is needed, otherwise false. */
1731 parse_field (struct import_assistant *ia,
1732 size_t row, size_t column,
1733 char **outputp, char **tooltipp)
1735 struct substring field;
1737 struct variable *var;
1738 const struct fmt_spec *in;
1739 struct fmt_spec out;
1743 field = ia->separators.columns[column].contents[row];
1744 var = dict_get_var (ia->formats.dict, column);
1745 value_init (&val, var_get_width (var));
1746 in = var_get_print_format (var);
1747 out = fmt_for_output_from_input (in);
1749 if (field.string != NULL)
1753 if (!data_in (field, LEGACY_NATIVE, in->type, 0, 0, 0,
1755 &val, var_get_width (var)))
1757 char fmt_string[FMT_STRING_LEN_MAX + 1];
1758 fmt_to_string (in, fmt_string);
1759 tooltip = xasprintf (_("Field content \"%.*s\" cannot be parsed in "
1761 (int) field.length, field.string,
1768 tooltip = xstrdup (_("This input line has too few separators "
1769 "to fill in this field."));
1770 value_set_missing (&val, var_get_width (var));
1772 if (outputp != NULL)
1774 *outputp = data_out (&val, dict_get_encoding (ia->formats.dict), &out);
1776 value_destroy (&val, var_get_width (var));
1778 ok = tooltip == NULL;
1779 if (tooltipp != NULL)
1780 *tooltipp = tooltip;
1786 /* Called to render one of the cells in the data preview tree
1789 render_output_cell (GtkTreeViewColumn *tree_column,
1790 GtkCellRenderer *cell,
1791 GtkTreeModel *model,
1795 struct import_assistant *ia = ia_;
1797 GValue gvalue = { 0, };
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),
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);
1813 g_object_set (cell, "background-set", FALSE, (void *) NULL);
1816 "background", "red",
1817 "background-set", TRUE,
1821 /* Called to render a tooltip for one of the cells in the data
1822 preview tree view. */
1824 on_query_output_tooltip (GtkWidget *widget, gint wx, gint wy,
1825 gboolean keyboard_mode UNUSED,
1826 GtkTooltip *tooltip, struct import_assistant *ia)
1831 if (!get_tooltip_location (widget, wx, wy, ia, &row, &column))
1834 if (parse_field (ia, row, column, NULL, &text))
1837 gtk_tooltip_set_text (tooltip, text);
1842 /* Utility functions used by multiple pages of the assistant. */
1845 get_tooltip_location (GtkWidget *widget, gint wx, gint wy,
1846 const struct import_assistant *ia,
1847 size_t *row, size_t *column)
1849 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
1853 GtkTreeViewColumn *tree_column;
1854 GtkTreeModel *tree_model;
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))
1870 gtk_tree_view_convert_widget_to_bin_window_coords (tree_view,
1872 if (!gtk_tree_view_get_path_at_pos (tree_view, bx, by,
1873 &path, &tree_column, NULL, NULL))
1876 *column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
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);
1885 *row = text_import_model_iter_to_row (&iter) + ia->first_line.skip_lines;
1890 make_tree_view (const struct import_assistant *ia,
1892 GtkTreeView **tree_view)
1894 GtkTreeModel *model;
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);
1902 add_line_number_column (ia, *tree_view);
1906 add_line_number_column (const struct import_assistant *ia,
1907 GtkTreeView *treeview)
1909 GtkTreeViewColumn *column;
1911 column = gtk_tree_view_column_new_with_attributes (
1912 "Line", ia->asst.prop_renderer,
1913 "text", TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER,
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);
1922 get_monospace_width (GtkTreeView *treeview, GtkCellRenderer *renderer,
1929 ds_put_char_multiple (&s, '0', char_cnt);
1930 ds_put_char (&s, ' ');
1931 width = get_string_width (treeview, renderer, ds_cstr (&s));
1938 get_string_width (GtkTreeView *treeview, GtkCellRenderer *renderer,
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);
1948 static GtkTreeViewColumn *
1949 make_data_column (struct import_assistant *ia, GtkTreeView *tree_view,
1950 bool input, gint dict_idx)
1952 struct variable *var = NULL;
1953 struct column *column = NULL;
1954 char name[(VAR_NAME_LEN * 2) + 1];
1956 gint content_width, header_width;
1957 GtkTreeViewColumn *tree_column;
1960 column = &ia->separators.columns[dict_idx];
1962 var = dict_get_var (ia->formats.dict, dict_idx);
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,
1968 header_width = get_string_width (tree_view, ia->asst.prop_renderer,
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,
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,
1987 static GtkTreeView *
1988 create_data_tree_view (bool input, GtkContainer *parent,
1989 struct import_assistant *ia)
1991 GtkTreeView *tree_view;
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);
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));
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);
2008 gtk_container_add (parent, GTK_WIDGET (tree_view));
2009 gtk_widget_show (GTK_WIDGET (tree_view));
2015 escape_underscores (const char *in, char *out)
2017 for (; *in != '\0'; in++)
2026 /* TextImportModel, a GtkTreeModel implementation used by some
2027 pages of the assistant. */
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))
2036 /* Random number used in 'stamp' member of GtkTreeIter. */
2037 #define TREE_MODEL_STAMP 0x7efd67d3
2039 struct TextImportModel
2042 struct string *lines;
2047 struct TextImportModelClass
2049 GObjectClass parent_class;
2052 GType text_import_model_get_type (void);
2053 static void text_import_model_tree_model_init (gpointer iface, gpointer data);
2056 text_import_model_get_type (void)
2058 static GType object_type = 0;
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 */
2074 static const GInterfaceInfo tree_model_info = {
2075 text_import_model_tree_model_init,
2080 object_type = g_type_register_static (G_TYPE_OBJECT,
2084 g_type_add_interface_static (object_type, GTK_TYPE_TREE_MODEL,
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.
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. */
2103 text_import_model_new (struct string *lines, size_t line_cnt,
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;
2116 tree_model_iter_has_child (GtkTreeModel *tree_model,
2123 tree_model_iter_parent (GtkTreeModel *tree_model,
2130 static GtkTreeModelFlags
2131 tree_model_get_flags (GtkTreeModel *model)
2133 g_return_val_if_fail (IS_TEXT_IMPORT_MODEL (model), (GtkTreeModelFlags) 0);
2135 return GTK_TREE_MODEL_LIST_ONLY | GTK_TREE_MODEL_ITERS_PERSIST;
2140 tree_model_n_columns (GtkTreeModel *model)
2146 tree_model_column_type (GtkTreeModel *model, gint index)
2148 return (index == TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER ? G_TYPE_INT
2149 : index == TEXT_IMPORT_MODEL_COLUMN_LINE ? G_TYPE_STRING
2154 init_iter (TextImportModel *list, gint idx, GtkTreeIter *iter)
2156 if (idx < 0 || idx >= list->line_cnt)
2159 iter->user_data = GINT_TO_POINTER (-1);
2164 iter->stamp = TREE_MODEL_STAMP;
2165 iter->user_data = GINT_TO_POINTER (idx);
2171 tree_model_get_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreePath *path)
2173 gint *indices, depth;
2175 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2177 g_return_val_if_fail (path, FALSE);
2179 indices = gtk_tree_path_get_indices (path);
2180 depth = gtk_tree_path_get_depth (path);
2182 g_return_val_if_fail (depth == 1, FALSE);
2184 return init_iter (list, indices[0], iter);
2189 tree_model_iter_next (GtkTreeModel *model, GtkTreeIter *iter)
2191 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2194 assert (iter->stamp == TREE_MODEL_STAMP);
2196 idx = GPOINTER_TO_INT (iter->user_data);
2197 return init_iter (list, idx == -1 ? -1 : idx + 1, iter);
2200 static GtkTreePath *
2201 tree_model_get_path (GtkTreeModel *model, GtkTreeIter *iter)
2205 g_return_val_if_fail (iter->stamp == TREE_MODEL_STAMP, FALSE);
2207 path = gtk_tree_path_new ();
2208 gtk_tree_path_append_index (path, GPOINTER_TO_INT (iter->user_data));
2214 tree_model_get_value (GtkTreeModel *model, GtkTreeIter *iter,
2215 gint column, GValue *value)
2217 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2220 g_return_if_fail (iter->stamp == TREE_MODEL_STAMP);
2222 idx = GPOINTER_TO_INT (iter->user_data);
2223 assert (idx >= 0 && idx < list->line_cnt);
2227 g_value_init (value, G_TYPE_INT);
2228 g_value_set_int (value, idx + list->first_line + 1);
2232 g_value_init (value, G_TYPE_STRING);
2233 g_value_set_static_string (value, ds_cstr (&list->lines[idx]));
2238 tree_model_iter_children (GtkTreeModel *tree_model,
2240 GtkTreeIter *parent)
2246 tree_model_n_children (GtkTreeModel *model, GtkTreeIter *iter)
2248 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2250 return iter == NULL ? list->line_cnt : 0;
2254 tree_model_nth_child (GtkTreeModel *model, GtkTreeIter *iter,
2255 GtkTreeIter *parent, gint n)
2257 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2258 g_return_val_if_fail (IS_TEXT_IMPORT_MODEL (model), FALSE);
2262 return init_iter (list, n, iter);
2266 text_import_model_tree_model_init (gpointer iface_, gpointer data UNUSED)
2268 GtkTreeModelIface *iface = (GtkTreeModelIface *) iface_;
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;
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;
2286 text_import_model_iter_to_row (const GtkTreeIter *iter)
2288 assert (iter->stamp == TREE_MODEL_STAMP);
2289 return GPOINTER_TO_INT (iter->user_data);
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. */
2296 push_watch_cursor (struct import_assistant *ia)
2298 if (++ia->asst.watch_cursor == 1)
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);
2309 /* Decrements the "watch cursor" level. If the level reaches
2310 zero, the cursor is reset to its default shape. */
2312 pop_watch_cursor (struct import_assistant *ia)
2314 if (--ia->asst.watch_cursor == 0)
2316 GtkWidget *widget = GTK_WIDGET (ia->asst.assistant);
2317 gdk_window_set_cursor (widget->window, NULL);