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;
860 gchar *title = _("Text");
862 make_tree_view (ia, 0, &tree_view);
864 column = gtk_tree_view_column_new_with_attributes
866 title, ia->asst.fixed_renderer,
867 "text", TEXT_IMPORT_MODEL_COLUMN_LINE,
870 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
873 for (i = 0; i < ia->file.line_cnt; i++)
875 size_t w = ds_length (&ia->file.lines[i]);
876 max_line_length = MAX (max_line_length, w);
879 content_width = get_monospace_width (tree_view, ia->asst.fixed_renderer,
881 header_width = get_string_width (tree_view, ia->asst.prop_renderer, title);
882 gtk_tree_view_column_set_fixed_width (column, MAX (content_width,
884 gtk_tree_view_append_column (tree_view, column);
886 gtk_tree_view_set_fixed_height_mode (tree_view, true);
888 gtk_container_add (parent, GTK_WIDGET (tree_view));
889 gtk_widget_show (GTK_WIDGET (tree_view));
894 /* Called when the line selected in the first_line tree view
897 on_first_line_change (GtkTreeSelection *selection UNUSED,
898 struct import_assistant *ia)
903 /* Called when the checkbox that indicates whether variable
904 names are in the row above the first line is toggled. */
906 on_variable_names_cb_toggle (GtkToggleButton *variable_names_cb UNUSED,
907 struct import_assistant *ia)
912 /* Sets the widgets to match IA's first_line substructure. */
914 set_first_line (struct import_assistant *ia)
918 path = gtk_tree_path_new_from_indices (ia->first_line.skip_lines, -1);
919 gtk_tree_view_set_cursor (GTK_TREE_VIEW (ia->first_line.tree_view),
921 gtk_tree_path_free (path);
923 gtk_toggle_button_set_active (
924 GTK_TOGGLE_BUTTON (ia->first_line.variable_names_cb),
925 ia->first_line.variable_names);
926 gtk_widget_set_sensitive (ia->first_line.variable_names_cb,
927 ia->first_line.skip_lines > 0);
930 /* Sets IA's first_line substructure to match the widgets. */
932 get_first_line (struct import_assistant *ia)
934 GtkTreeSelection *selection;
938 selection = gtk_tree_view_get_selection (ia->first_line.tree_view);
939 if (gtk_tree_selection_get_selected (selection, &model, &iter))
941 GtkTreePath *path = gtk_tree_model_get_path (model, &iter);
942 int row = gtk_tree_path_get_indices (path)[0];
943 gtk_tree_path_free (path);
945 ia->first_line.skip_lines = row;
946 ia->first_line.variable_names =
947 (ia->first_line.skip_lines > 0
948 && gtk_toggle_button_get_active (
949 GTK_TOGGLE_BUTTON (ia->first_line.variable_names_cb)));
951 gtk_widget_set_sensitive (ia->first_line.variable_names_cb,
952 ia->first_line.skip_lines > 0);
955 /* The "separators" page of the assistant. */
957 static void revise_fields_preview (struct import_assistant *ia);
958 static void choose_likely_separators (struct import_assistant *ia);
959 static void find_commonest_chars (unsigned long int histogram[UCHAR_MAX + 1],
960 const char *targets, const char *def,
961 struct string *result);
962 static void clear_fields (struct import_assistant *ia);
963 static void revise_fields_preview (struct import_assistant *);
964 static void set_separators (struct import_assistant *);
965 static void get_separators (struct import_assistant *);
966 static void on_separators_custom_entry_notify (GObject *UNUSED,
968 struct import_assistant *);
969 static void on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
970 struct import_assistant *);
971 static void on_quote_combo_change (GtkComboBox *combo,
972 struct import_assistant *);
973 static void on_quote_cb_toggle (GtkToggleButton *quote_cb,
974 struct import_assistant *);
975 static void on_separator_toggle (GtkToggleButton *, struct import_assistant *);
976 static void render_input_cell (GtkTreeViewColumn *tree_column,
977 GtkCellRenderer *cell,
978 GtkTreeModel *model, GtkTreeIter *iter,
980 static gboolean on_query_input_tooltip (GtkWidget *widget, gint wx, gint wy,
981 gboolean keyboard_mode UNUSED,
983 struct import_assistant *);
985 /* A common field separator and its identifying name. */
988 const char *name; /* Name (for use with get_widget_assert). */
989 int c; /* Separator character. */
992 /* All the separators in the dialog box. */
993 static const struct separator separators[] =
1005 #define SEPARATOR_CNT (sizeof separators / sizeof *separators)
1008 set_quote_list (GtkComboBoxEntry *cb)
1010 GtkListStore *list = gtk_list_store_new (1, G_TYPE_STRING);
1013 const gchar *seperator[3] = {"'\"", "\'", "\""};
1015 for (i = 0; i < 3; i++)
1017 const gchar *s = seperator[i];
1019 /* Add a new row to the model */
1020 gtk_list_store_append (list, &iter);
1021 gtk_list_store_set (list, &iter,
1027 gtk_combo_box_set_model (GTK_COMBO_BOX (cb), GTK_TREE_MODEL (list));
1029 gtk_combo_box_entry_set_text_column (cb, 0);
1032 /* Initializes IA's separators substructure. */
1034 init_separators_page (struct import_assistant *ia)
1036 GtkBuilder *builder = ia->asst.builder;
1037 struct separators_page *p = &ia->separators;
1040 choose_likely_separators (ia);
1042 p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Separators"),
1043 GTK_ASSISTANT_PAGE_CONTENT);
1044 p->custom_cb = get_widget_assert (builder, "custom-cb");
1045 p->custom_entry = get_widget_assert (builder, "custom-entry");
1046 p->quote_combo = get_widget_assert (builder, "quote-combo");
1047 p->quote_entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (p->quote_combo)));
1048 p->quote_cb = get_widget_assert (builder, "quote-cb");
1049 p->escape_cb = get_widget_assert (builder, "escape");
1051 set_separators (ia);
1052 set_quote_list (GTK_COMBO_BOX_ENTRY (p->quote_combo));
1053 p->fields_tree_view = GTK_TREE_VIEW (get_widget_assert (builder, "fields"));
1054 g_signal_connect (p->quote_combo, "changed",
1055 G_CALLBACK (on_quote_combo_change), ia);
1056 g_signal_connect (p->quote_cb, "toggled",
1057 G_CALLBACK (on_quote_cb_toggle), ia);
1058 g_signal_connect (p->custom_entry, "notify::text",
1059 G_CALLBACK (on_separators_custom_entry_notify), ia);
1060 g_signal_connect (p->custom_cb, "toggled",
1061 G_CALLBACK (on_separators_custom_cb_toggle), ia);
1062 for (i = 0; i < SEPARATOR_CNT; i++)
1063 g_signal_connect (get_widget_assert (builder, separators[i].name),
1064 "toggled", G_CALLBACK (on_separator_toggle), ia);
1065 g_signal_connect (p->escape_cb, "toggled",
1066 G_CALLBACK (on_separator_toggle), ia);
1069 /* Frees IA's separators substructure. */
1071 destroy_separators_page (struct import_assistant *ia)
1073 struct separators_page *s = &ia->separators;
1075 ds_destroy (&s->separators);
1076 ds_destroy (&s->quotes);
1080 /* Called just before the separators page becomes visible in the
1083 prepare_separators_page (struct import_assistant *ia)
1085 revise_fields_preview (ia);
1088 /* Called when the Reset button is clicked on the separators
1089 page, resets the separators to the defaults. */
1091 reset_separators_page (struct import_assistant *ia)
1093 choose_likely_separators (ia);
1094 set_separators (ia);
1097 /* Frees and clears the column data in IA's separators
1100 clear_fields (struct import_assistant *ia)
1102 struct separators_page *s = &ia->separators;
1104 if (s->column_cnt > 0)
1109 for (row = 0; row < ia->file.line_cnt; row++)
1111 const struct string *line = &ia->file.lines[row];
1112 const char *line_start = ds_data (line);
1113 const char *line_end = ds_end (line);
1115 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1117 char *s = ss_data (col->contents[row]);
1118 if (!(s >= line_start && s <= line_end))
1119 ss_dealloc (&col->contents[row]);
1123 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1126 free (col->contents);
1135 /* Breaks the file data in IA into columns based on the
1136 separators set in IA's separators substructure. */
1138 split_fields (struct import_assistant *ia)
1140 struct separators_page *s = &ia->separators;
1141 size_t columns_allocated;
1147 /* Is space in the set of separators? */
1148 space_sep = ss_find_char (ds_ss (&s->separators), ' ') != SIZE_MAX;
1150 /* Split all the lines, not just those from
1151 ia->first_line.skip_lines on, so that we split the line that
1152 contains variables names if ia->first_line.variable_names is
1154 columns_allocated = 0;
1155 for (row = 0; row < ia->file.line_cnt; row++)
1157 struct string *line = &ia->file.lines[row];
1158 struct substring text = ds_ss (line);
1161 for (column_idx = 0; ; column_idx++)
1163 struct substring field;
1164 struct column *column;
1167 ss_ltrim (&text, ss_cstr (" "));
1168 if (ss_is_empty (text))
1170 if (column_idx != 0)
1174 else if (!ds_is_empty (&s->quotes)
1175 && ds_find_char (&s->quotes, text.string[0]) != SIZE_MAX)
1177 int quote = ss_get_char (&text);
1179 ss_get_until (&text, quote, &field);
1186 while ((c = ss_get_char (&text)) != EOF)
1188 ds_put_char (&s, c);
1189 else if (ss_match_char (&text, quote))
1190 ds_put_char (&s, quote);
1197 ss_get_chars (&text, ss_cspan (text, ds_ss (&s->separators)),
1200 if (column_idx >= s->column_cnt)
1202 struct column *column;
1204 if (s->column_cnt >= columns_allocated)
1205 s->columns = x2nrealloc (s->columns, &columns_allocated,
1206 sizeof *s->columns);
1207 column = &s->columns[s->column_cnt++];
1208 column->name = NULL;
1210 column->contents = xcalloc (ia->file.line_cnt,
1211 sizeof *column->contents);
1213 column = &s->columns[column_idx];
1214 column->contents[row] = field;
1215 if (ss_length (field) > column->width)
1216 column->width = ss_length (field);
1219 ss_ltrim (&text, ss_cstr (" "));
1220 if (ss_is_empty (text))
1222 if (ss_find_char (ds_ss (&s->separators), ss_first (text))
1224 ss_advance (&text, 1);
1229 /* Chooses a name for each column on the separators page */
1231 choose_column_names (struct import_assistant *ia)
1233 const struct first_line_page *f = &ia->first_line;
1234 struct separators_page *s = &ia->separators;
1235 struct dictionary *dict;
1236 unsigned long int generated_name_count = 0;
1240 dict = dict_create ();
1241 name_row = f->variable_names && f->skip_lines ? f->skip_lines : 0;
1242 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1244 char name[VAR_NAME_LEN + 1];
1247 hint = name_row ? ss_xstrdup (col->contents[name_row - 1]) : NULL;
1248 if (!dict_make_unique_var_name (dict, hint, &generated_name_count, name))
1252 col->name = xstrdup (name);
1253 dict_create_var_assert (dict, name, 0);
1255 dict_destroy (dict);
1258 /* Picks the most likely separator and quote characters based on
1261 choose_likely_separators (struct import_assistant *ia)
1263 unsigned long int histogram[UCHAR_MAX + 1] = { 0 };
1266 /* Construct a histogram of all the characters used in the
1268 for (row = 0; row < ia->file.line_cnt; row++)
1270 struct substring line = ds_ss (&ia->file.lines[row]);
1271 size_t length = ss_length (line);
1273 for (i = 0; i < length; i++)
1274 histogram[(unsigned char) line.string[i]]++;
1277 find_commonest_chars (histogram, "\"'", "", &ia->separators.quotes);
1278 find_commonest_chars (histogram, ",;:/|!\t-", ",",
1279 &ia->separators.separators);
1280 ia->separators.escape = true;
1283 /* Chooses the most common character among those in TARGETS,
1284 based on the frequency data in HISTOGRAM, and stores it in
1285 RESULT. If there is a tie for the most common character among
1286 those in TARGETS, the earliest character is chosen. If none
1287 of the TARGETS appear at all, then DEF is used as a
1290 find_commonest_chars (unsigned long int histogram[UCHAR_MAX + 1],
1291 const char *targets, const char *def,
1292 struct string *result)
1294 unsigned char max = 0;
1295 unsigned long int max_count = 0;
1297 for (; *targets != '\0'; targets++)
1299 unsigned char c = *targets;
1300 unsigned long int count = histogram[c];
1301 if (count > max_count)
1310 ds_put_char (result, max);
1313 ds_assign_cstr (result, def);
1316 /* Revises the contents of the fields tree view based on the
1317 currently chosen set of separators. */
1319 revise_fields_preview (struct import_assistant *ia)
1323 push_watch_cursor (ia);
1325 w = GTK_WIDGET (ia->separators.fields_tree_view);
1326 gtk_widget_destroy (w);
1327 get_separators (ia);
1329 choose_column_names (ia);
1330 ia->separators.fields_tree_view = create_data_tree_view (
1332 GTK_CONTAINER (get_widget_assert (ia->asst.builder, "fields-scroller")),
1335 pop_watch_cursor (ia);
1338 /* Sets the widgets to match IA's separators substructure. */
1340 set_separators (struct import_assistant *ia)
1342 struct separators_page *s = &ia->separators;
1344 struct string custom;
1349 ds_init_empty (&custom);
1351 for (i = 0; i < ds_length (&s->separators); i++)
1353 unsigned char c = ds_at (&s->separators, i);
1356 for (j = 0; j < SEPARATOR_CNT; j++)
1358 const struct separator *s = &separators[j];
1366 ds_put_char (&custom, c);
1370 for (i = 0; i < SEPARATOR_CNT; i++)
1372 const struct separator *s = &separators[i];
1373 GtkWidget *button = get_widget_assert (ia->asst.builder, s->name);
1374 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
1375 (seps & (1u << i)) != 0);
1377 any_custom = !ds_is_empty (&custom);
1378 gtk_entry_set_text (GTK_ENTRY (s->custom_entry), ds_cstr (&custom));
1379 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->custom_cb),
1381 gtk_widget_set_sensitive (s->custom_entry, any_custom);
1382 ds_destroy (&custom);
1384 any_quotes = !ds_is_empty (&s->quotes);
1386 gtk_entry_set_text (s->quote_entry,
1387 any_quotes ? ds_cstr (&s->quotes) : "\"");
1388 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->quote_cb),
1390 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->escape_cb),
1392 gtk_widget_set_sensitive (s->quote_combo, any_quotes);
1393 gtk_widget_set_sensitive (s->escape_cb, any_quotes);
1396 /* Sets IA's separators substructure to match the widgets. */
1398 get_separators (struct import_assistant *ia)
1400 struct separators_page *s = &ia->separators;
1403 ds_clear (&s->separators);
1404 for (i = 0; i < SEPARATOR_CNT; i++)
1406 const struct separator *sep = &separators[i];
1407 GtkWidget *button = get_widget_assert (ia->asst.builder, sep->name);
1408 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
1409 ds_put_char (&s->separators, sep->c);
1412 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->custom_cb)))
1413 ds_put_cstr (&s->separators,
1414 gtk_entry_get_text (GTK_ENTRY (s->custom_entry)));
1416 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->quote_cb)))
1418 gchar *text = gtk_combo_box_get_active_text (
1419 GTK_COMBO_BOX (s->quote_combo));
1420 ds_assign_cstr (&s->quotes, text);
1424 ds_clear (&s->quotes);
1425 s->escape = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->escape_cb));
1428 /* Called when the user changes the entry field for custom
1431 on_separators_custom_entry_notify (GObject *gobject UNUSED,
1432 GParamSpec *arg1 UNUSED,
1433 struct import_assistant *ia)
1435 revise_fields_preview (ia);
1438 /* Called when the user toggles the checkbox that enables custom
1441 on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
1442 struct import_assistant *ia)
1444 bool is_active = gtk_toggle_button_get_active (custom_cb);
1445 gtk_widget_set_sensitive (ia->separators.custom_entry, is_active);
1446 revise_fields_preview (ia);
1449 /* Called when the user changes the selection in the combo box
1450 that selects a quote character. */
1452 on_quote_combo_change (GtkComboBox *combo, struct import_assistant *ia)
1454 revise_fields_preview (ia);
1457 /* Called when the user toggles the checkbox that enables
1460 on_quote_cb_toggle (GtkToggleButton *quote_cb, struct import_assistant *ia)
1462 bool is_active = gtk_toggle_button_get_active (quote_cb);
1463 gtk_widget_set_sensitive (ia->separators.quote_combo, is_active);
1464 gtk_widget_set_sensitive (ia->separators.escape_cb, is_active);
1465 revise_fields_preview (ia);
1468 /* Called when the user toggles one of the separators
1471 on_separator_toggle (GtkToggleButton *toggle UNUSED,
1472 struct import_assistant *ia)
1474 revise_fields_preview (ia);
1477 /* Called to render one of the cells in the fields preview tree
1480 render_input_cell (GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1481 GtkTreeModel *model, GtkTreeIter *iter,
1484 struct import_assistant *ia = ia_;
1485 struct substring field;
1489 column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1491 row = text_import_model_iter_to_row (iter) + ia->first_line.skip_lines;
1492 field = ia->separators.columns[column].contents[row];
1493 if (field.string != NULL)
1495 GValue text = {0, };
1496 g_value_init (&text, G_TYPE_STRING);
1497 g_value_take_string (&text, ss_xstrdup (field));
1498 g_object_set_property (G_OBJECT (cell), "text", &text);
1499 g_value_unset (&text);
1500 g_object_set (cell, "background-set", FALSE, (void *) NULL);
1505 "background", "red",
1506 "background-set", TRUE,
1510 /* Called to render a tooltip on one of the cells in the fields
1511 preview tree view. */
1513 on_query_input_tooltip (GtkWidget *widget, gint wx, gint wy,
1514 gboolean keyboard_mode UNUSED,
1515 GtkTooltip *tooltip, struct import_assistant *ia)
1519 if (!get_tooltip_location (widget, wx, wy, ia, &row, &column))
1522 if (ia->separators.columns[column].contents[row].string != NULL)
1525 gtk_tooltip_set_text (tooltip,
1526 _("This input line has too few separators "
1527 "to fill in this field."));
1531 /* The "formats" page of the assistant. */
1533 static void on_variable_change (PsppireDict *dict, int idx,
1534 struct import_assistant *);
1535 static void clear_modified_vars (struct import_assistant *);
1537 /* Initializes IA's formats substructure. */
1539 init_formats_page (struct import_assistant *ia)
1541 GtkBuilder *builder = ia->asst.builder;
1542 struct formats_page *p = &ia->formats;
1544 p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Formats"),
1545 GTK_ASSISTANT_PAGE_CONFIRM);
1546 p->data_tree_view = GTK_TREE_VIEW (get_widget_assert (builder, "data"));
1547 p->modified_vars = NULL;
1548 p->modified_var_cnt = 0;
1552 /* Frees IA's formats substructure. */
1554 destroy_formats_page (struct import_assistant *ia)
1556 struct formats_page *p = &ia->formats;
1558 if (p->psppire_dict != NULL)
1560 /* This destroys p->dict also. */
1561 g_object_unref (p->psppire_dict);
1563 clear_modified_vars (ia);
1566 /* Called just before the formats page of the assistant is
1569 prepare_formats_page (struct import_assistant *ia)
1571 struct dictionary *dict;
1572 PsppireDict *psppire_dict;
1573 PsppireVarStore *var_store;
1574 GtkBin *vars_scroller;
1575 GtkWidget *old_var_sheet;
1576 PsppireVarSheet *var_sheet;
1577 struct separators_page *s = &ia->separators;
1578 struct formats_page *p = &ia->formats;
1579 struct fmt_guesser *fg;
1580 unsigned long int number = 0;
1583 push_watch_cursor (ia);
1585 dict = dict_create ();
1586 fg = fmt_guesser_create ();
1587 for (column_idx = 0; column_idx < s->column_cnt; column_idx++)
1589 struct variable *modified_var;
1590 char name[VAR_NAME_LEN + 1];
1592 modified_var = (column_idx < p->modified_var_cnt
1593 ? p->modified_vars[column_idx] : NULL);
1594 if (modified_var == NULL)
1596 struct column *column = &s->columns[column_idx];
1597 struct variable *var;
1598 struct fmt_spec format;
1601 /* Choose variable name. */
1602 if (!dict_make_unique_var_name (dict, column->name, &number, name))
1605 /* Choose variable format. */
1606 fmt_guesser_clear (fg);
1607 for (row = ia->first_line.skip_lines; row < ia->file.line_cnt; row++)
1608 fmt_guesser_add (fg, column->contents[row]);
1609 fmt_guesser_guess (fg, &format);
1610 fmt_fix_input (&format);
1612 /* Create variable. */
1613 var = dict_create_var_assert (dict, name, fmt_var_width (&format));
1614 var_set_both_formats (var, &format);
1618 if (!dict_make_unique_var_name (dict, var_get_name (modified_var),
1621 dict_clone_var_as_assert (dict, modified_var, name);
1624 fmt_guesser_destroy (fg);
1626 psppire_dict = psppire_dict_new_from_dict (dict);
1627 g_signal_connect (psppire_dict, "variable_changed",
1628 G_CALLBACK (on_variable_change), ia);
1629 ia->formats.dict = dict;
1630 ia->formats.psppire_dict = psppire_dict;
1632 /* XXX: PsppireVarStore doesn't hold a reference to
1633 psppire_dict for now, but it should. After it does, we
1634 should g_object_ref the psppire_dict here, since we also
1635 hold a reference via ia->formats.dict. */
1636 var_store = psppire_var_store_new (psppire_dict);
1637 g_object_set (var_store,
1638 "format-type", PSPPIRE_VAR_STORE_INPUT_FORMATS,
1640 var_sheet = PSPPIRE_VAR_SHEET (psppire_var_sheet_new ());
1641 g_object_set (var_sheet,
1643 "may-create-vars", FALSE,
1646 vars_scroller = GTK_BIN (get_widget_assert (ia->asst.builder, "vars-scroller"));
1647 old_var_sheet = gtk_bin_get_child (vars_scroller);
1648 if (old_var_sheet != NULL)
1649 gtk_widget_destroy (old_var_sheet);
1650 gtk_container_add (GTK_CONTAINER (vars_scroller), GTK_WIDGET (var_sheet));
1651 gtk_widget_show (GTK_WIDGET (var_sheet));
1653 gtk_widget_destroy (GTK_WIDGET (ia->formats.data_tree_view));
1654 ia->formats.data_tree_view = create_data_tree_view (
1656 GTK_CONTAINER (get_widget_assert (ia->asst.builder, "data-scroller")),
1659 pop_watch_cursor (ia);
1662 /* Clears the set of user-modified variables from IA's formats
1663 substructure. This discards user modifications to variable
1664 formats, thereby causing formats to revert to their
1667 clear_modified_vars (struct import_assistant *ia)
1669 struct formats_page *p = &ia->formats;
1672 for (i = 0; i < p->modified_var_cnt; i++)
1673 var_destroy (p->modified_vars[i]);
1674 free (p->modified_vars);
1675 p->modified_vars = NULL;
1676 p->modified_var_cnt = 0;
1679 /* Resets the formats page to its defaults, discarding user
1682 reset_formats_page (struct import_assistant *ia)
1684 clear_modified_vars (ia);
1685 prepare_formats_page (ia);
1688 /* Called when the user changes one of the variables in the
1691 on_variable_change (PsppireDict *dict, int dict_idx,
1692 struct import_assistant *ia)
1694 struct formats_page *p = &ia->formats;
1695 GtkTreeView *tv = ia->formats.data_tree_view;
1696 gint column_idx = dict_idx + 1;
1698 push_watch_cursor (ia);
1700 /* Remove previous column and replace with new column. */
1701 gtk_tree_view_remove_column (tv, gtk_tree_view_get_column (tv, column_idx));
1702 gtk_tree_view_insert_column (tv, make_data_column (ia, tv, false, dict_idx),
1705 /* Save a copy of the modified variable in modified_vars, so
1706 that its attributes will be preserved if we back up to the
1707 previous page with the Prev button and then come back
1709 if (dict_idx >= p->modified_var_cnt)
1712 p->modified_vars = xnrealloc (p->modified_vars, dict_idx + 1,
1713 sizeof *p->modified_vars);
1714 for (i = 0; i <= dict_idx; i++)
1715 p->modified_vars[i] = NULL;
1716 p->modified_var_cnt = dict_idx + 1;
1718 if (p->modified_vars[dict_idx])
1719 var_destroy (p->modified_vars[dict_idx]);
1720 p->modified_vars[dict_idx]
1721 = var_clone (psppire_dict_get_variable (dict, dict_idx));
1723 pop_watch_cursor (ia);
1726 /* Parses the contents of the field at (ROW,COLUMN) according to
1727 its variable format. If OUTPUTP is non-null, then *OUTPUTP
1728 receives the formatted output for that field (which must be
1729 freed with free). If TOOLTIPP is non-null, then *TOOLTIPP
1730 receives a message suitable for use in a tooltip, if one is
1731 needed, or a null pointer otherwise. Returns true if a
1732 tooltip message is needed, otherwise false. */
1734 parse_field (struct import_assistant *ia,
1735 size_t row, size_t column,
1736 char **outputp, char **tooltipp)
1738 struct substring field;
1740 struct variable *var;
1741 const struct fmt_spec *in;
1742 struct fmt_spec out;
1746 field = ia->separators.columns[column].contents[row];
1747 var = dict_get_var (ia->formats.dict, column);
1748 value_init (&val, var_get_width (var));
1749 in = var_get_print_format (var);
1750 out = fmt_for_output_from_input (in);
1752 if (field.string != NULL)
1756 if (!data_in (field, LEGACY_NATIVE, in->type, 0, 0, 0,
1758 &val, var_get_width (var)))
1760 char fmt_string[FMT_STRING_LEN_MAX + 1];
1761 fmt_to_string (in, fmt_string);
1762 tooltip = xasprintf (_("Field content \"%.*s\" cannot be parsed in "
1764 (int) field.length, field.string,
1771 tooltip = xstrdup (_("This input line has too few separators "
1772 "to fill in this field."));
1773 value_set_missing (&val, var_get_width (var));
1775 if (outputp != NULL)
1777 *outputp = data_out (&val, dict_get_encoding (ia->formats.dict), &out);
1779 value_destroy (&val, var_get_width (var));
1781 ok = tooltip == NULL;
1782 if (tooltipp != NULL)
1783 *tooltipp = tooltip;
1789 /* Called to render one of the cells in the data preview tree
1792 render_output_cell (GtkTreeViewColumn *tree_column,
1793 GtkCellRenderer *cell,
1794 GtkTreeModel *model,
1798 struct import_assistant *ia = ia_;
1800 GValue gvalue = { 0, };
1803 ok = parse_field (ia,
1804 (text_import_model_iter_to_row (iter)
1805 + ia->first_line.skip_lines),
1806 GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1810 g_value_init (&gvalue, G_TYPE_STRING);
1811 g_value_take_string (&gvalue, output);
1812 g_object_set_property (G_OBJECT (cell), "text", &gvalue);
1813 g_value_unset (&gvalue);
1816 g_object_set (cell, "background-set", FALSE, (void *) NULL);
1819 "background", "red",
1820 "background-set", TRUE,
1824 /* Called to render a tooltip for one of the cells in the data
1825 preview tree view. */
1827 on_query_output_tooltip (GtkWidget *widget, gint wx, gint wy,
1828 gboolean keyboard_mode UNUSED,
1829 GtkTooltip *tooltip, struct import_assistant *ia)
1834 if (!get_tooltip_location (widget, wx, wy, ia, &row, &column))
1837 if (parse_field (ia, row, column, NULL, &text))
1840 gtk_tooltip_set_text (tooltip, text);
1845 /* Utility functions used by multiple pages of the assistant. */
1848 get_tooltip_location (GtkWidget *widget, gint wx, gint wy,
1849 const struct import_assistant *ia,
1850 size_t *row, size_t *column)
1852 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
1856 GtkTreeViewColumn *tree_column;
1857 GtkTreeModel *tree_model;
1860 /* Check that WIDGET is really visible on the screen before we
1861 do anything else. This is a bug fix for a sticky situation:
1862 when text_data_import_assistant() returns, it frees the data
1863 necessary to compose the tool tip message, but there may be
1864 a tool tip under preparation at that point (even if there is
1865 no visible tool tip) that will call back into us a little
1866 bit later. Perhaps the correct solution to this problem is
1867 to make the data related to the tool tips part of a GObject
1868 that only gets destroyed when all references are released,
1869 but this solution appears to be effective too. */
1870 if (!GTK_WIDGET_MAPPED (widget))
1873 gtk_tree_view_convert_widget_to_bin_window_coords (tree_view,
1875 if (!gtk_tree_view_get_path_at_pos (tree_view, bx, by,
1876 &path, &tree_column, NULL, NULL))
1879 *column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1882 tree_model = gtk_tree_view_get_model (tree_view);
1883 ok = gtk_tree_model_get_iter (tree_model, &iter, path);
1884 gtk_tree_path_free (path);
1888 *row = text_import_model_iter_to_row (&iter) + ia->first_line.skip_lines;
1893 make_tree_view (const struct import_assistant *ia,
1895 GtkTreeView **tree_view)
1897 GtkTreeModel *model;
1899 *tree_view = GTK_TREE_VIEW (gtk_tree_view_new ());
1900 model = GTK_TREE_MODEL (text_import_model_new (
1901 ia->file.lines + first_line,
1902 ia->file.line_cnt - first_line, first_line));
1903 gtk_tree_view_set_model (*tree_view, model);
1905 add_line_number_column (ia, *tree_view);
1909 add_line_number_column (const struct import_assistant *ia,
1910 GtkTreeView *treeview)
1912 GtkTreeViewColumn *column;
1914 column = gtk_tree_view_column_new_with_attributes (
1915 _("Line"), ia->asst.prop_renderer,
1916 "text", TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER,
1918 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
1919 gtk_tree_view_column_set_fixed_width (
1920 column, get_monospace_width (treeview, ia->asst.prop_renderer, 5));
1921 gtk_tree_view_append_column (treeview, column);
1925 get_monospace_width (GtkTreeView *treeview, GtkCellRenderer *renderer,
1932 ds_put_char_multiple (&s, '0', char_cnt);
1933 ds_put_char (&s, ' ');
1934 width = get_string_width (treeview, renderer, ds_cstr (&s));
1941 get_string_width (GtkTreeView *treeview, GtkCellRenderer *renderer,
1945 g_object_set (G_OBJECT (renderer), "text", string, (void *) NULL);
1946 gtk_cell_renderer_get_size (renderer, GTK_WIDGET (treeview),
1947 NULL, NULL, NULL, &width, NULL);
1951 static GtkTreeViewColumn *
1952 make_data_column (struct import_assistant *ia, GtkTreeView *tree_view,
1953 bool input, gint dict_idx)
1955 struct variable *var = NULL;
1956 struct column *column = NULL;
1957 char name[(VAR_NAME_LEN * 2) + 1];
1959 gint content_width, header_width;
1960 GtkTreeViewColumn *tree_column;
1963 column = &ia->separators.columns[dict_idx];
1965 var = dict_get_var (ia->formats.dict, dict_idx);
1967 escape_underscores (input ? column->name : var_get_name (var), name);
1968 char_cnt = input ? column->width : var_get_print_format (var)->w;
1969 content_width = get_monospace_width (tree_view, ia->asst.fixed_renderer,
1971 header_width = get_string_width (tree_view, ia->asst.prop_renderer,
1974 tree_column = gtk_tree_view_column_new ();
1975 g_object_set_data (G_OBJECT (tree_column), "column-number",
1976 GINT_TO_POINTER (dict_idx));
1977 gtk_tree_view_column_set_title (tree_column, name);
1978 gtk_tree_view_column_pack_start (tree_column, ia->asst.fixed_renderer,
1980 gtk_tree_view_column_set_cell_data_func (
1981 tree_column, ia->asst.fixed_renderer,
1982 input ? render_input_cell : render_output_cell, ia, NULL);
1983 gtk_tree_view_column_set_sizing (tree_column, GTK_TREE_VIEW_COLUMN_FIXED);
1984 gtk_tree_view_column_set_fixed_width (tree_column, MAX (content_width,
1990 static GtkTreeView *
1991 create_data_tree_view (bool input, GtkContainer *parent,
1992 struct import_assistant *ia)
1994 GtkTreeView *tree_view;
1997 make_tree_view (ia, ia->first_line.skip_lines, &tree_view);
1998 gtk_tree_selection_set_mode (gtk_tree_view_get_selection (tree_view),
1999 GTK_SELECTION_NONE);
2001 for (i = 0; i < ia->separators.column_cnt; i++)
2002 gtk_tree_view_append_column (tree_view,
2003 make_data_column (ia, tree_view, input, i));
2005 g_object_set (G_OBJECT (tree_view), "has-tooltip", TRUE, (void *) NULL);
2006 g_signal_connect (tree_view, "query-tooltip",
2007 G_CALLBACK (input ? on_query_input_tooltip
2008 : on_query_output_tooltip), ia);
2009 gtk_tree_view_set_fixed_height_mode (tree_view, true);
2011 gtk_container_add (parent, GTK_WIDGET (tree_view));
2012 gtk_widget_show (GTK_WIDGET (tree_view));
2018 escape_underscores (const char *in, char *out)
2020 for (; *in != '\0'; in++)
2029 /* TextImportModel, a GtkTreeModel implementation used by some
2030 pages of the assistant. */
2032 #define G_TYPE_TEXT_IMPORT_MODEL (text_import_model_get_type ())
2033 #define TEXT_IMPORT_MODEL(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), G_TYPE_TEXT_IMPORT_MODEL, TextImportModel))
2034 #define TEXT_IMPORT_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), G_TYPE_TEXT_IMPORT_MODEL, TextImportModelClass))
2035 #define IS_TEXT_IMPORT_MODEL(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), G_TYPE_TEXT_IMPORT_MODEL))
2036 #define IS_TEXT_IMPORT_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), G_TYPE_TEXT_IMPORT_MODEL))
2037 #define TEXT_IMPORT_MODEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), G_TYPE_TEXT_IMPORT_MODEL, TextImportModelClass))
2039 /* Random number used in 'stamp' member of GtkTreeIter. */
2040 #define TREE_MODEL_STAMP 0x7efd67d3
2042 struct TextImportModel
2045 struct string *lines;
2050 struct TextImportModelClass
2052 GObjectClass parent_class;
2055 GType text_import_model_get_type (void);
2056 static void text_import_model_tree_model_init (gpointer iface, gpointer data);
2059 text_import_model_get_type (void)
2061 static GType object_type = 0;
2065 static const GTypeInfo object_info = {
2066 sizeof (TextImportModelClass),
2067 (GBaseInitFunc) NULL,
2068 (GBaseFinalizeFunc) NULL,
2069 NULL, /* class_init */
2070 NULL, /* class_finalize */
2071 NULL, /* class_data */
2072 sizeof (TextImportModel),
2073 0, /* n_preallocs */
2074 NULL, /* instance_init */
2077 static const GInterfaceInfo tree_model_info = {
2078 text_import_model_tree_model_init,
2083 object_type = g_type_register_static (G_TYPE_OBJECT,
2087 g_type_add_interface_static (object_type, GTK_TYPE_TREE_MODEL,
2097 /* Creates and returns a new TextImportModel that contains the
2098 LINE_CNT lines in LINES. The lines before FIRST_LINE in LINES
2099 are not part of the model, but they are included in the line
2100 numbers in the TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER column.
2102 The caller retains responsibility for freeing LINES and must
2103 ensure that its lifetime and that of the strings that it
2104 contains exceeds that of the TextImportModel. */
2106 text_import_model_new (struct string *lines, size_t line_cnt,
2109 TextImportModel *new_text_import_model
2110 = g_object_new (G_TYPE_TEXT_IMPORT_MODEL, NULL);
2111 new_text_import_model->lines = lines;
2112 new_text_import_model->line_cnt = line_cnt;
2113 new_text_import_model->first_line = first_line;
2114 return new_text_import_model;
2119 tree_model_iter_has_child (GtkTreeModel *tree_model,
2126 tree_model_iter_parent (GtkTreeModel *tree_model,
2133 static GtkTreeModelFlags
2134 tree_model_get_flags (GtkTreeModel *model)
2136 g_return_val_if_fail (IS_TEXT_IMPORT_MODEL (model), (GtkTreeModelFlags) 0);
2138 return GTK_TREE_MODEL_LIST_ONLY | GTK_TREE_MODEL_ITERS_PERSIST;
2143 tree_model_n_columns (GtkTreeModel *model)
2149 tree_model_column_type (GtkTreeModel *model, gint index)
2151 return (index == TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER ? G_TYPE_INT
2152 : index == TEXT_IMPORT_MODEL_COLUMN_LINE ? G_TYPE_STRING
2157 init_iter (TextImportModel *list, gint idx, GtkTreeIter *iter)
2159 if (idx < 0 || idx >= list->line_cnt)
2162 iter->user_data = GINT_TO_POINTER (-1);
2167 iter->stamp = TREE_MODEL_STAMP;
2168 iter->user_data = GINT_TO_POINTER (idx);
2174 tree_model_get_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreePath *path)
2176 gint *indices, depth;
2178 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2180 g_return_val_if_fail (path, FALSE);
2182 indices = gtk_tree_path_get_indices (path);
2183 depth = gtk_tree_path_get_depth (path);
2185 g_return_val_if_fail (depth == 1, FALSE);
2187 return init_iter (list, indices[0], iter);
2192 tree_model_iter_next (GtkTreeModel *model, GtkTreeIter *iter)
2194 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2197 assert (iter->stamp == TREE_MODEL_STAMP);
2199 idx = GPOINTER_TO_INT (iter->user_data);
2200 return init_iter (list, idx == -1 ? -1 : idx + 1, iter);
2203 static GtkTreePath *
2204 tree_model_get_path (GtkTreeModel *model, GtkTreeIter *iter)
2208 g_return_val_if_fail (iter->stamp == TREE_MODEL_STAMP, FALSE);
2210 path = gtk_tree_path_new ();
2211 gtk_tree_path_append_index (path, GPOINTER_TO_INT (iter->user_data));
2217 tree_model_get_value (GtkTreeModel *model, GtkTreeIter *iter,
2218 gint column, GValue *value)
2220 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2223 g_return_if_fail (iter->stamp == TREE_MODEL_STAMP);
2225 idx = GPOINTER_TO_INT (iter->user_data);
2226 assert (idx >= 0 && idx < list->line_cnt);
2230 g_value_init (value, G_TYPE_INT);
2231 g_value_set_int (value, idx + list->first_line + 1);
2235 g_value_init (value, G_TYPE_STRING);
2236 g_value_set_static_string (value, ds_cstr (&list->lines[idx]));
2241 tree_model_iter_children (GtkTreeModel *tree_model,
2243 GtkTreeIter *parent)
2249 tree_model_n_children (GtkTreeModel *model, GtkTreeIter *iter)
2251 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2253 return iter == NULL ? list->line_cnt : 0;
2257 tree_model_nth_child (GtkTreeModel *model, GtkTreeIter *iter,
2258 GtkTreeIter *parent, gint n)
2260 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2261 g_return_val_if_fail (IS_TEXT_IMPORT_MODEL (model), FALSE);
2265 return init_iter (list, n, iter);
2269 text_import_model_tree_model_init (gpointer iface_, gpointer data UNUSED)
2271 GtkTreeModelIface *iface = (GtkTreeModelIface *) iface_;
2273 iface->get_flags = tree_model_get_flags;
2274 iface->get_n_columns = tree_model_n_columns;
2275 iface->get_column_type = tree_model_column_type;
2276 iface->get_iter = tree_model_get_iter;
2277 iface->iter_next = tree_model_iter_next;
2278 iface->get_path = tree_model_get_path;
2279 iface->get_value = tree_model_get_value;
2281 iface->iter_children = tree_model_iter_children;
2282 iface->iter_has_child = tree_model_iter_has_child;
2283 iface->iter_n_children = tree_model_n_children;
2284 iface->iter_nth_child = tree_model_nth_child;
2285 iface->iter_parent = tree_model_iter_parent;
2289 text_import_model_iter_to_row (const GtkTreeIter *iter)
2291 assert (iter->stamp == TREE_MODEL_STAMP);
2292 return GPOINTER_TO_INT (iter->user_data);
2295 /* Increments the "watch cursor" level, setting the cursor for
2296 the assistant window to a watch face to indicate to the user
2297 that the ongoing operation may take some time. */
2299 push_watch_cursor (struct import_assistant *ia)
2301 if (++ia->asst.watch_cursor == 1)
2303 GtkWidget *widget = GTK_WIDGET (ia->asst.assistant);
2304 GdkDisplay *display = gtk_widget_get_display (widget);
2305 GdkCursor *cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
2306 gdk_window_set_cursor (widget->window, cursor);
2307 gdk_cursor_unref (cursor);
2308 gdk_display_flush (display);
2312 /* Decrements the "watch cursor" level. If the level reaches
2313 zero, the cursor is reset to its default shape. */
2315 pop_watch_cursor (struct import_assistant *ia)
2317 if (--ia->asst.watch_cursor == 0)
2319 GtkWidget *widget = GTK_WIDGET (ia->asst.assistant);
2320 gdk_window_set_cursor (widget->window, NULL);