1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2008, 2009, 2010, 2011, 2012 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/>. */
19 #include "ui/gui/text-data-import-dialog.h"
22 #include <gtk-contrib/psppire-sheet.h>
28 #include "data/data-in.h"
29 #include "data/data-out.h"
30 #include "data/format-guesser.h"
31 #include "data/value-labels.h"
32 #include "language/data-io/data-parser.h"
33 #include "language/lexer/lexer.h"
34 #include "libpspp/assertion.h"
35 #include "libpspp/i18n.h"
36 #include "libpspp/message.h"
37 #include "ui/gui/checkbox-treeview.h"
38 #include "ui/gui/descriptives-dialog.h"
39 #include "ui/gui/dialog-common.h"
40 #include "ui/gui/executor.h"
41 #include "ui/gui/helper.h"
42 #include "ui/gui/builder-wrapper.h"
43 #include "ui/gui/psppire-data-window.h"
44 #include "ui/gui/psppire-dialog.h"
45 #include "ui/gui/psppire-var-sheet.h"
46 #include "ui/gui/psppire-var-store.h"
47 #include "ui/gui/psppire-scanf.h"
48 #include "ui/syntax-gen.h"
51 #include "gl/xalloc.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 char *escape_underscores (const char *in);
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 (PsppireDataWindow *dw)
238 GtkWindow *parent_window = GTK_WINDOW (dw);
239 struct import_assistant *ia;
241 ia = xzalloc (sizeof *ia);
242 if (!init_file (ia, parent_window))
248 init_assistant (ia, parent_window);
249 init_intro_page (ia);
250 init_first_line_page (ia);
251 init_separators_page (ia);
252 init_formats_page (ia);
254 gtk_widget_show_all (GTK_WIDGET (ia->asst.assistant));
256 ia->asst.main_loop = g_main_loop_new (NULL, false);
257 g_main_loop_run (ia->asst.main_loop);
258 g_main_loop_unref (ia->asst.main_loop);
260 switch (ia->asst.response)
262 case GTK_RESPONSE_APPLY:
263 free (execute_syntax_string (dw, generate_syntax (ia)));
265 case PSPPIRE_RESPONSE_PASTE:
266 free (paste_syntax_to_window (generate_syntax (ia)));
272 destroy_formats_page (ia);
273 destroy_separators_page (ia);
274 destroy_assistant (ia);
279 /* Emits PSPP syntax to S that applies the dictionary attributes
280 (such as missing values and value labels) of the variables in
283 apply_dict (const struct dictionary *dict, struct string *s)
285 size_t var_cnt = dict_get_var_cnt (dict);
288 for (i = 0; i < var_cnt; i++)
290 struct variable *var = dict_get_var (dict, i);
291 const char *name = var_get_name (var);
292 enum val_type type = var_get_type (var);
293 int width = var_get_width (var);
294 enum measure measure = var_get_measure (var);
295 enum alignment alignment = var_get_alignment (var);
296 const struct fmt_spec *format = var_get_print_format (var);
298 if (var_has_missing_values (var))
300 const struct missing_values *mv = var_get_missing_values (var);
303 syntax_gen_pspp (s, "MISSING VALUES %ss (", name);
304 for (j = 0; j < mv_n_values (mv); j++)
307 ds_put_cstr (s, ", ");
308 syntax_gen_value (s, mv_get_value (mv, j), width, format);
311 if (mv_has_range (mv))
314 if (mv_has_value (mv))
315 ds_put_cstr (s, ", ");
316 mv_get_range (mv, &low, &high);
317 syntax_gen_num_range (s, low, high, format);
319 ds_put_cstr (s, ").\n");
321 if (var_has_value_labels (var))
323 const struct val_labs *vls = var_get_value_labels (var);
324 const struct val_lab **labels = val_labs_sorted (vls);
325 size_t n_labels = val_labs_count (vls);
328 syntax_gen_pspp (s, "VALUE LABELS %ss", name);
329 for (i = 0; i < n_labels; i++)
331 const struct val_lab *vl = labels[i];
332 ds_put_cstr (s, "\n ");
333 syntax_gen_value (s, &vl->value, width, format);
334 ds_put_byte (s, ' ');
335 syntax_gen_string (s, ss_cstr (val_lab_get_escaped_label (vl)));
338 ds_put_cstr (s, ".\n");
340 if (var_has_label (var))
341 syntax_gen_pspp (s, "VARIABLE LABELS %ss %sq.\n",
342 name, var_get_label (var));
343 if (measure != var_default_measure (type))
344 syntax_gen_pspp (s, "VARIABLE LEVEL %ss (%ss).\n",
346 (measure == MEASURE_NOMINAL ? "NOMINAL"
347 : measure == MEASURE_ORDINAL ? "ORDINAL"
349 if (alignment != var_default_alignment (type))
350 syntax_gen_pspp (s, "VARIABLE ALIGNMENT %ss (%ss).\n",
352 (alignment == ALIGN_LEFT ? "LEFT"
353 : alignment == ALIGN_CENTRE ? "CENTER"
355 if (var_get_display_width (var) != var_default_display_width (width))
356 syntax_gen_pspp (s, "VARIABLE WIDTH %ss (%d).\n",
357 name, var_get_display_width (var));
361 /* Generates and returns PSPP syntax to execute the import
362 operation described by IA. The caller must free the syntax
365 generate_syntax (const struct import_assistant *ia)
367 struct string s = DS_EMPTY_INITIALIZER;
376 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (
377 ia->intro.n_cases_button)))
378 ds_put_format (&s, " /IMPORTCASES=FIRST %d\n",
379 gtk_spin_button_get_value_as_int (
380 GTK_SPIN_BUTTON (ia->intro.n_cases_spin)));
381 else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (
382 ia->intro.percent_button)))
383 ds_put_format (&s, " /IMPORTCASES=PERCENT %d\n",
384 gtk_spin_button_get_value_as_int (
385 GTK_SPIN_BUTTON (ia->intro.percent_spin)));
387 ds_put_cstr (&s, " /IMPORTCASES=ALL\n");
389 " /ARRANGEMENT=DELIMITED\n"
391 if (ia->first_line.skip_lines > 0)
392 ds_put_format (&s, " /FIRSTCASE=%d\n", ia->first_line.skip_lines + 1);
393 ds_put_cstr (&s, " /DELIMITERS=\"");
394 if (ds_find_byte (&ia->separators.separators, '\t') != SIZE_MAX)
395 ds_put_cstr (&s, "\\t");
396 if (ds_find_byte (&ia->separators.separators, '\\') != SIZE_MAX)
397 ds_put_cstr (&s, "\\\\");
398 for (i = 0; i < ds_length (&ia->separators.separators); i++)
400 char c = ds_at (&ia->separators.separators, i);
402 ds_put_cstr (&s, "\"\"");
403 else if (c != '\t' && c != '\\')
406 ds_put_cstr (&s, "\"\n");
407 if (!ds_is_empty (&ia->separators.quotes))
408 syntax_gen_pspp (&s, " /QUALIFIER=%sq\n", ds_cstr (&ia->separators.quotes));
409 if (!ds_is_empty (&ia->separators.quotes) && ia->separators.escape)
410 ds_put_cstr (&s, " /ESCAPE\n");
411 ds_put_cstr (&s, " /VARIABLES=\n");
413 var_cnt = dict_get_var_cnt (ia->formats.dict);
414 for (i = 0; i < var_cnt; i++)
416 struct variable *var = dict_get_var (ia->formats.dict, i);
417 char format_string[FMT_STRING_LEN_MAX + 1];
418 fmt_to_string (var_get_print_format (var), format_string);
419 ds_put_format (&s, " %s %s%s\n",
420 var_get_name (var), format_string,
421 i == var_cnt - 1 ? "." : "");
424 apply_dict (ia->formats.dict, &s);
429 /* Choosing a file and reading it. */
431 static char *choose_file (GtkWindow *parent_window);
433 /* Obtains the file to import from the user and initializes IA's
434 file substructure. PARENT_WINDOW must be the window to use
435 as the file chooser window's parent.
437 Returns true if successful, false if the file name could not
438 be obtained or the file could not be read. */
440 init_file (struct import_assistant *ia, GtkWindow *parent_window)
442 struct file *file = &ia->file;
443 enum { MAX_PREVIEW_LINES = 1000 }; /* Max number of lines to read. */
444 enum { MAX_LINE_LEN = 16384 }; /* Max length of an acceptable line. */
447 file->file_name = choose_file (parent_window);
448 if (file->file_name == NULL)
451 stream = fopen (file->file_name, "r");
454 msg (ME, _("Could not open `%s': %s"),
455 file->file_name, strerror (errno));
459 file->lines = xnmalloc (MAX_PREVIEW_LINES, sizeof *file->lines);
460 for (; file->line_cnt < MAX_PREVIEW_LINES; file->line_cnt++)
462 struct string *line = &file->lines[file->line_cnt];
464 ds_init_empty (line);
465 if (!ds_read_line (line, stream, MAX_LINE_LEN))
469 else if (ferror (stream))
470 msg (ME, _("Error reading `%s': %s"),
471 file->file_name, strerror (errno));
473 msg (ME, _("Failed to read `%s', because it contains a line "
474 "over %d bytes long and therefore appears not to be "
476 file->file_name, MAX_LINE_LEN);
481 ds_chomp_byte (line, '\n');
482 ds_chomp_byte (line, '\r');
485 if (file->line_cnt == 0)
487 msg (ME, _("`%s' is empty."), file->file_name);
493 /* Estimate the number of lines in the file. */
494 if (file->line_cnt < MAX_PREVIEW_LINES)
495 file->total_lines = file->line_cnt;
499 off_t position = ftello (stream);
500 if (fstat (fileno (stream), &s) == 0 && position > 0)
501 file->total_lines = (double) file->line_cnt / position * s.st_size;
503 file->total_lines = 0;
509 /* Frees IA's file substructure. */
511 destroy_file (struct import_assistant *ia)
513 struct file *f = &ia->file;
516 for (i = 0; i < f->line_cnt; i++)
517 ds_destroy (&f->lines[i]);
519 g_free (f->file_name);
522 /* Obtains the file to read from the user and returns the name of
523 the file as a string that must be freed with g_free if
524 successful, otherwise a null pointer. PARENT_WINDOW must be
525 the window to use as the file chooser window's parent. */
527 choose_file (GtkWindow *parent_window)
531 GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Import Delimited Text Data"),
533 GTK_FILE_CHOOSER_ACTION_OPEN,
534 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
535 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
538 g_object_set (dialog, "local-only", FALSE, NULL);
540 switch (gtk_dialog_run (GTK_DIALOG (dialog)))
542 case GTK_RESPONSE_ACCEPT:
543 file_name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
549 gtk_widget_destroy (dialog);
556 static void close_assistant (struct import_assistant *, int response);
557 static void on_prepare (GtkAssistant *assistant, GtkWidget *page,
558 struct import_assistant *);
559 static void on_cancel (GtkAssistant *assistant, struct import_assistant *);
560 static void on_close (GtkAssistant *assistant, struct import_assistant *);
561 static void on_paste (GtkButton *button, struct import_assistant *);
562 static void on_reset (GtkButton *button, struct import_assistant *);
563 static void close_assistant (struct import_assistant *, int response);
565 /* Initializes IA's asst substructure. PARENT_WINDOW must be the
566 window to use as the assistant window's parent. */
568 init_assistant (struct import_assistant *ia, GtkWindow *parent_window)
570 struct assistant *a = &ia->asst;
572 a->builder = builder_new ("text-data-import.ui");
573 a->assistant = GTK_ASSISTANT (gtk_assistant_new ());
574 g_signal_connect (a->assistant, "prepare", G_CALLBACK (on_prepare), ia);
575 g_signal_connect (a->assistant, "cancel", G_CALLBACK (on_cancel), ia);
576 g_signal_connect (a->assistant, "close", G_CALLBACK (on_close), ia);
577 a->paste_button = gtk_button_new_from_stock (GTK_STOCK_PASTE);
578 gtk_assistant_add_action_widget (a->assistant, a->paste_button);
579 g_signal_connect (a->paste_button, "clicked", G_CALLBACK (on_paste), ia);
580 a->reset_button = gtk_button_new_from_stock ("pspp-stock-reset");
581 gtk_assistant_add_action_widget (a->assistant, a->reset_button);
582 g_signal_connect (a->reset_button, "clicked", G_CALLBACK (on_reset), ia);
583 gtk_window_set_title (GTK_WINDOW (a->assistant),
584 _("Importing Delimited Text Data"));
585 gtk_window_set_transient_for (GTK_WINDOW (a->assistant), parent_window);
586 gtk_window_set_icon_name (GTK_WINDOW (a->assistant), "pspp");
588 a->prop_renderer = gtk_cell_renderer_text_new ();
589 g_object_ref_sink (a->prop_renderer);
590 a->fixed_renderer = gtk_cell_renderer_text_new ();
591 g_object_ref_sink (a->fixed_renderer);
592 g_object_set (G_OBJECT (a->fixed_renderer),
593 "family", "Monospace",
597 /* Frees IA's asst substructure. */
599 destroy_assistant (struct import_assistant *ia)
601 struct assistant *a = &ia->asst;
603 g_object_unref (a->prop_renderer);
604 g_object_unref (a->fixed_renderer);
605 g_object_unref (a->builder);
608 /* Appends a page of the given TYPE, with PAGE as its content, to
609 the GtkAssistant encapsulated by IA. Returns the GtkWidget
610 that represents the page. */
612 add_page_to_assistant (struct import_assistant *ia,
613 GtkWidget *page, GtkAssistantPageType type)
619 title = gtk_window_get_title (GTK_WINDOW (page));
620 title_copy = xstrdup (title ? title : "");
622 content = gtk_bin_get_child (GTK_BIN (page));
624 g_object_ref (content);
625 gtk_container_remove (GTK_CONTAINER (page), content);
627 gtk_widget_destroy (page);
629 gtk_assistant_append_page (ia->asst.assistant, content);
630 gtk_assistant_set_page_type (ia->asst.assistant, content, type);
631 gtk_assistant_set_page_title (ia->asst.assistant, content, title_copy);
632 gtk_assistant_set_page_complete (ia->asst.assistant, content, true);
639 /* Called just before PAGE is displayed as the current page of
640 ASSISTANT, this updates IA content according to the new
643 on_prepare (GtkAssistant *assistant, GtkWidget *page,
644 struct import_assistant *ia)
647 if (gtk_assistant_get_page_type (assistant, page)
648 == GTK_ASSISTANT_PAGE_CONFIRM)
649 gtk_widget_grab_focus (assistant->apply);
651 gtk_widget_grab_focus (assistant->forward);
653 if (page == ia->separators.page)
654 prepare_separators_page (ia);
655 else if (page == ia->formats.page)
656 prepare_formats_page (ia);
658 gtk_widget_show (ia->asst.reset_button);
659 if (page == ia->formats.page)
660 gtk_widget_show (ia->asst.paste_button);
662 gtk_widget_hide (ia->asst.paste_button);
665 /* Called when the Cancel button in the assistant is clicked. */
667 on_cancel (GtkAssistant *assistant, struct import_assistant *ia)
669 close_assistant (ia, GTK_RESPONSE_CANCEL);
672 /* Called when the Apply button on the last page of the assistant
675 on_close (GtkAssistant *assistant, struct import_assistant *ia)
677 close_assistant (ia, GTK_RESPONSE_APPLY);
680 /* Called when the Paste button on the last page of the assistant
683 on_paste (GtkButton *button, struct import_assistant *ia)
685 close_assistant (ia, PSPPIRE_RESPONSE_PASTE);
688 /* Called when the Reset button is clicked. */
690 on_reset (GtkButton *button, struct import_assistant *ia)
692 gint page_num = gtk_assistant_get_current_page (ia->asst.assistant);
693 GtkWidget *page = gtk_assistant_get_nth_page (ia->asst.assistant, page_num);
695 if (page == ia->intro.page)
696 reset_intro_page (ia);
697 else if (page == ia->first_line.page)
698 reset_first_line_page (ia);
699 else if (page == ia->separators.page)
700 reset_separators_page (ia);
701 else if (page == ia->formats.page)
702 reset_formats_page (ia);
705 /* Causes the assistant to close, returning RESPONSE for
706 interpretation by text_data_import_assistant. */
708 close_assistant (struct import_assistant *ia, int response)
710 ia->asst.response = response;
711 g_main_loop_quit (ia->asst.main_loop);
712 gtk_widget_hide (GTK_WIDGET (ia->asst.assistant));
715 /* The "intro" page of the assistant. */
717 static void on_intro_amount_changed (struct import_assistant *);
719 /* Initializes IA's intro substructure. */
721 init_intro_page (struct import_assistant *ia)
723 GtkBuilder *builder = ia->asst.builder;
724 struct intro_page *p = &ia->intro;
726 GtkWidget *hbox_n_cases ;
727 GtkWidget *hbox_percent ;
731 p->n_cases_spin = gtk_spin_button_new_with_range (0, INT_MAX, 100);
733 hbox_n_cases = psppire_scanf_new (_("Only the first %4d cases"), &p->n_cases_spin);
735 table = get_widget_assert (builder, "button-table");
737 gtk_table_attach_defaults (GTK_TABLE (table), hbox_n_cases,
741 p->percent_spin = gtk_spin_button_new_with_range (0, 100, 10);
743 hbox_percent = psppire_scanf_new (_("Only the first %3d %% of file (approximately)"), &p->percent_spin);
745 gtk_table_attach_defaults (GTK_TABLE (table), hbox_percent,
749 p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Intro"),
750 GTK_ASSISTANT_PAGE_INTRO);
752 p->all_cases_button = get_widget_assert (builder, "import-all-cases");
754 p->n_cases_button = get_widget_assert (builder, "import-n-cases");
756 p->percent_button = get_widget_assert (builder, "import-percent");
758 g_signal_connect_swapped (p->all_cases_button, "toggled",
759 G_CALLBACK (on_intro_amount_changed), ia);
760 g_signal_connect_swapped (p->n_cases_button, "toggled",
761 G_CALLBACK (on_intro_amount_changed), ia);
762 g_signal_connect_swapped (p->percent_button, "toggled",
763 G_CALLBACK (on_intro_amount_changed), ia);
765 on_intro_amount_changed (ia);
768 ds_put_cstr (&s, _("This assistant will guide you through the process of "
769 "importing data into PSPP from a text file with one line "
770 "per case, in which fields are separated by tabs, "
771 "commas, or other delimiters.\n\n"));
772 if (ia->file.total_is_exact)
774 &s, ngettext ("The selected file contains %zu line of text. ",
775 "The selected file contains %zu lines of text. ",
778 else if (ia->file.total_lines > 0)
782 "The selected file contains approximately %lu line of text. ",
783 "The selected file contains approximately %lu lines of text. ",
784 ia->file.total_lines),
785 ia->file.total_lines);
788 "Only the first %zu line of the file will be shown for "
789 "preview purposes in the following screens. ",
790 "Only the first %zu lines of the file will be shown for "
791 "preview purposes in the following screens. ",
795 ds_put_cstr (&s, _("You may choose below how much of the file should "
796 "actually be imported."));
797 gtk_label_set_text (GTK_LABEL (get_widget_assert (builder, "intro-label")),
802 /* Resets IA's intro page to its initial state. */
804 reset_intro_page (struct import_assistant *ia)
806 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->intro.all_cases_button),
810 /* Called when one of the radio buttons is clicked. */
812 on_intro_amount_changed (struct import_assistant *ia)
814 struct intro_page *p = &ia->intro;
816 gtk_widget_set_sensitive (p->n_cases_spin,
817 gtk_toggle_button_get_active (
818 GTK_TOGGLE_BUTTON (p->n_cases_button)));
820 gtk_widget_set_sensitive (p->percent_spin,
821 gtk_toggle_button_get_active (
822 GTK_TOGGLE_BUTTON (p->percent_button)));
825 /* The "first line" page of the assistant. */
827 static GtkTreeView *create_lines_tree_view (GtkContainer *parent_window,
828 struct import_assistant *);
829 static void on_first_line_change (GtkTreeSelection *,
830 struct import_assistant *);
831 static void on_variable_names_cb_toggle (GtkToggleButton *,
832 struct import_assistant *);
833 static void set_first_line (struct import_assistant *);
834 static void get_first_line (struct import_assistant *);
836 /* Initializes IA's first_line substructure. */
838 init_first_line_page (struct import_assistant *ia)
840 struct first_line_page *p = &ia->first_line;
841 GtkBuilder *builder = ia->asst.builder;
843 p->page = add_page_to_assistant (ia, get_widget_assert (builder, "FirstLine"),
844 GTK_ASSISTANT_PAGE_CONTENT);
845 gtk_widget_destroy (get_widget_assert (builder, "first-line"));
846 p->tree_view = create_lines_tree_view (
847 GTK_CONTAINER (get_widget_assert (builder, "first-line-scroller")), ia);
848 p->variable_names_cb = get_widget_assert (builder, "variable-names");
849 gtk_tree_selection_set_mode (
850 gtk_tree_view_get_selection (GTK_TREE_VIEW (p->tree_view)),
851 GTK_SELECTION_BROWSE);
853 g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (p->tree_view)),
854 "changed", G_CALLBACK (on_first_line_change), ia);
855 g_signal_connect (p->variable_names_cb, "toggled",
856 G_CALLBACK (on_variable_names_cb_toggle), ia);
859 /* Resets the first_line page to its initial content. */
861 reset_first_line_page (struct import_assistant *ia)
863 ia->first_line.skip_lines = 0;
864 ia->first_line.variable_names = false;
868 /* Creates and returns a tree view that contains each of the
869 lines in IA's file as a row. */
871 create_lines_tree_view (GtkContainer *parent, struct import_assistant *ia)
873 GtkTreeView *tree_view;
874 GtkTreeViewColumn *column;
875 size_t max_line_length;
876 gint content_width, header_width;
878 const gchar *title = _("Text");
880 make_tree_view (ia, 0, &tree_view);
882 column = gtk_tree_view_column_new_with_attributes
884 title, ia->asst.fixed_renderer,
885 "text", TEXT_IMPORT_MODEL_COLUMN_LINE,
888 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
891 for (i = 0; i < ia->file.line_cnt; i++)
893 size_t w = ds_length (&ia->file.lines[i]);
894 max_line_length = MAX (max_line_length, w);
897 content_width = get_monospace_width (tree_view, ia->asst.fixed_renderer,
899 header_width = get_string_width (tree_view, ia->asst.prop_renderer, title);
900 gtk_tree_view_column_set_fixed_width (column, MAX (content_width,
902 gtk_tree_view_append_column (tree_view, column);
904 gtk_tree_view_set_fixed_height_mode (tree_view, true);
906 gtk_container_add (parent, GTK_WIDGET (tree_view));
907 gtk_widget_show (GTK_WIDGET (tree_view));
912 /* Called when the line selected in the first_line tree view
915 on_first_line_change (GtkTreeSelection *selection UNUSED,
916 struct import_assistant *ia)
921 /* Called when the checkbox that indicates whether variable
922 names are in the row above the first line is toggled. */
924 on_variable_names_cb_toggle (GtkToggleButton *variable_names_cb UNUSED,
925 struct import_assistant *ia)
930 /* Sets the widgets to match IA's first_line substructure. */
932 set_first_line (struct import_assistant *ia)
936 path = gtk_tree_path_new_from_indices (ia->first_line.skip_lines, -1);
937 gtk_tree_view_set_cursor (GTK_TREE_VIEW (ia->first_line.tree_view),
939 gtk_tree_path_free (path);
941 gtk_toggle_button_set_active (
942 GTK_TOGGLE_BUTTON (ia->first_line.variable_names_cb),
943 ia->first_line.variable_names);
944 gtk_widget_set_sensitive (ia->first_line.variable_names_cb,
945 ia->first_line.skip_lines > 0);
948 /* Sets IA's first_line substructure to match the widgets. */
950 get_first_line (struct import_assistant *ia)
952 GtkTreeSelection *selection;
956 selection = gtk_tree_view_get_selection (ia->first_line.tree_view);
957 if (gtk_tree_selection_get_selected (selection, &model, &iter))
959 GtkTreePath *path = gtk_tree_model_get_path (model, &iter);
960 int row = gtk_tree_path_get_indices (path)[0];
961 gtk_tree_path_free (path);
963 ia->first_line.skip_lines = row;
964 ia->first_line.variable_names =
965 (ia->first_line.skip_lines > 0
966 && gtk_toggle_button_get_active (
967 GTK_TOGGLE_BUTTON (ia->first_line.variable_names_cb)));
969 gtk_widget_set_sensitive (ia->first_line.variable_names_cb,
970 ia->first_line.skip_lines > 0);
973 /* The "separators" page of the assistant. */
975 static void revise_fields_preview (struct import_assistant *ia);
976 static void choose_likely_separators (struct import_assistant *ia);
977 static void find_commonest_chars (unsigned long int histogram[UCHAR_MAX + 1],
978 const char *targets, const char *def,
979 struct string *result);
980 static void clear_fields (struct import_assistant *ia);
981 static void revise_fields_preview (struct import_assistant *);
982 static void set_separators (struct import_assistant *);
983 static void get_separators (struct import_assistant *);
984 static void on_separators_custom_entry_notify (GObject *UNUSED,
986 struct import_assistant *);
987 static void on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
988 struct import_assistant *);
989 static void on_quote_combo_change (GtkComboBox *combo,
990 struct import_assistant *);
991 static void on_quote_cb_toggle (GtkToggleButton *quote_cb,
992 struct import_assistant *);
993 static void on_separator_toggle (GtkToggleButton *, struct import_assistant *);
994 static void render_input_cell (GtkTreeViewColumn *tree_column,
995 GtkCellRenderer *cell,
996 GtkTreeModel *model, GtkTreeIter *iter,
998 static gboolean on_query_input_tooltip (GtkWidget *widget, gint wx, gint wy,
999 gboolean keyboard_mode UNUSED,
1000 GtkTooltip *tooltip,
1001 struct import_assistant *);
1003 /* A common field separator and its identifying name. */
1006 const char *name; /* Name (for use with get_widget_assert). */
1007 int c; /* Separator character. */
1010 /* All the separators in the dialog box. */
1011 static const struct separator separators[] =
1023 #define SEPARATOR_CNT (sizeof separators / sizeof *separators)
1026 set_quote_list (GtkComboBoxEntry *cb)
1028 GtkListStore *list = gtk_list_store_new (1, G_TYPE_STRING);
1031 const gchar *seperator[3] = {"'\"", "\'", "\""};
1033 for (i = 0; i < 3; i++)
1035 const gchar *s = seperator[i];
1037 /* Add a new row to the model */
1038 gtk_list_store_append (list, &iter);
1039 gtk_list_store_set (list, &iter,
1045 gtk_combo_box_set_model (GTK_COMBO_BOX (cb), GTK_TREE_MODEL (list));
1047 gtk_combo_box_entry_set_text_column (cb, 0);
1050 /* Initializes IA's separators substructure. */
1052 init_separators_page (struct import_assistant *ia)
1054 GtkBuilder *builder = ia->asst.builder;
1055 struct separators_page *p = &ia->separators;
1058 choose_likely_separators (ia);
1060 p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Separators"),
1061 GTK_ASSISTANT_PAGE_CONTENT);
1062 p->custom_cb = get_widget_assert (builder, "custom-cb");
1063 p->custom_entry = get_widget_assert (builder, "custom-entry");
1064 p->quote_combo = get_widget_assert (builder, "quote-combo");
1065 p->quote_entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (p->quote_combo)));
1066 p->quote_cb = get_widget_assert (builder, "quote-cb");
1067 p->escape_cb = get_widget_assert (builder, "escape");
1069 set_separators (ia);
1070 set_quote_list (GTK_COMBO_BOX_ENTRY (p->quote_combo));
1071 p->fields_tree_view = GTK_TREE_VIEW (get_widget_assert (builder, "fields"));
1072 g_signal_connect (p->quote_combo, "changed",
1073 G_CALLBACK (on_quote_combo_change), ia);
1074 g_signal_connect (p->quote_cb, "toggled",
1075 G_CALLBACK (on_quote_cb_toggle), ia);
1076 g_signal_connect (p->custom_entry, "notify::text",
1077 G_CALLBACK (on_separators_custom_entry_notify), ia);
1078 g_signal_connect (p->custom_cb, "toggled",
1079 G_CALLBACK (on_separators_custom_cb_toggle), ia);
1080 for (i = 0; i < SEPARATOR_CNT; i++)
1081 g_signal_connect (get_widget_assert (builder, separators[i].name),
1082 "toggled", G_CALLBACK (on_separator_toggle), ia);
1083 g_signal_connect (p->escape_cb, "toggled",
1084 G_CALLBACK (on_separator_toggle), ia);
1087 /* Frees IA's separators substructure. */
1089 destroy_separators_page (struct import_assistant *ia)
1091 struct separators_page *s = &ia->separators;
1093 ds_destroy (&s->separators);
1094 ds_destroy (&s->quotes);
1098 /* Called just before the separators page becomes visible in the
1101 prepare_separators_page (struct import_assistant *ia)
1103 revise_fields_preview (ia);
1106 /* Called when the Reset button is clicked on the separators
1107 page, resets the separators to the defaults. */
1109 reset_separators_page (struct import_assistant *ia)
1111 choose_likely_separators (ia);
1112 set_separators (ia);
1115 /* Frees and clears the column data in IA's separators
1118 clear_fields (struct import_assistant *ia)
1120 struct separators_page *s = &ia->separators;
1122 if (s->column_cnt > 0)
1127 for (row = 0; row < ia->file.line_cnt; row++)
1129 const struct string *line = &ia->file.lines[row];
1130 const char *line_start = ds_data (line);
1131 const char *line_end = ds_end (line);
1133 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1135 char *s = ss_data (col->contents[row]);
1136 if (!(s >= line_start && s <= line_end))
1137 ss_dealloc (&col->contents[row]);
1141 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1144 free (col->contents);
1153 /* Breaks the file data in IA into columns based on the
1154 separators set in IA's separators substructure. */
1156 split_fields (struct import_assistant *ia)
1158 struct separators_page *s = &ia->separators;
1159 size_t columns_allocated;
1165 /* Is space in the set of separators? */
1166 space_sep = ss_find_byte (ds_ss (&s->separators), ' ') != SIZE_MAX;
1168 /* Split all the lines, not just those from
1169 ia->first_line.skip_lines on, so that we split the line that
1170 contains variables names if ia->first_line.variable_names is
1172 columns_allocated = 0;
1173 for (row = 0; row < ia->file.line_cnt; row++)
1175 struct string *line = &ia->file.lines[row];
1176 struct substring text = ds_ss (line);
1179 for (column_idx = 0; ; column_idx++)
1181 struct substring field;
1182 struct column *column;
1185 ss_ltrim (&text, ss_cstr (" "));
1186 if (ss_is_empty (text))
1188 if (column_idx != 0)
1192 else if (!ds_is_empty (&s->quotes)
1193 && ds_find_byte (&s->quotes, text.string[0]) != SIZE_MAX)
1195 int quote = ss_get_byte (&text);
1197 ss_get_until (&text, quote, &field);
1204 while ((c = ss_get_byte (&text)) != EOF)
1206 ds_put_byte (&s, c);
1207 else if (ss_match_byte (&text, quote))
1208 ds_put_byte (&s, quote);
1215 ss_get_bytes (&text, ss_cspan (text, ds_ss (&s->separators)),
1218 if (column_idx >= s->column_cnt)
1220 struct column *column;
1222 if (s->column_cnt >= columns_allocated)
1223 s->columns = x2nrealloc (s->columns, &columns_allocated,
1224 sizeof *s->columns);
1225 column = &s->columns[s->column_cnt++];
1226 column->name = NULL;
1228 column->contents = xcalloc (ia->file.line_cnt,
1229 sizeof *column->contents);
1231 column = &s->columns[column_idx];
1232 column->contents[row] = field;
1233 if (ss_length (field) > column->width)
1234 column->width = ss_length (field);
1237 ss_ltrim (&text, ss_cstr (" "));
1238 if (ss_is_empty (text))
1240 if (ss_find_byte (ds_ss (&s->separators), ss_first (text))
1242 ss_advance (&text, 1);
1247 /* Chooses a name for each column on the separators page */
1249 choose_column_names (struct import_assistant *ia)
1251 const struct first_line_page *f = &ia->first_line;
1252 struct separators_page *s = &ia->separators;
1253 struct dictionary *dict;
1254 unsigned long int generated_name_count = 0;
1258 dict = dict_create (get_default_encoding ());
1259 name_row = f->variable_names && f->skip_lines ? f->skip_lines : 0;
1260 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1264 hint = name_row ? ss_xstrdup (col->contents[name_row - 1]) : NULL;
1265 name = dict_make_unique_var_name (dict, hint, &generated_name_count);
1269 dict_create_var_assert (dict, name, 0);
1271 dict_destroy (dict);
1274 /* Picks the most likely separator and quote characters based on
1277 choose_likely_separators (struct import_assistant *ia)
1279 unsigned long int histogram[UCHAR_MAX + 1] = { 0 };
1282 /* Construct a histogram of all the characters used in the
1284 for (row = 0; row < ia->file.line_cnt; row++)
1286 struct substring line = ds_ss (&ia->file.lines[row]);
1287 size_t length = ss_length (line);
1289 for (i = 0; i < length; i++)
1290 histogram[(unsigned char) line.string[i]]++;
1293 find_commonest_chars (histogram, "\"'", "", &ia->separators.quotes);
1294 find_commonest_chars (histogram, ",;:/|!\t-", ",",
1295 &ia->separators.separators);
1296 ia->separators.escape = true;
1299 /* Chooses the most common character among those in TARGETS,
1300 based on the frequency data in HISTOGRAM, and stores it in
1301 RESULT. If there is a tie for the most common character among
1302 those in TARGETS, the earliest character is chosen. If none
1303 of the TARGETS appear at all, then DEF is used as a
1306 find_commonest_chars (unsigned long int histogram[UCHAR_MAX + 1],
1307 const char *targets, const char *def,
1308 struct string *result)
1310 unsigned char max = 0;
1311 unsigned long int max_count = 0;
1313 for (; *targets != '\0'; targets++)
1315 unsigned char c = *targets;
1316 unsigned long int count = histogram[c];
1317 if (count > max_count)
1326 ds_put_byte (result, max);
1329 ds_assign_cstr (result, def);
1332 /* Revises the contents of the fields tree view based on the
1333 currently chosen set of separators. */
1335 revise_fields_preview (struct import_assistant *ia)
1339 push_watch_cursor (ia);
1341 w = GTK_WIDGET (ia->separators.fields_tree_view);
1342 gtk_widget_destroy (w);
1343 get_separators (ia);
1345 choose_column_names (ia);
1346 ia->separators.fields_tree_view = create_data_tree_view (
1348 GTK_CONTAINER (get_widget_assert (ia->asst.builder, "fields-scroller")),
1351 pop_watch_cursor (ia);
1354 /* Sets the widgets to match IA's separators substructure. */
1356 set_separators (struct import_assistant *ia)
1358 struct separators_page *s = &ia->separators;
1360 struct string custom;
1365 ds_init_empty (&custom);
1367 for (i = 0; i < ds_length (&s->separators); i++)
1369 unsigned char c = ds_at (&s->separators, i);
1372 for (j = 0; j < SEPARATOR_CNT; j++)
1374 const struct separator *s = &separators[j];
1382 ds_put_byte (&custom, c);
1386 for (i = 0; i < SEPARATOR_CNT; i++)
1388 const struct separator *s = &separators[i];
1389 GtkWidget *button = get_widget_assert (ia->asst.builder, s->name);
1390 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
1391 (seps & (1u << i)) != 0);
1393 any_custom = !ds_is_empty (&custom);
1394 gtk_entry_set_text (GTK_ENTRY (s->custom_entry), ds_cstr (&custom));
1395 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->custom_cb),
1397 gtk_widget_set_sensitive (s->custom_entry, any_custom);
1398 ds_destroy (&custom);
1400 any_quotes = !ds_is_empty (&s->quotes);
1402 gtk_entry_set_text (s->quote_entry,
1403 any_quotes ? ds_cstr (&s->quotes) : "\"");
1404 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->quote_cb),
1406 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->escape_cb),
1408 gtk_widget_set_sensitive (s->quote_combo, any_quotes);
1409 gtk_widget_set_sensitive (s->escape_cb, any_quotes);
1412 /* Sets IA's separators substructure to match the widgets. */
1414 get_separators (struct import_assistant *ia)
1416 struct separators_page *s = &ia->separators;
1419 ds_clear (&s->separators);
1420 for (i = 0; i < SEPARATOR_CNT; i++)
1422 const struct separator *sep = &separators[i];
1423 GtkWidget *button = get_widget_assert (ia->asst.builder, sep->name);
1424 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
1425 ds_put_byte (&s->separators, sep->c);
1428 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->custom_cb)))
1429 ds_put_cstr (&s->separators,
1430 gtk_entry_get_text (GTK_ENTRY (s->custom_entry)));
1432 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->quote_cb)))
1434 gchar *text = gtk_combo_box_get_active_text (
1435 GTK_COMBO_BOX (s->quote_combo));
1436 ds_assign_cstr (&s->quotes, text);
1440 ds_clear (&s->quotes);
1441 s->escape = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->escape_cb));
1444 /* Called when the user changes the entry field for custom
1447 on_separators_custom_entry_notify (GObject *gobject UNUSED,
1448 GParamSpec *arg1 UNUSED,
1449 struct import_assistant *ia)
1451 revise_fields_preview (ia);
1454 /* Called when the user toggles the checkbox that enables custom
1457 on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
1458 struct import_assistant *ia)
1460 bool is_active = gtk_toggle_button_get_active (custom_cb);
1461 gtk_widget_set_sensitive (ia->separators.custom_entry, is_active);
1462 revise_fields_preview (ia);
1465 /* Called when the user changes the selection in the combo box
1466 that selects a quote character. */
1468 on_quote_combo_change (GtkComboBox *combo, struct import_assistant *ia)
1470 revise_fields_preview (ia);
1473 /* Called when the user toggles the checkbox that enables
1476 on_quote_cb_toggle (GtkToggleButton *quote_cb, struct import_assistant *ia)
1478 bool is_active = gtk_toggle_button_get_active (quote_cb);
1479 gtk_widget_set_sensitive (ia->separators.quote_combo, is_active);
1480 gtk_widget_set_sensitive (ia->separators.escape_cb, is_active);
1481 revise_fields_preview (ia);
1484 /* Called when the user toggles one of the separators
1487 on_separator_toggle (GtkToggleButton *toggle UNUSED,
1488 struct import_assistant *ia)
1490 revise_fields_preview (ia);
1493 /* Called to render one of the cells in the fields preview tree
1496 render_input_cell (GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1497 GtkTreeModel *model, GtkTreeIter *iter,
1500 struct import_assistant *ia = ia_;
1501 struct substring field;
1505 column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1507 row = text_import_model_iter_to_row (iter) + ia->first_line.skip_lines;
1508 field = ia->separators.columns[column].contents[row];
1509 if (field.string != NULL)
1511 GValue text = {0, };
1512 g_value_init (&text, G_TYPE_STRING);
1513 g_value_take_string (&text, ss_xstrdup (field));
1514 g_object_set_property (G_OBJECT (cell), "text", &text);
1515 g_value_unset (&text);
1516 g_object_set (cell, "background-set", FALSE, (void *) NULL);
1521 "background", "red",
1522 "background-set", TRUE,
1526 /* Called to render a tooltip on one of the cells in the fields
1527 preview tree view. */
1529 on_query_input_tooltip (GtkWidget *widget, gint wx, gint wy,
1530 gboolean keyboard_mode UNUSED,
1531 GtkTooltip *tooltip, struct import_assistant *ia)
1535 if (!get_tooltip_location (widget, wx, wy, ia, &row, &column))
1538 if (ia->separators.columns[column].contents[row].string != NULL)
1541 gtk_tooltip_set_text (tooltip,
1542 _("This input line has too few separators "
1543 "to fill in this field."));
1547 /* The "formats" page of the assistant. */
1549 static void on_variable_change (PsppireDict *dict, int idx,
1550 struct import_assistant *);
1551 static void clear_modified_vars (struct import_assistant *);
1553 /* Initializes IA's formats substructure. */
1555 init_formats_page (struct import_assistant *ia)
1557 GtkBuilder *builder = ia->asst.builder;
1558 struct formats_page *p = &ia->formats;
1560 p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Formats"),
1561 GTK_ASSISTANT_PAGE_CONFIRM);
1562 p->data_tree_view = GTK_TREE_VIEW (get_widget_assert (builder, "data"));
1563 p->modified_vars = NULL;
1564 p->modified_var_cnt = 0;
1568 /* Frees IA's formats substructure. */
1570 destroy_formats_page (struct import_assistant *ia)
1572 struct formats_page *p = &ia->formats;
1574 if (p->psppire_dict != NULL)
1576 /* This destroys p->dict also. */
1577 g_object_unref (p->psppire_dict);
1579 clear_modified_vars (ia);
1582 /* Called just before the formats page of the assistant is
1585 prepare_formats_page (struct import_assistant *ia)
1587 struct dictionary *dict;
1588 PsppireDict *psppire_dict;
1589 PsppireVarStore *var_store;
1590 GtkBin *vars_scroller;
1591 GtkWidget *old_var_sheet;
1592 PsppireVarSheet *var_sheet;
1593 struct separators_page *s = &ia->separators;
1594 struct formats_page *p = &ia->formats;
1595 struct fmt_guesser *fg;
1596 unsigned long int number = 0;
1599 push_watch_cursor (ia);
1601 dict = dict_create (get_default_encoding ());
1602 fg = fmt_guesser_create ();
1603 for (column_idx = 0; column_idx < s->column_cnt; column_idx++)
1605 struct variable *modified_var;
1607 modified_var = (column_idx < p->modified_var_cnt
1608 ? p->modified_vars[column_idx] : NULL);
1609 if (modified_var == NULL)
1611 struct column *column = &s->columns[column_idx];
1612 struct variable *var;
1613 struct fmt_spec format;
1617 /* Choose variable name. */
1618 name = dict_make_unique_var_name (dict, column->name, &number);
1620 /* Choose variable format. */
1621 fmt_guesser_clear (fg);
1622 for (row = ia->first_line.skip_lines; row < ia->file.line_cnt; row++)
1623 fmt_guesser_add (fg, column->contents[row]);
1624 fmt_guesser_guess (fg, &format);
1625 fmt_fix_input (&format);
1627 /* Create variable. */
1628 var = dict_create_var_assert (dict, name, fmt_var_width (&format));
1629 var_set_both_formats (var, &format);
1637 name = dict_make_unique_var_name (dict, var_get_name (modified_var),
1639 dict_clone_var_as_assert (dict, modified_var, name);
1643 fmt_guesser_destroy (fg);
1645 psppire_dict = psppire_dict_new_from_dict (dict);
1646 g_signal_connect (psppire_dict, "variable_changed",
1647 G_CALLBACK (on_variable_change), ia);
1648 ia->formats.dict = dict;
1649 ia->formats.psppire_dict = psppire_dict;
1651 /* XXX: PsppireVarStore doesn't hold a reference to
1652 psppire_dict for now, but it should. After it does, we
1653 should g_object_ref the psppire_dict here, since we also
1654 hold a reference via ia->formats.dict. */
1655 var_store = psppire_var_store_new (psppire_dict);
1656 g_object_set (var_store,
1657 "format-type", PSPPIRE_VAR_STORE_INPUT_FORMATS,
1659 var_sheet = PSPPIRE_VAR_SHEET (psppire_var_sheet_new ());
1660 g_object_set (var_sheet,
1662 "may-create-vars", FALSE,
1665 vars_scroller = GTK_BIN (get_widget_assert (ia->asst.builder, "vars-scroller"));
1666 old_var_sheet = gtk_bin_get_child (vars_scroller);
1667 if (old_var_sheet != NULL)
1668 gtk_widget_destroy (old_var_sheet);
1669 gtk_container_add (GTK_CONTAINER (vars_scroller), GTK_WIDGET (var_sheet));
1670 gtk_widget_show (GTK_WIDGET (var_sheet));
1672 gtk_widget_destroy (GTK_WIDGET (ia->formats.data_tree_view));
1673 ia->formats.data_tree_view = create_data_tree_view (
1675 GTK_CONTAINER (get_widget_assert (ia->asst.builder, "data-scroller")),
1678 pop_watch_cursor (ia);
1681 /* Clears the set of user-modified variables from IA's formats
1682 substructure. This discards user modifications to variable
1683 formats, thereby causing formats to revert to their
1686 clear_modified_vars (struct import_assistant *ia)
1688 struct formats_page *p = &ia->formats;
1691 for (i = 0; i < p->modified_var_cnt; i++)
1692 var_destroy (p->modified_vars[i]);
1693 free (p->modified_vars);
1694 p->modified_vars = NULL;
1695 p->modified_var_cnt = 0;
1698 /* Resets the formats page to its defaults, discarding user
1701 reset_formats_page (struct import_assistant *ia)
1703 clear_modified_vars (ia);
1704 prepare_formats_page (ia);
1707 /* Called when the user changes one of the variables in the
1710 on_variable_change (PsppireDict *dict, int dict_idx,
1711 struct import_assistant *ia)
1713 struct formats_page *p = &ia->formats;
1714 GtkTreeView *tv = ia->formats.data_tree_view;
1715 gint column_idx = dict_idx + 1;
1717 push_watch_cursor (ia);
1719 /* Remove previous column and replace with new column. */
1720 gtk_tree_view_remove_column (tv, gtk_tree_view_get_column (tv, column_idx));
1721 gtk_tree_view_insert_column (tv, make_data_column (ia, tv, false, dict_idx),
1724 /* Save a copy of the modified variable in modified_vars, so
1725 that its attributes will be preserved if we back up to the
1726 previous page with the Prev button and then come back
1728 if (dict_idx >= p->modified_var_cnt)
1731 p->modified_vars = xnrealloc (p->modified_vars, dict_idx + 1,
1732 sizeof *p->modified_vars);
1733 for (i = 0; i <= dict_idx; i++)
1734 p->modified_vars[i] = NULL;
1735 p->modified_var_cnt = dict_idx + 1;
1737 if (p->modified_vars[dict_idx])
1738 var_destroy (p->modified_vars[dict_idx]);
1739 p->modified_vars[dict_idx]
1740 = var_clone (psppire_dict_get_variable (dict, dict_idx));
1742 pop_watch_cursor (ia);
1745 /* Parses the contents of the field at (ROW,COLUMN) according to
1746 its variable format. If OUTPUTP is non-null, then *OUTPUTP
1747 receives the formatted output for that field (which must be
1748 freed with free). If TOOLTIPP is non-null, then *TOOLTIPP
1749 receives a message suitable for use in a tooltip, if one is
1750 needed, or a null pointer otherwise. Returns true if a
1751 tooltip message is needed, otherwise false. */
1753 parse_field (struct import_assistant *ia,
1754 size_t row, size_t column,
1755 char **outputp, char **tooltipp)
1757 struct substring field;
1759 struct variable *var;
1760 const struct fmt_spec *in;
1761 struct fmt_spec out;
1765 field = ia->separators.columns[column].contents[row];
1766 var = dict_get_var (ia->formats.dict, column);
1767 value_init (&val, var_get_width (var));
1768 in = var_get_print_format (var);
1769 out = fmt_for_output_from_input (in);
1771 if (field.string != NULL)
1775 error = data_in (field, C_ENCODING, in->type, &val, var_get_width (var),
1776 dict_get_encoding (ia->formats.dict));
1779 tooltip = xasprintf (_("Cannot parse field content `%.*s' as "
1781 (int) field.length, field.string,
1782 fmt_name (in->type), error);
1788 tooltip = xstrdup (_("This input line has too few separators "
1789 "to fill in this field."));
1790 value_set_missing (&val, var_get_width (var));
1792 if (outputp != NULL)
1794 *outputp = data_out (&val, dict_get_encoding (ia->formats.dict), &out);
1796 value_destroy (&val, var_get_width (var));
1798 ok = tooltip == NULL;
1799 if (tooltipp != NULL)
1800 *tooltipp = tooltip;
1806 /* Called to render one of the cells in the data preview tree
1809 render_output_cell (GtkTreeViewColumn *tree_column,
1810 GtkCellRenderer *cell,
1811 GtkTreeModel *model,
1815 struct import_assistant *ia = ia_;
1817 GValue gvalue = { 0, };
1820 ok = parse_field (ia,
1821 (text_import_model_iter_to_row (iter)
1822 + ia->first_line.skip_lines),
1823 GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1827 g_value_init (&gvalue, G_TYPE_STRING);
1828 g_value_take_string (&gvalue, output);
1829 g_object_set_property (G_OBJECT (cell), "text", &gvalue);
1830 g_value_unset (&gvalue);
1833 g_object_set (cell, "background-set", FALSE, (void *) NULL);
1836 "background", "red",
1837 "background-set", TRUE,
1841 /* Called to render a tooltip for one of the cells in the data
1842 preview tree view. */
1844 on_query_output_tooltip (GtkWidget *widget, gint wx, gint wy,
1845 gboolean keyboard_mode UNUSED,
1846 GtkTooltip *tooltip, struct import_assistant *ia)
1851 if (!get_tooltip_location (widget, wx, wy, ia, &row, &column))
1854 if (parse_field (ia, row, column, NULL, &text))
1857 gtk_tooltip_set_text (tooltip, text);
1862 /* Utility functions used by multiple pages of the assistant. */
1865 get_tooltip_location (GtkWidget *widget, gint wx, gint wy,
1866 const struct import_assistant *ia,
1867 size_t *row, size_t *column)
1869 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
1873 GtkTreeViewColumn *tree_column;
1874 GtkTreeModel *tree_model;
1877 /* Check that WIDGET is really visible on the screen before we
1878 do anything else. This is a bug fix for a sticky situation:
1879 when text_data_import_assistant() returns, it frees the data
1880 necessary to compose the tool tip message, but there may be
1881 a tool tip under preparation at that point (even if there is
1882 no visible tool tip) that will call back into us a little
1883 bit later. Perhaps the correct solution to this problem is
1884 to make the data related to the tool tips part of a GObject
1885 that only gets destroyed when all references are released,
1886 but this solution appears to be effective too. */
1887 if (!gtk_widget_get_mapped (widget))
1890 gtk_tree_view_convert_widget_to_bin_window_coords (tree_view,
1892 if (!gtk_tree_view_get_path_at_pos (tree_view, bx, by,
1893 &path, &tree_column, NULL, NULL))
1896 *column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1899 tree_model = gtk_tree_view_get_model (tree_view);
1900 ok = gtk_tree_model_get_iter (tree_model, &iter, path);
1901 gtk_tree_path_free (path);
1905 *row = text_import_model_iter_to_row (&iter) + ia->first_line.skip_lines;
1910 make_tree_view (const struct import_assistant *ia,
1912 GtkTreeView **tree_view)
1914 GtkTreeModel *model;
1916 *tree_view = GTK_TREE_VIEW (gtk_tree_view_new ());
1917 model = GTK_TREE_MODEL (text_import_model_new (
1918 ia->file.lines + first_line,
1919 ia->file.line_cnt - first_line, first_line));
1920 gtk_tree_view_set_model (*tree_view, model);
1922 add_line_number_column (ia, *tree_view);
1926 add_line_number_column (const struct import_assistant *ia,
1927 GtkTreeView *treeview)
1929 GtkTreeViewColumn *column;
1931 column = gtk_tree_view_column_new_with_attributes (
1932 _("Line"), ia->asst.prop_renderer,
1933 "text", TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER,
1935 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
1936 gtk_tree_view_column_set_fixed_width (
1937 column, get_monospace_width (treeview, ia->asst.prop_renderer, 5));
1938 gtk_tree_view_append_column (treeview, column);
1942 get_monospace_width (GtkTreeView *treeview, GtkCellRenderer *renderer,
1949 ds_put_byte_multiple (&s, '0', char_cnt);
1950 ds_put_byte (&s, ' ');
1951 width = get_string_width (treeview, renderer, ds_cstr (&s));
1958 get_string_width (GtkTreeView *treeview, GtkCellRenderer *renderer,
1962 g_object_set (G_OBJECT (renderer), "text", string, (void *) NULL);
1963 gtk_cell_renderer_get_size (renderer, GTK_WIDGET (treeview),
1964 NULL, NULL, NULL, &width, NULL);
1968 static GtkTreeViewColumn *
1969 make_data_column (struct import_assistant *ia, GtkTreeView *tree_view,
1970 bool input, gint dict_idx)
1972 struct variable *var = NULL;
1973 struct column *column = NULL;
1975 gint content_width, header_width;
1976 GtkTreeViewColumn *tree_column;
1980 column = &ia->separators.columns[dict_idx];
1982 var = dict_get_var (ia->formats.dict, dict_idx);
1984 name = escape_underscores (input ? column->name : var_get_name (var));
1985 char_cnt = input ? column->width : var_get_print_format (var)->w;
1986 content_width = get_monospace_width (tree_view, ia->asst.fixed_renderer,
1988 header_width = get_string_width (tree_view, ia->asst.prop_renderer,
1991 tree_column = gtk_tree_view_column_new ();
1992 g_object_set_data (G_OBJECT (tree_column), "column-number",
1993 GINT_TO_POINTER (dict_idx));
1994 gtk_tree_view_column_set_title (tree_column, name);
1995 gtk_tree_view_column_pack_start (tree_column, ia->asst.fixed_renderer,
1997 gtk_tree_view_column_set_cell_data_func (
1998 tree_column, ia->asst.fixed_renderer,
1999 input ? render_input_cell : render_output_cell, ia, NULL);
2000 gtk_tree_view_column_set_sizing (tree_column, GTK_TREE_VIEW_COLUMN_FIXED);
2001 gtk_tree_view_column_set_fixed_width (tree_column, MAX (content_width,
2009 static GtkTreeView *
2010 create_data_tree_view (bool input, GtkContainer *parent,
2011 struct import_assistant *ia)
2013 GtkTreeView *tree_view;
2016 make_tree_view (ia, ia->first_line.skip_lines, &tree_view);
2017 gtk_tree_selection_set_mode (gtk_tree_view_get_selection (tree_view),
2018 GTK_SELECTION_NONE);
2020 for (i = 0; i < ia->separators.column_cnt; i++)
2021 gtk_tree_view_append_column (tree_view,
2022 make_data_column (ia, tree_view, input, i));
2024 g_object_set (G_OBJECT (tree_view), "has-tooltip", TRUE, (void *) NULL);
2025 g_signal_connect (tree_view, "query-tooltip",
2026 G_CALLBACK (input ? on_query_input_tooltip
2027 : on_query_output_tooltip), ia);
2028 gtk_tree_view_set_fixed_height_mode (tree_view, true);
2030 gtk_container_add (parent, GTK_WIDGET (tree_view));
2031 gtk_widget_show (GTK_WIDGET (tree_view));
2037 escape_underscores (const char *in)
2039 char *out = xmalloc (2 * strlen (in) + 1);
2043 for (; *in != '\0'; in++)
2054 /* TextImportModel, a GtkTreeModel implementation used by some
2055 pages of the assistant. */
2057 #define G_TYPE_TEXT_IMPORT_MODEL (text_import_model_get_type ())
2058 #define TEXT_IMPORT_MODEL(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), G_TYPE_TEXT_IMPORT_MODEL, TextImportModel))
2059 #define TEXT_IMPORT_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), G_TYPE_TEXT_IMPORT_MODEL, TextImportModelClass))
2060 #define IS_TEXT_IMPORT_MODEL(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), G_TYPE_TEXT_IMPORT_MODEL))
2061 #define IS_TEXT_IMPORT_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), G_TYPE_TEXT_IMPORT_MODEL))
2062 #define TEXT_IMPORT_MODEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), G_TYPE_TEXT_IMPORT_MODEL, TextImportModelClass))
2064 /* Random number used in 'stamp' member of GtkTreeIter. */
2065 #define TREE_MODEL_STAMP 0x7efd67d3
2067 struct TextImportModel
2070 struct string *lines;
2075 struct TextImportModelClass
2077 GObjectClass parent_class;
2080 GType text_import_model_get_type (void);
2081 static void text_import_model_tree_model_init (gpointer iface, gpointer data);
2084 text_import_model_get_type (void)
2086 static GType object_type = 0;
2090 static const GTypeInfo object_info = {
2091 sizeof (TextImportModelClass),
2092 (GBaseInitFunc) NULL,
2093 (GBaseFinalizeFunc) NULL,
2094 NULL, /* class_init */
2095 NULL, /* class_finalize */
2096 NULL, /* class_data */
2097 sizeof (TextImportModel),
2098 0, /* n_preallocs */
2099 NULL, /* instance_init */
2102 static const GInterfaceInfo tree_model_info = {
2103 text_import_model_tree_model_init,
2108 object_type = g_type_register_static (G_TYPE_OBJECT,
2112 g_type_add_interface_static (object_type, GTK_TYPE_TREE_MODEL,
2122 /* Creates and returns a new TextImportModel that contains the
2123 LINE_CNT lines in LINES. The lines before FIRST_LINE in LINES
2124 are not part of the model, but they are included in the line
2125 numbers in the TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER column.
2127 The caller retains responsibility for freeing LINES and must
2128 ensure that its lifetime and that of the strings that it
2129 contains exceeds that of the TextImportModel. */
2131 text_import_model_new (struct string *lines, size_t line_cnt,
2134 TextImportModel *new_text_import_model
2135 = g_object_new (G_TYPE_TEXT_IMPORT_MODEL, NULL);
2136 new_text_import_model->lines = lines;
2137 new_text_import_model->line_cnt = line_cnt;
2138 new_text_import_model->first_line = first_line;
2139 return new_text_import_model;
2144 tree_model_iter_has_child (GtkTreeModel *tree_model,
2151 tree_model_iter_parent (GtkTreeModel *tree_model,
2158 static GtkTreeModelFlags
2159 tree_model_get_flags (GtkTreeModel *model)
2161 g_return_val_if_fail (IS_TEXT_IMPORT_MODEL (model), (GtkTreeModelFlags) 0);
2163 return GTK_TREE_MODEL_LIST_ONLY | GTK_TREE_MODEL_ITERS_PERSIST;
2168 tree_model_n_columns (GtkTreeModel *model)
2174 tree_model_column_type (GtkTreeModel *model, gint index)
2176 return (index == TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER ? G_TYPE_INT
2177 : index == TEXT_IMPORT_MODEL_COLUMN_LINE ? G_TYPE_STRING
2182 init_iter (TextImportModel *list, gint idx, GtkTreeIter *iter)
2184 if (idx < 0 || idx >= list->line_cnt)
2187 iter->user_data = GINT_TO_POINTER (-1);
2192 iter->stamp = TREE_MODEL_STAMP;
2193 iter->user_data = GINT_TO_POINTER (idx);
2199 tree_model_get_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreePath *path)
2201 gint *indices, depth;
2203 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2205 g_return_val_if_fail (path, FALSE);
2207 indices = gtk_tree_path_get_indices (path);
2208 depth = gtk_tree_path_get_depth (path);
2210 g_return_val_if_fail (depth == 1, FALSE);
2212 return init_iter (list, indices[0], iter);
2217 tree_model_iter_next (GtkTreeModel *model, GtkTreeIter *iter)
2219 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2222 assert (iter->stamp == TREE_MODEL_STAMP);
2224 idx = GPOINTER_TO_INT (iter->user_data);
2225 return init_iter (list, idx == -1 ? -1 : idx + 1, iter);
2228 static GtkTreePath *
2229 tree_model_get_path (GtkTreeModel *model, GtkTreeIter *iter)
2233 g_return_val_if_fail (iter->stamp == TREE_MODEL_STAMP, FALSE);
2235 path = gtk_tree_path_new ();
2236 gtk_tree_path_append_index (path, GPOINTER_TO_INT (iter->user_data));
2242 tree_model_get_value (GtkTreeModel *model, GtkTreeIter *iter,
2243 gint column, GValue *value)
2245 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2248 g_return_if_fail (iter->stamp == TREE_MODEL_STAMP);
2250 idx = GPOINTER_TO_INT (iter->user_data);
2251 assert (idx >= 0 && idx < list->line_cnt);
2255 g_value_init (value, G_TYPE_INT);
2256 g_value_set_int (value, idx + list->first_line + 1);
2260 g_value_init (value, G_TYPE_STRING);
2261 g_value_set_static_string (value, ds_cstr (&list->lines[idx]));
2266 tree_model_iter_children (GtkTreeModel *tree_model,
2268 GtkTreeIter *parent)
2274 tree_model_n_children (GtkTreeModel *model, GtkTreeIter *iter)
2276 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2278 return iter == NULL ? list->line_cnt : 0;
2282 tree_model_nth_child (GtkTreeModel *model, GtkTreeIter *iter,
2283 GtkTreeIter *parent, gint n)
2285 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2286 g_return_val_if_fail (IS_TEXT_IMPORT_MODEL (model), FALSE);
2290 return init_iter (list, n, iter);
2294 text_import_model_tree_model_init (gpointer iface_, gpointer data UNUSED)
2296 GtkTreeModelIface *iface = (GtkTreeModelIface *) iface_;
2298 iface->get_flags = tree_model_get_flags;
2299 iface->get_n_columns = tree_model_n_columns;
2300 iface->get_column_type = tree_model_column_type;
2301 iface->get_iter = tree_model_get_iter;
2302 iface->iter_next = tree_model_iter_next;
2303 iface->get_path = tree_model_get_path;
2304 iface->get_value = tree_model_get_value;
2306 iface->iter_children = tree_model_iter_children;
2307 iface->iter_has_child = tree_model_iter_has_child;
2308 iface->iter_n_children = tree_model_n_children;
2309 iface->iter_nth_child = tree_model_nth_child;
2310 iface->iter_parent = tree_model_iter_parent;
2314 text_import_model_iter_to_row (const GtkTreeIter *iter)
2316 assert (iter->stamp == TREE_MODEL_STAMP);
2317 return GPOINTER_TO_INT (iter->user_data);
2320 /* Increments the "watch cursor" level, setting the cursor for
2321 the assistant window to a watch face to indicate to the user
2322 that the ongoing operation may take some time. */
2324 push_watch_cursor (struct import_assistant *ia)
2326 if (++ia->asst.watch_cursor == 1)
2328 GtkWidget *widget = GTK_WIDGET (ia->asst.assistant);
2329 GdkDisplay *display = gtk_widget_get_display (widget);
2330 GdkCursor *cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
2331 gdk_window_set_cursor (widget->window, cursor);
2332 gdk_cursor_unref (cursor);
2333 gdk_display_flush (display);
2337 /* Decrements the "watch cursor" level. If the level reaches
2338 zero, the cursor is reset to its default shape. */
2340 pop_watch_cursor (struct import_assistant *ia)
2342 if (--ia->asst.watch_cursor == 0)
2344 GtkWidget *widget = GTK_WIDGET (ia->asst.assistant);
2345 gdk_window_set_cursor (widget->window, NULL);