1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2008, 2009 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>
48 #include <ui/gui/helper.h>
54 #define _(msgid) gettext (msgid)
55 #define N_(msgid) msgid
58 #if !GTK_CHECK_VERSION (2, 10, 0)
61 text_data_import_assistant (GObject *o, gpointer de_)
63 struct data_editor *de = de_;
66 gtk_message_dialog_new (GTK_WINDOW (de),
70 _("The text import assistant has not been "
71 "compiled into this build of PSPPIRE, because "
72 "GTK+ version 2.10.0 or later was not available."));
74 gtk_dialog_run (GTK_DIALOG (dialog));
76 gtk_widget_destroy (dialog);
81 /* TextImportModel, a GtkTreeModel used by the text data import
85 TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER, /* 1-based line number in file */
86 TEXT_IMPORT_MODEL_COLUMN_LINE, /* The line from the file. */
88 typedef struct TextImportModel TextImportModel;
89 typedef struct TextImportModelClass TextImportModelClass;
91 TextImportModel *text_import_model_new (struct string *lines, size_t line_cnt,
93 gint text_import_model_iter_to_row (const GtkTreeIter *);
95 struct import_assistant;
97 /* The file to be imported. */
100 char *file_name; /* File name. */
101 unsigned long int total_lines; /* Number of lines in file. */
102 bool total_is_exact; /* Is total_lines exact (or an estimate)? */
104 /* The first several lines of the file. */
105 struct string *lines;
108 static bool init_file (struct import_assistant *, GtkWindow *parent);
109 static void destroy_file (struct import_assistant *);
111 /* The main body of the GTK+ assistant and related data. */
115 GtkAssistant *assistant;
116 GMainLoop *main_loop;
117 GtkWidget *paste_button;
118 GtkWidget *reset_button;
122 GtkCellRenderer *prop_renderer;
123 GtkCellRenderer *fixed_renderer;
125 static void init_assistant (struct import_assistant *, GtkWindow *);
126 static void destroy_assistant (struct import_assistant *);
127 static GtkWidget *add_page_to_assistant (struct import_assistant *,
129 GtkAssistantPageType);
131 /* The introduction page of the assistant. */
135 GtkWidget *all_cases_button;
136 GtkWidget *n_cases_button;
137 GtkWidget *n_cases_spin;
138 GtkWidget *percent_button;
139 GtkWidget *percent_spin;
141 static void init_intro_page (struct import_assistant *);
142 static void reset_intro_page (struct import_assistant *);
144 /* Page where the user chooses the first line of data. */
145 struct first_line_page
147 int skip_lines; /* Number of initial lines to skip? */
148 bool variable_names; /* Variable names above first line of data? */
151 GtkTreeView *tree_view;
152 GtkWidget *variable_names_cb;
154 static void init_first_line_page (struct import_assistant *);
155 static void reset_first_line_page (struct import_assistant *);
157 /* Page where the user chooses field separators. */
158 struct separators_page
160 /* How to break lines into columns. */
161 struct string separators; /* Field separators. */
162 struct string quotes; /* Quote characters. */
163 bool escape; /* Doubled quotes yield a quote mark? */
165 /* The columns produced thereby. */
166 struct column *columns; /* Information about each column. */
167 size_t column_cnt; /* Number of columns. */
170 GtkWidget *custom_cb;
171 GtkWidget *custom_entry;
173 GtkWidget *quote_combo;
174 GtkEntry *quote_entry;
175 GtkWidget *escape_cb;
176 GtkTreeView *fields_tree_view;
178 /* The columns that the separators divide the data into. */
181 /* Variable name for this column. This is the variable name
182 used on the separators page; it can be overridden by the
183 user on the formats page. */
186 /* Maximum length of any row in this column. */
189 /* Contents of this column: contents[row] is the contents for
192 A null substring indicates a missing column for that row
193 (because the line contains an insufficient number of
196 contents[] elements may be substrings of the lines[]
197 strings that represent the whole lines of the file, to
198 save memory. Other elements are dynamically allocated
199 with ss_alloc_substring. */
200 struct substring *contents;
202 static void init_separators_page (struct import_assistant *);
203 static void destroy_separators_page (struct import_assistant *);
204 static void prepare_separators_page (struct import_assistant *);
205 static void reset_separators_page (struct import_assistant *);
207 /* Page where the user verifies and adjusts input formats. */
210 struct dictionary *dict;
213 GtkTreeView *data_tree_view;
214 PsppireDict *psppire_dict;
215 struct variable **modified_vars;
216 size_t modified_var_cnt;
218 static void init_formats_page (struct import_assistant *);
219 static void destroy_formats_page (struct import_assistant *);
220 static void prepare_formats_page (struct import_assistant *);
221 static void reset_formats_page (struct import_assistant *);
223 struct import_assistant
226 struct assistant asst;
227 struct intro_page intro;
228 struct first_line_page first_line;
229 struct separators_page separators;
230 struct formats_page formats;
233 static void apply_dict (const struct dictionary *, struct string *);
234 static char *generate_syntax (const struct import_assistant *);
236 static gboolean get_tooltip_location (GtkWidget *widget, gint wx, gint wy,
237 const struct import_assistant *,
238 size_t *row, size_t *column);
239 static void make_tree_view (const struct import_assistant *ia,
241 GtkTreeView **tree_view);
242 static void add_line_number_column (const struct import_assistant *,
244 static gint get_monospace_width (GtkTreeView *, GtkCellRenderer *,
246 static gint get_string_width (GtkTreeView *, GtkCellRenderer *,
248 static GtkTreeViewColumn *make_data_column (struct import_assistant *,
249 GtkTreeView *, bool input,
251 static GtkTreeView *create_data_tree_view (bool input, GtkContainer *parent,
252 struct import_assistant *);
253 static void escape_underscores (const char *in, char *out);
254 static void push_watch_cursor (struct import_assistant *);
255 static void pop_watch_cursor (struct import_assistant *);
257 /* Pops up the Text Data Import assistant. */
259 text_data_import_assistant (GObject *o, gpointer de_)
261 struct data_editor *de = de_;
262 GtkWindow *parent_window = GTK_WINDOW (de);
263 struct import_assistant *ia;
265 ia = xzalloc (sizeof *ia);
266 if (!init_file (ia, parent_window))
272 init_assistant (ia, parent_window);
273 init_intro_page (ia);
274 init_first_line_page (ia);
275 init_separators_page (ia);
276 init_formats_page (ia);
278 gtk_widget_show_all (GTK_WIDGET (ia->asst.assistant));
280 ia->asst.main_loop = g_main_loop_new (NULL, false);
281 g_main_loop_run (ia->asst.main_loop);
282 g_main_loop_unref (ia->asst.main_loop);
284 switch (ia->asst.response)
286 case GTK_RESPONSE_APPLY:
288 char *syntax = generate_syntax (ia);
289 execute_syntax (create_syntax_string_source (syntax));
293 case PSPPIRE_RESPONSE_PASTE:
295 char *syntax = generate_syntax (ia);
296 paste_syntax_in_new_window (syntax);
304 destroy_formats_page (ia);
305 destroy_separators_page (ia);
306 destroy_assistant (ia);
311 /* Emits PSPP syntax to S that applies the dictionary attributes
312 (such as missing values and value labels) of the variables in
315 apply_dict (const struct dictionary *dict, struct string *s)
317 size_t var_cnt = dict_get_var_cnt (dict);
320 for (i = 0; i < var_cnt; i++)
322 struct variable *var = dict_get_var (dict, i);
323 const char *name = var_get_name (var);
324 enum val_type type = var_get_type (var);
325 int width = var_get_width (var);
326 enum measure measure = var_get_measure (var);
327 enum alignment alignment = var_get_alignment (var);
328 const struct fmt_spec *format = var_get_print_format (var);
330 if (var_has_missing_values (var))
332 const struct missing_values *mv = var_get_missing_values (var);
335 syntax_gen_pspp (s, "MISSING VALUES %ss (", name);
336 for (j = 0; j < mv_n_values (mv); j++)
340 ds_put_cstr (s, ", ");
341 mv_get_value (mv, &value, j);
342 syntax_gen_value (s, &value, width, format);
345 if (mv_has_range (mv))
348 if (mv_has_value (mv))
349 ds_put_cstr (s, ", ");
350 mv_get_range (mv, &low, &high);
351 syntax_gen_num_range (s, low, high, format);
353 ds_put_cstr (s, ").\n");
355 if (var_has_value_labels (var))
357 const struct val_labs *vls = var_get_value_labels (var);
358 struct val_labs_iterator *iter;
361 syntax_gen_pspp (s, "VALUE LABELS %ss", name);
362 for (vl = val_labs_first_sorted (vls, &iter); vl != NULL;
363 vl = val_labs_next (vls, &iter))
365 ds_put_cstr (s, "\n ");
366 syntax_gen_value (s, &vl->value, width, format);
367 ds_put_char (s, ' ');
368 syntax_gen_string (s, ss_cstr (vl->label));
370 ds_put_cstr (s, ".\n");
372 if (var_has_label (var))
373 syntax_gen_pspp (s, "VARIABLE LABELS %ss %sq.\n",
374 name, var_get_label (var));
375 if (measure != var_default_measure (type))
376 syntax_gen_pspp (s, "VARIABLE LEVEL %ss (%ss).\n",
378 (measure == MEASURE_NOMINAL ? "NOMINAL"
379 : measure == MEASURE_ORDINAL ? "ORDINAL"
381 if (alignment != var_default_alignment (type))
382 syntax_gen_pspp (s, "VARIABLE ALIGNMENT %ss (%ss).\n",
384 (alignment == ALIGN_LEFT ? "LEFT"
385 : alignment == ALIGN_CENTRE ? "CENTER"
387 if (var_get_display_width (var) != var_default_display_width (width))
388 syntax_gen_pspp (s, "VARIABLE WIDTH %ss (%d).\n",
389 name, var_get_display_width (var));
393 /* Generates and returns PSPP syntax to execute the import
394 operation described by IA. The caller must free the syntax
397 generate_syntax (const struct import_assistant *ia)
399 struct string s = DS_EMPTY_INITIALIZER;
408 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (
409 ia->intro.n_cases_button)))
410 ds_put_format (&s, " /IMPORTCASES=FIRST %d\n",
411 gtk_spin_button_get_value_as_int (
412 GTK_SPIN_BUTTON (ia->intro.n_cases_spin)));
413 else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (
414 ia->intro.percent_button)))
415 ds_put_format (&s, " /IMPORTCASES=PERCENT %d\n",
416 gtk_spin_button_get_value_as_int (
417 GTK_SPIN_BUTTON (ia->intro.percent_spin)));
419 ds_put_cstr (&s, " /IMPORTCASES=ALL\n");
421 " /ARRANGEMENT=DELIMITED\n"
423 if (ia->first_line.skip_lines > 0)
424 ds_put_format (&s, " /FIRSTCASE=%d\n", ia->first_line.skip_lines + 1);
425 ds_put_cstr (&s, " /DELIMITERS=\"");
426 if (ds_find_char (&ia->separators.separators, '\t') != SIZE_MAX)
427 ds_put_cstr (&s, "\\t");
428 if (ds_find_char (&ia->separators.separators, '\\') != SIZE_MAX)
429 ds_put_cstr (&s, "\\\\");
430 for (i = 0; i < ds_length (&ia->separators.separators); i++)
432 char c = ds_at (&ia->separators.separators, i);
434 ds_put_cstr (&s, "\"\"");
435 else if (c != '\t' && c != '\\')
438 ds_put_cstr (&s, "\"\n");
439 if (!ds_is_empty (&ia->separators.quotes))
440 syntax_gen_pspp (&s, " /QUALIFIER=%sq\n", ds_cstr (&ia->separators.quotes));
441 if (!ds_is_empty (&ia->separators.quotes) && ia->separators.escape)
442 ds_put_cstr (&s, " /ESCAPE\n");
443 ds_put_cstr (&s, " /VARIABLES=\n");
445 var_cnt = dict_get_var_cnt (ia->formats.dict);
446 for (i = 0; i < var_cnt; i++)
448 struct variable *var = dict_get_var (ia->formats.dict, i);
449 char format_string[FMT_STRING_LEN_MAX + 1];
450 fmt_to_string (var_get_print_format (var), format_string);
451 ds_put_format (&s, " %s %s%s\n",
452 var_get_name (var), format_string,
453 i == var_cnt - 1 ? "." : "");
456 apply_dict (ia->formats.dict, &s);
461 /* Choosing a file and reading it. */
463 static char *choose_file (GtkWindow *parent_window);
465 /* Obtains the file to import from the user and initializes IA's
466 file substructure. PARENT_WINDOW must be the window to use
467 as the file chooser window's parent.
469 Returns true if successful, false if the file name could not
470 be obtained or the file could not be read. */
472 init_file (struct import_assistant *ia, GtkWindow *parent_window)
474 struct file *file = &ia->file;
475 enum { MAX_PREVIEW_LINES = 1000 }; /* Max number of lines to read. */
476 enum { MAX_LINE_LEN = 16384 }; /* Max length of an acceptable line. */
479 file->file_name = choose_file (parent_window);
480 if (file->file_name == NULL)
483 stream = fopen (file->file_name, "r");
486 msg (ME, _("Could not open \"%s\": %s"),
487 file->file_name, strerror (errno));
491 file->lines = xnmalloc (MAX_PREVIEW_LINES, sizeof *file->lines);
492 for (; file->line_cnt < MAX_PREVIEW_LINES; file->line_cnt++)
494 struct string *line = &file->lines[file->line_cnt];
496 ds_init_empty (line);
497 if (!ds_read_line (line, stream, MAX_LINE_LEN))
501 else if (ferror (stream))
502 msg (ME, _("Error reading \"%s\": %s"),
503 file->file_name, strerror (errno));
505 msg (ME, _("Failed to read \"%s\", because it contains a line "
506 "over %d bytes long and therefore appears not to be "
508 file->file_name, MAX_LINE_LEN);
513 ds_chomp (line, '\n');
514 ds_chomp (line, '\r');
517 if (file->line_cnt == 0)
519 msg (ME, _("\"%s\" is empty."), file->file_name);
525 /* Estimate the number of lines in the file. */
526 if (file->line_cnt < MAX_PREVIEW_LINES)
527 file->total_lines = file->line_cnt;
531 off_t position = ftello (stream);
532 if (fstat (fileno (stream), &s) == 0 && position > 0)
533 file->total_lines = (double) file->line_cnt / position * s.st_size;
535 file->total_lines = 0;
541 /* Frees IA's file substructure. */
543 destroy_file (struct import_assistant *ia)
545 struct file *f = &ia->file;
548 for (i = 0; i < f->line_cnt; i++)
549 ds_destroy (&f->lines[i]);
551 g_free (f->file_name);
554 /* Obtains the file to read from the user and returns the name of
555 the file as a string that must be freed with g_free if
556 successful, otherwise a null pointer. PARENT_WINDOW must be
557 the window to use as the file chooser window's parent. */
559 choose_file (GtkWindow *parent_window)
564 dialog = gtk_file_chooser_dialog_new (_("Import Delimited Text Data"),
566 GTK_FILE_CHOOSER_ACTION_OPEN,
567 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
568 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
571 switch (gtk_dialog_run (GTK_DIALOG (dialog)))
573 case GTK_RESPONSE_ACCEPT:
574 file_name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
580 gtk_widget_destroy (dialog);
587 static void close_assistant (struct import_assistant *, int response);
588 static void on_prepare (GtkAssistant *assistant, GtkWidget *page,
589 struct import_assistant *);
590 static void on_cancel (GtkAssistant *assistant, struct import_assistant *);
591 static void on_close (GtkAssistant *assistant, struct import_assistant *);
592 static void on_paste (GtkButton *button, struct import_assistant *);
593 static void on_reset (GtkButton *button, struct import_assistant *);
594 static void close_assistant (struct import_assistant *, int response);
596 /* Initializes IA's asst substructure. PARENT_WINDOW must be the
597 window to use as the assistant window's parent. */
599 init_assistant (struct import_assistant *ia, GtkWindow *parent_window)
601 struct assistant *a = &ia->asst;
603 a->builder = builder_new ("text-data-import.ui");
604 a->assistant = GTK_ASSISTANT (gtk_assistant_new ());
605 g_signal_connect (a->assistant, "prepare", G_CALLBACK (on_prepare), ia);
606 g_signal_connect (a->assistant, "cancel", G_CALLBACK (on_cancel), ia);
607 g_signal_connect (a->assistant, "close", G_CALLBACK (on_close), ia);
608 a->paste_button = gtk_button_new_from_stock (GTK_STOCK_PASTE);
609 gtk_assistant_add_action_widget (a->assistant, a->paste_button);
610 g_signal_connect (a->paste_button, "clicked", G_CALLBACK (on_paste), ia);
611 a->reset_button = gtk_button_new_from_stock ("pspp-stock-reset");
612 gtk_assistant_add_action_widget (a->assistant, a->reset_button);
613 g_signal_connect (a->reset_button, "clicked", G_CALLBACK (on_reset), ia);
614 gtk_window_set_title (GTK_WINDOW (a->assistant),
615 _("Importing Delimited Text Data"));
616 gtk_window_set_transient_for (GTK_WINDOW (a->assistant), parent_window);
618 a->prop_renderer = gtk_cell_renderer_text_new ();
619 g_object_ref_sink (a->prop_renderer);
620 a->fixed_renderer = gtk_cell_renderer_text_new ();
621 g_object_ref_sink (a->fixed_renderer);
622 g_object_set (G_OBJECT (a->fixed_renderer),
623 "family", "Monospace",
627 /* Frees IA's asst substructure. */
629 destroy_assistant (struct import_assistant *ia)
631 struct assistant *a = &ia->asst;
633 g_object_unref (a->prop_renderer);
634 g_object_unref (a->fixed_renderer);
635 g_object_unref (a->builder);
638 /* Appends a page of the given TYPE, with PAGE as its content, to
639 the GtkAssistant encapsulated by IA. Returns the GtkWidget
640 that represents the page. */
642 add_page_to_assistant (struct import_assistant *ia,
643 GtkWidget *page, GtkAssistantPageType type)
649 title = gtk_window_get_title (GTK_WINDOW (page));
650 title_copy = xstrdup (title ? title : "");
652 content = gtk_bin_get_child (GTK_BIN (page));
654 g_object_ref (content);
655 gtk_container_remove (GTK_CONTAINER (page), content);
657 gtk_widget_destroy (page);
659 gtk_assistant_append_page (ia->asst.assistant, content);
660 gtk_assistant_set_page_type (ia->asst.assistant, content, type);
661 gtk_assistant_set_page_title (ia->asst.assistant, content, title_copy);
662 gtk_assistant_set_page_complete (ia->asst.assistant, content, true);
669 /* Called just before PAGE is displayed as the current page of
670 ASSISTANT, this updates IA content according to the new
673 on_prepare (GtkAssistant *assistant, GtkWidget *page,
674 struct import_assistant *ia)
676 if (page == ia->separators.page)
677 prepare_separators_page (ia);
678 else if (page == ia->formats.page)
679 prepare_formats_page (ia);
681 gtk_widget_show (ia->asst.reset_button);
682 if (page == ia->formats.page)
683 gtk_widget_show (ia->asst.paste_button);
685 gtk_widget_hide (ia->asst.paste_button);
688 /* Called when the Cancel button in the assistant is clicked. */
690 on_cancel (GtkAssistant *assistant, struct import_assistant *ia)
692 close_assistant (ia, GTK_RESPONSE_CANCEL);
695 /* Called when the Apply button on the last page of the assistant
698 on_close (GtkAssistant *assistant, struct import_assistant *ia)
700 close_assistant (ia, GTK_RESPONSE_APPLY);
703 /* Called when the Paste button on the last page of the assistant
706 on_paste (GtkButton *button, struct import_assistant *ia)
708 close_assistant (ia, PSPPIRE_RESPONSE_PASTE);
711 /* Called when the Reset button is clicked. */
713 on_reset (GtkButton *button, struct import_assistant *ia)
715 gint page_num = gtk_assistant_get_current_page (ia->asst.assistant);
716 GtkWidget *page = gtk_assistant_get_nth_page (ia->asst.assistant, page_num);
718 if (page == ia->intro.page)
719 reset_intro_page (ia);
720 else if (page == ia->first_line.page)
721 reset_first_line_page (ia);
722 else if (page == ia->separators.page)
723 reset_separators_page (ia);
724 else if (page == ia->formats.page)
725 reset_formats_page (ia);
728 /* Causes the assistant to close, returning RESPONSE for
729 interpretation by text_data_import_assistant. */
731 close_assistant (struct import_assistant *ia, int response)
733 ia->asst.response = response;
734 g_main_loop_quit (ia->asst.main_loop);
735 gtk_widget_hide (GTK_WIDGET (ia->asst.assistant));
738 /* The "intro" page of the assistant. */
740 static void on_intro_amount_changed (GtkToggleButton *button,
741 struct import_assistant *);
743 /* Initializes IA's intro substructure. */
745 init_intro_page (struct import_assistant *ia)
747 GtkBuilder *builder = ia->asst.builder;
748 struct intro_page *p = &ia->intro;
751 p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Intro"),
752 GTK_ASSISTANT_PAGE_INTRO);
753 p->all_cases_button = get_widget_assert (builder, "import-all-cases");
754 p->n_cases_button = get_widget_assert (builder, "import-n-cases");
755 p->n_cases_spin = get_widget_assert (builder, "n-cases-spin");
756 p->percent_button = get_widget_assert (builder, "import-percent");
757 p->percent_spin = get_widget_assert (builder, "percent-spin");
758 g_signal_connect (p->all_cases_button, "toggled",
759 G_CALLBACK (on_intro_amount_changed), ia);
760 g_signal_connect (p->n_cases_button, "toggled",
761 G_CALLBACK (on_intro_amount_changed), ia);
762 g_signal_connect (p->percent_button, "toggled",
763 G_CALLBACK (on_intro_amount_changed), ia);
766 ds_put_cstr (&s, _("This assistant will guide you through the process of "
767 "importing data into PSPP from a text file with one line "
768 "per case, in which fields are separated by tabs, "
769 "commas, or other delimiters.\n\n"));
770 if (ia->file.total_is_exact)
772 &s, ngettext ("The selected file contains %zu line of text. ",
773 "The selected file contains %zu lines of text. ",
776 else if (ia->file.total_lines > 0)
780 "The selected file contains approximately %lu line of text. ",
781 "The selected file contains approximately %lu lines of text. ",
782 ia->file.total_lines),
783 ia->file.total_lines);
786 "Only the first %zu line of the file will be shown for "
787 "preview purposes in the following screens. ",
788 "Only the first %zu lines of the file will be shown for "
789 "preview purposes in the following screens. ",
793 ds_put_cstr (&s, _("You may choose below how much of the file should "
794 "actually be imported."));
795 gtk_label_set_text (GTK_LABEL (get_widget_assert (builder, "intro-label")),
800 /* Resets IA's intro page to its initial state. */
802 reset_intro_page (struct import_assistant *ia)
804 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->intro.all_cases_button),
808 /* Called when one of the radio buttons is clicked. */
810 on_intro_amount_changed (GtkToggleButton *button UNUSED,
811 struct import_assistant *ia)
813 struct intro_page *p = &ia->intro;
815 gtk_widget_set_sensitive (p->n_cases_spin,
816 gtk_toggle_button_get_active (
817 GTK_TOGGLE_BUTTON (p->n_cases_button)));
819 gtk_widget_set_sensitive (ia->intro.percent_spin,
820 gtk_toggle_button_get_active (
821 GTK_TOGGLE_BUTTON (p->percent_button)));
824 /* The "first line" page of the assistant. */
826 static GtkTreeView *create_lines_tree_view (GtkContainer *parent_window,
827 struct import_assistant *);
828 static void on_first_line_change (GtkTreeSelection *,
829 struct import_assistant *);
830 static void on_variable_names_cb_toggle (GtkToggleButton *,
831 struct import_assistant *);
832 static void set_first_line (struct import_assistant *);
833 static void get_first_line (struct import_assistant *);
835 /* Initializes IA's first_line substructure. */
837 init_first_line_page (struct import_assistant *ia)
839 struct first_line_page *p = &ia->first_line;
840 GtkBuilder *builder = ia->asst.builder;
842 p->page = add_page_to_assistant (ia, get_widget_assert (builder, "FirstLine"),
843 GTK_ASSISTANT_PAGE_CONTENT);
844 gtk_widget_destroy (get_widget_assert (builder, "first-line"));
845 p->tree_view = create_lines_tree_view (
846 GTK_CONTAINER (get_widget_assert (builder, "first-line-scroller")), ia);
847 p->variable_names_cb = get_widget_assert (builder, "variable-names");
848 gtk_tree_selection_set_mode (
849 gtk_tree_view_get_selection (GTK_TREE_VIEW (p->tree_view)),
850 GTK_SELECTION_BROWSE);
852 g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (p->tree_view)),
853 "changed", G_CALLBACK (on_first_line_change), ia);
854 g_signal_connect (p->variable_names_cb, "toggled",
855 G_CALLBACK (on_variable_names_cb_toggle), ia);
858 /* Resets the first_line page to its initial content. */
860 reset_first_line_page (struct import_assistant *ia)
862 ia->first_line.skip_lines = 0;
863 ia->first_line.variable_names = false;
867 /* Creates and returns a tree view that contains each of the
868 lines in IA's file as a row. */
870 create_lines_tree_view (GtkContainer *parent, struct import_assistant *ia)
872 GtkTreeView *tree_view;
873 GtkTreeViewColumn *column;
874 size_t max_line_length;
875 gint content_width, header_width;
878 make_tree_view (ia, 0, &tree_view);
880 column = gtk_tree_view_column_new_with_attributes (
881 "Text", ia->asst.fixed_renderer,
882 "text", TEXT_IMPORT_MODEL_COLUMN_LINE,
884 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
887 for (i = 0; i < ia->file.line_cnt; i++)
889 size_t w = ds_length (&ia->file.lines[i]);
890 max_line_length = MAX (max_line_length, w);
893 content_width = get_monospace_width (tree_view, ia->asst.fixed_renderer,
895 header_width = get_string_width (tree_view, ia->asst.prop_renderer, "Text");
896 gtk_tree_view_column_set_fixed_width (column, MAX (content_width,
898 gtk_tree_view_append_column (tree_view, column);
900 gtk_tree_view_set_fixed_height_mode (tree_view, true);
902 gtk_container_add (parent, GTK_WIDGET (tree_view));
903 gtk_widget_show (GTK_WIDGET (tree_view));
908 /* Called when the line selected in the first_line tree view
911 on_first_line_change (GtkTreeSelection *selection UNUSED,
912 struct import_assistant *ia)
917 /* Called when the checkbox that indicates whether variable
918 names are in the row above the first line is toggled. */
920 on_variable_names_cb_toggle (GtkToggleButton *variable_names_cb UNUSED,
921 struct import_assistant *ia)
926 /* Sets the widgets to match IA's first_line substructure. */
928 set_first_line (struct import_assistant *ia)
932 path = gtk_tree_path_new_from_indices (ia->first_line.skip_lines, -1);
933 gtk_tree_view_set_cursor (GTK_TREE_VIEW (ia->first_line.tree_view),
935 gtk_tree_path_free (path);
937 gtk_toggle_button_set_active (
938 GTK_TOGGLE_BUTTON (ia->first_line.variable_names_cb),
939 ia->first_line.variable_names);
940 gtk_widget_set_sensitive (ia->first_line.variable_names_cb,
941 ia->first_line.skip_lines > 0);
944 /* Sets IA's first_line substructure to match the widgets. */
946 get_first_line (struct import_assistant *ia)
948 GtkTreeSelection *selection;
952 selection = gtk_tree_view_get_selection (ia->first_line.tree_view);
953 if (gtk_tree_selection_get_selected (selection, &model, &iter))
955 GtkTreePath *path = gtk_tree_model_get_path (model, &iter);
956 int row = gtk_tree_path_get_indices (path)[0];
957 gtk_tree_path_free (path);
959 ia->first_line.skip_lines = row;
960 ia->first_line.variable_names =
961 (ia->first_line.skip_lines > 0
962 && gtk_toggle_button_get_active (
963 GTK_TOGGLE_BUTTON (ia->first_line.variable_names_cb)));
965 gtk_widget_set_sensitive (ia->first_line.variable_names_cb,
966 ia->first_line.skip_lines > 0);
969 /* The "separators" page of the assistant. */
971 static void revise_fields_preview (struct import_assistant *ia);
972 static void choose_likely_separators (struct import_assistant *ia);
973 static void find_commonest_chars (unsigned long int histogram[UCHAR_MAX + 1],
974 const char *targets, const char *def,
975 struct string *result);
976 static void clear_fields (struct import_assistant *ia);
977 static void revise_fields_preview (struct import_assistant *);
978 static void set_separators (struct import_assistant *);
979 static void get_separators (struct import_assistant *);
980 static void on_separators_custom_entry_notify (GObject *UNUSED,
982 struct import_assistant *);
983 static void on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
984 struct import_assistant *);
985 static void on_quote_combo_change (GtkComboBox *combo,
986 struct import_assistant *);
987 static void on_quote_cb_toggle (GtkToggleButton *quote_cb,
988 struct import_assistant *);
989 static void on_separator_toggle (GtkToggleButton *, struct import_assistant *);
990 static void render_input_cell (GtkTreeViewColumn *tree_column,
991 GtkCellRenderer *cell,
992 GtkTreeModel *model, GtkTreeIter *iter,
994 static gboolean on_query_input_tooltip (GtkWidget *widget, gint wx, gint wy,
995 gboolean keyboard_mode UNUSED,
997 struct import_assistant *);
999 /* A common field separator and its identifying name. */
1002 const char *name; /* Name (for use with get_widget_assert). */
1003 int c; /* Separator character. */
1006 /* All the separators in the dialog box. */
1007 static const struct separator separators[] =
1019 #define SEPARATOR_CNT (sizeof separators / sizeof *separators)
1022 set_quote_list (GtkComboBoxEntry *cb)
1024 GtkListStore *list = gtk_list_store_new (1, G_TYPE_STRING);
1027 const gchar *seperator[3] = {"'\"", "\'", "\""};
1029 for (i = 0; i < 3; i++)
1031 const gchar *s = seperator[i];
1033 /* Add a new row to the model */
1034 gtk_list_store_append (list, &iter);
1035 gtk_list_store_set (list, &iter,
1041 gtk_combo_box_set_model (GTK_COMBO_BOX (cb), GTK_TREE_MODEL (list));
1043 gtk_combo_box_entry_set_text_column (cb, 0);
1046 /* Initializes IA's separators substructure. */
1048 init_separators_page (struct import_assistant *ia)
1050 GtkBuilder *builder = ia->asst.builder;
1051 struct separators_page *p = &ia->separators;
1054 choose_likely_separators (ia);
1056 p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Separators"),
1057 GTK_ASSISTANT_PAGE_CONTENT);
1058 p->custom_cb = get_widget_assert (builder, "custom-cb");
1059 p->custom_entry = get_widget_assert (builder, "custom-entry");
1060 p->quote_combo = get_widget_assert (builder, "quote-combo");
1061 p->quote_entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (p->quote_combo)));
1062 p->quote_cb = get_widget_assert (builder, "quote-cb");
1063 p->escape_cb = get_widget_assert (builder, "escape");
1065 set_separators (ia);
1066 set_quote_list (GTK_COMBO_BOX_ENTRY (p->quote_combo));
1067 p->fields_tree_view = GTK_TREE_VIEW (get_widget_assert (builder, "fields"));
1068 g_signal_connect (GTK_COMBO_BOX (p->quote_combo), "changed",
1069 G_CALLBACK (on_quote_combo_change), ia);
1070 g_signal_connect (p->quote_cb, "toggled",
1071 G_CALLBACK (on_quote_cb_toggle), ia);
1072 g_signal_connect (GTK_ENTRY (p->custom_entry), "notify::text",
1073 G_CALLBACK (on_separators_custom_entry_notify), ia);
1074 g_signal_connect (p->custom_cb, "toggled",
1075 G_CALLBACK (on_separators_custom_cb_toggle), ia);
1076 for (i = 0; i < SEPARATOR_CNT; i++)
1077 g_signal_connect (get_widget_assert (builder, separators[i].name),
1078 "toggled", G_CALLBACK (on_separator_toggle), ia);
1079 g_signal_connect (p->escape_cb, "toggled",
1080 G_CALLBACK (on_separator_toggle), ia);
1083 /* Frees IA's separators substructure. */
1085 destroy_separators_page (struct import_assistant *ia)
1087 struct separators_page *s = &ia->separators;
1089 ds_destroy (&s->separators);
1090 ds_destroy (&s->quotes);
1094 /* Called just before the separators page becomes visible in the
1097 prepare_separators_page (struct import_assistant *ia)
1099 revise_fields_preview (ia);
1102 /* Called when the Reset button is clicked on the separators
1103 page, resets the separators to the defaults. */
1105 reset_separators_page (struct import_assistant *ia)
1107 choose_likely_separators (ia);
1108 set_separators (ia);
1111 /* Frees and clears the column data in IA's separators
1114 clear_fields (struct import_assistant *ia)
1116 struct separators_page *s = &ia->separators;
1118 if (s->column_cnt > 0)
1123 for (row = 0; row < ia->file.line_cnt; row++)
1125 const struct string *line = &ia->file.lines[row];
1126 const char *line_start = ds_data (line);
1127 const char *line_end = ds_end (line);
1129 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1131 char *s = ss_data (col->contents[row]);
1132 if (!(s >= line_start && s <= line_end))
1133 ss_dealloc (&col->contents[row]);
1137 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1140 free (col->contents);
1149 /* Breaks the file data in IA into columns based on the
1150 separators set in IA's separators substructure. */
1152 split_fields (struct import_assistant *ia)
1154 struct separators_page *s = &ia->separators;
1155 size_t columns_allocated;
1161 /* Is space in the set of separators? */
1162 space_sep = ss_find_char (ds_ss (&s->separators), ' ') != SIZE_MAX;
1164 /* Split all the lines, not just those from
1165 ia->first_line.skip_lines on, so that we split the line that
1166 contains variables names if ia->first_line.variable_names is
1168 columns_allocated = 0;
1169 for (row = 0; row < ia->file.line_cnt; row++)
1171 struct string *line = &ia->file.lines[row];
1172 struct substring text = ds_ss (line);
1175 for (column_idx = 0; ; column_idx++)
1177 struct substring field;
1178 struct column *column;
1181 ss_ltrim (&text, ss_cstr (" "));
1182 if (ss_is_empty (text))
1184 if (column_idx != 0)
1188 else if (!ds_is_empty (&s->quotes)
1189 && ds_find_char (&s->quotes, text.string[0]) != SIZE_MAX)
1191 int quote = ss_get_char (&text);
1193 ss_get_until (&text, quote, &field);
1200 while ((c = ss_get_char (&text)) != EOF)
1202 ds_put_char (&s, c);
1203 else if (ss_match_char (&text, quote))
1204 ds_put_char (&s, quote);
1211 ss_get_chars (&text, ss_cspan (text, ds_ss (&s->separators)),
1214 if (column_idx >= s->column_cnt)
1216 struct column *column;
1218 if (s->column_cnt >= columns_allocated)
1219 s->columns = x2nrealloc (s->columns, &columns_allocated,
1220 sizeof *s->columns);
1221 column = &s->columns[s->column_cnt++];
1222 column->name = NULL;
1224 column->contents = xcalloc (ia->file.line_cnt,
1225 sizeof *column->contents);
1227 column = &s->columns[column_idx];
1228 column->contents[row] = field;
1229 if (ss_length (field) > column->width)
1230 column->width = ss_length (field);
1233 ss_ltrim (&text, ss_cstr (" "));
1234 if (ss_is_empty (text))
1236 if (ss_find_char (ds_ss (&s->separators), ss_first (text))
1238 ss_advance (&text, 1);
1243 /* Chooses a name for each column on the separators page */
1245 choose_column_names (struct import_assistant *ia)
1247 const struct first_line_page *f = &ia->first_line;
1248 struct separators_page *s = &ia->separators;
1249 struct dictionary *dict;
1250 unsigned long int generated_name_count = 0;
1254 dict = dict_create ();
1255 name_row = f->variable_names && f->skip_lines ? f->skip_lines : 0;
1256 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1258 char name[VAR_NAME_LEN + 1];
1261 hint = name_row ? ss_xstrdup (col->contents[name_row - 1]) : NULL;
1262 if (!dict_make_unique_var_name (dict, hint, &generated_name_count, name))
1266 col->name = xstrdup (name);
1267 dict_create_var_assert (dict, name, 0);
1269 dict_destroy (dict);
1272 /* Picks the most likely separator and quote characters based on
1275 choose_likely_separators (struct import_assistant *ia)
1277 unsigned long int histogram[UCHAR_MAX + 1] = { 0 };
1280 /* Construct a histogram of all the characters used in the
1282 for (row = 0; row < ia->file.line_cnt; row++)
1284 struct substring line = ds_ss (&ia->file.lines[row]);
1285 size_t length = ss_length (line);
1287 for (i = 0; i < length; i++)
1288 histogram[(unsigned char) line.string[i]]++;
1291 find_commonest_chars (histogram, "\"'", "", &ia->separators.quotes);
1292 find_commonest_chars (histogram, ",;:/|!\t-", ",",
1293 &ia->separators.separators);
1294 ia->separators.escape = true;
1297 /* Chooses the most common character among those in TARGETS,
1298 based on the frequency data in HISTOGRAM, and stores it in
1299 RESULT. If there is a tie for the most common character among
1300 those in TARGETS, the earliest character is chosen. If none
1301 of the TARGETS appear at all, then DEF is used as a
1304 find_commonest_chars (unsigned long int histogram[UCHAR_MAX + 1],
1305 const char *targets, const char *def,
1306 struct string *result)
1308 unsigned char max = 0;
1309 unsigned long int max_count = 0;
1311 for (; *targets != '\0'; targets++)
1313 unsigned char c = *targets;
1314 unsigned long int count = histogram[c];
1315 if (count > max_count)
1324 ds_put_char (result, max);
1327 ds_assign_cstr (result, def);
1330 /* Revises the contents of the fields tree view based on the
1331 currently chosen set of separators. */
1333 revise_fields_preview (struct import_assistant *ia)
1337 push_watch_cursor (ia);
1339 w = GTK_WIDGET (ia->separators.fields_tree_view);
1340 gtk_widget_destroy (w);
1341 get_separators (ia);
1343 choose_column_names (ia);
1344 ia->separators.fields_tree_view = create_data_tree_view (
1346 GTK_CONTAINER (get_widget_assert (ia->asst.builder, "fields-scroller")),
1349 pop_watch_cursor (ia);
1352 /* Sets the widgets to match IA's separators substructure. */
1354 set_separators (struct import_assistant *ia)
1356 struct separators_page *s = &ia->separators;
1358 struct string custom;
1363 ds_init_empty (&custom);
1365 for (i = 0; i < ds_length (&s->separators); i++)
1367 unsigned char c = ds_at (&s->separators, i);
1370 for (j = 0; j < SEPARATOR_CNT; j++)
1372 const struct separator *s = &separators[j];
1380 ds_put_char (&custom, c);
1384 for (i = 0; i < SEPARATOR_CNT; i++)
1386 const struct separator *s = &separators[i];
1387 GtkWidget *button = get_widget_assert (ia->asst.builder, s->name);
1388 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
1389 (seps & (1u << i)) != 0);
1391 any_custom = !ds_is_empty (&custom);
1392 gtk_entry_set_text (GTK_ENTRY (s->custom_entry), ds_cstr (&custom));
1393 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->custom_cb),
1395 gtk_widget_set_sensitive (s->custom_entry, any_custom);
1396 ds_destroy (&custom);
1398 any_quotes = !ds_is_empty (&s->quotes);
1400 gtk_entry_set_text (s->quote_entry,
1401 any_quotes ? ds_cstr (&s->quotes) : "\"");
1402 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->quote_cb),
1404 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->escape_cb),
1406 gtk_widget_set_sensitive (s->quote_combo, any_quotes);
1407 gtk_widget_set_sensitive (s->escape_cb, any_quotes);
1410 /* Sets IA's separators substructure to match the widgets. */
1412 get_separators (struct import_assistant *ia)
1414 struct separators_page *s = &ia->separators;
1417 ds_clear (&s->separators);
1418 for (i = 0; i < SEPARATOR_CNT; i++)
1420 const struct separator *sep = &separators[i];
1421 GtkWidget *button = get_widget_assert (ia->asst.builder, sep->name);
1422 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
1423 ds_put_char (&s->separators, sep->c);
1426 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->custom_cb)))
1427 ds_put_cstr (&s->separators,
1428 gtk_entry_get_text (GTK_ENTRY (s->custom_entry)));
1430 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->quote_cb)))
1432 gchar *text = gtk_combo_box_get_active_text (
1433 GTK_COMBO_BOX (s->quote_combo));
1434 ds_assign_cstr (&s->quotes, text);
1438 ds_clear (&s->quotes);
1439 s->escape = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->escape_cb));
1442 /* Called when the user changes the entry field for custom
1445 on_separators_custom_entry_notify (GObject *gobject UNUSED,
1446 GParamSpec *arg1 UNUSED,
1447 struct import_assistant *ia)
1449 revise_fields_preview (ia);
1452 /* Called when the user toggles the checkbox that enables custom
1455 on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
1456 struct import_assistant *ia)
1458 bool is_active = gtk_toggle_button_get_active (custom_cb);
1459 gtk_widget_set_sensitive (ia->separators.custom_entry, is_active);
1460 revise_fields_preview (ia);
1463 /* Called when the user changes the selection in the combo box
1464 that selects a quote character. */
1466 on_quote_combo_change (GtkComboBox *combo, struct import_assistant *ia)
1468 revise_fields_preview (ia);
1471 /* Called when the user toggles the checkbox that enables
1474 on_quote_cb_toggle (GtkToggleButton *quote_cb, struct import_assistant *ia)
1476 bool is_active = gtk_toggle_button_get_active (quote_cb);
1477 gtk_widget_set_sensitive (ia->separators.quote_combo, is_active);
1478 gtk_widget_set_sensitive (ia->separators.escape_cb, is_active);
1479 revise_fields_preview (ia);
1482 /* Called when the user toggles one of the separators
1485 on_separator_toggle (GtkToggleButton *toggle UNUSED,
1486 struct import_assistant *ia)
1488 revise_fields_preview (ia);
1491 /* Called to render one of the cells in the fields preview tree
1494 render_input_cell (GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1495 GtkTreeModel *model, GtkTreeIter *iter,
1498 struct import_assistant *ia = ia_;
1499 struct substring field;
1503 column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1505 row = text_import_model_iter_to_row (iter) + ia->first_line.skip_lines;
1506 field = ia->separators.columns[column].contents[row];
1507 if (field.string != NULL)
1509 GValue text = {0, };
1510 g_value_init (&text, G_TYPE_STRING);
1511 g_value_take_string (&text, ss_xstrdup (field));
1512 g_object_set_property (G_OBJECT (cell), "text", &text);
1513 g_value_unset (&text);
1514 g_object_set (cell, "background-set", FALSE, (void *) NULL);
1519 "background", "red",
1520 "background-set", TRUE,
1524 /* Called to render a tooltip on one of the cells in the fields
1525 preview tree view. */
1527 on_query_input_tooltip (GtkWidget *widget, gint wx, gint wy,
1528 gboolean keyboard_mode UNUSED,
1529 GtkTooltip *tooltip, struct import_assistant *ia)
1533 if (!get_tooltip_location (widget, wx, wy, ia, &row, &column))
1536 if (ia->separators.columns[column].contents[row].string != NULL)
1539 gtk_tooltip_set_text (tooltip,
1540 _("This input line has too few separators "
1541 "to fill in this field."));
1545 /* The "formats" page of the assistant. */
1547 static void on_variable_change (PsppireDict *dict, int idx,
1548 struct import_assistant *);
1549 static void clear_modified_vars (struct import_assistant *);
1551 /* Initializes IA's formats substructure. */
1553 init_formats_page (struct import_assistant *ia)
1555 GtkBuilder *builder = ia->asst.builder;
1556 struct formats_page *p = &ia->formats;
1558 p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Formats"),
1559 GTK_ASSISTANT_PAGE_CONFIRM);
1560 p->data_tree_view = GTK_TREE_VIEW (get_widget_assert (builder, "data"));
1561 p->modified_vars = NULL;
1562 p->modified_var_cnt = 0;
1565 /* Frees IA's formats substructure. */
1567 destroy_formats_page (struct import_assistant *ia)
1569 struct formats_page *p = &ia->formats;
1571 if (p->psppire_dict != NULL)
1573 /* This destroys p->dict also. */
1574 g_object_unref (p->psppire_dict);
1576 clear_modified_vars (ia);
1579 /* Called just before the formats page of the assistant is
1582 prepare_formats_page (struct import_assistant *ia)
1584 struct dictionary *dict;
1585 PsppireDict *psppire_dict;
1586 PsppireVarStore *var_store;
1587 GtkBin *vars_scroller;
1588 GtkWidget *old_var_sheet;
1589 PsppireVarSheet *var_sheet;
1590 struct separators_page *s = &ia->separators;
1591 struct formats_page *p = &ia->formats;
1592 struct fmt_guesser *fg;
1593 unsigned long int number = 0;
1596 push_watch_cursor (ia);
1598 dict = dict_create ();
1599 fg = fmt_guesser_create ();
1600 for (column_idx = 0; column_idx < s->column_cnt; column_idx++)
1602 struct variable *modified_var;
1603 char name[VAR_NAME_LEN + 1];
1605 modified_var = (column_idx < p->modified_var_cnt
1606 ? p->modified_vars[column_idx] : NULL);
1607 if (modified_var == NULL)
1609 struct column *column = &s->columns[column_idx];
1610 struct variable *var;
1611 struct fmt_spec format;
1614 /* Choose variable name. */
1615 if (!dict_make_unique_var_name (dict, column->name, &number, name))
1618 /* Choose variable format. */
1619 fmt_guesser_clear (fg);
1620 for (row = ia->first_line.skip_lines; row < ia->file.line_cnt; row++)
1621 fmt_guesser_add (fg, column->contents[row]);
1622 fmt_guesser_guess (fg, &format);
1623 fmt_fix_input (&format);
1625 /* Create variable. */
1626 var = dict_create_var_assert (dict, name, fmt_var_width (&format));
1627 var_set_both_formats (var, &format);
1631 if (!dict_make_unique_var_name (dict, var_get_name (modified_var),
1634 dict_clone_var_assert (dict, modified_var, name);
1637 fmt_guesser_destroy (fg);
1639 psppire_dict = psppire_dict_new_from_dict (dict);
1640 g_signal_connect (psppire_dict, "variable_changed",
1641 G_CALLBACK (on_variable_change), ia);
1642 ia->formats.dict = dict;
1643 ia->formats.psppire_dict = psppire_dict;
1645 /* XXX: PsppireVarStore doesn't hold a reference to
1646 psppire_dict for now, but it should. After it does, we
1647 should g_object_ref the psppire_dict here, since we also
1648 hold a reference via ia->formats.dict. */
1649 var_store = psppire_var_store_new (psppire_dict);
1650 g_object_set (var_store,
1651 "format-type", PSPPIRE_VAR_STORE_INPUT_FORMATS,
1653 var_sheet = PSPPIRE_VAR_SHEET (psppire_var_sheet_new ());
1654 g_object_set (var_sheet,
1656 "may-create-vars", FALSE,
1659 vars_scroller = GTK_BIN (get_widget_assert (ia->asst.builder, "vars-scroller"));
1660 old_var_sheet = gtk_bin_get_child (vars_scroller);
1661 if (old_var_sheet != NULL)
1662 gtk_widget_destroy (old_var_sheet);
1663 gtk_container_add (GTK_CONTAINER (vars_scroller), GTK_WIDGET (var_sheet));
1664 gtk_widget_show (GTK_WIDGET (var_sheet));
1666 gtk_widget_destroy (GTK_WIDGET (ia->formats.data_tree_view));
1667 ia->formats.data_tree_view = create_data_tree_view (
1669 GTK_CONTAINER (get_widget_assert (ia->asst.builder, "data-scroller")),
1672 pop_watch_cursor (ia);
1675 /* Clears the set of user-modified variables from IA's formats
1676 substructure. This discards user modifications to variable
1677 formats, thereby causing formats to revert to their
1680 clear_modified_vars (struct import_assistant *ia)
1682 struct formats_page *p = &ia->formats;
1685 for (i = 0; i < p->modified_var_cnt; i++)
1686 var_destroy (p->modified_vars[i]);
1687 free (p->modified_vars);
1688 p->modified_vars = NULL;
1689 p->modified_var_cnt = 0;
1692 /* Resets the formats page to its defaults, discarding user
1695 reset_formats_page (struct import_assistant *ia)
1697 clear_modified_vars (ia);
1698 prepare_formats_page (ia);
1701 /* Called when the user changes one of the variables in the
1704 on_variable_change (PsppireDict *dict, int dict_idx,
1705 struct import_assistant *ia)
1707 struct formats_page *p = &ia->formats;
1708 GtkTreeView *tv = ia->formats.data_tree_view;
1709 gint column_idx = dict_idx + 1;
1711 push_watch_cursor (ia);
1713 /* Remove previous column and replace with new column. */
1714 gtk_tree_view_remove_column (tv, gtk_tree_view_get_column (tv, column_idx));
1715 gtk_tree_view_insert_column (tv, make_data_column (ia, tv, false, dict_idx),
1718 /* Save a copy of the modified variable in modified_vars, so
1719 that its attributes will be preserved if we back up to the
1720 previous page with the Prev button and then come back
1722 if (dict_idx >= p->modified_var_cnt)
1725 p->modified_vars = xnrealloc (p->modified_vars, dict_idx + 1,
1726 sizeof *p->modified_vars);
1727 for (i = 0; i <= dict_idx; i++)
1728 p->modified_vars[i] = NULL;
1729 p->modified_var_cnt = dict_idx + 1;
1731 if (p->modified_vars[dict_idx])
1732 var_destroy (p->modified_vars[dict_idx]);
1733 p->modified_vars[dict_idx]
1734 = var_clone (psppire_dict_get_variable (dict, dict_idx));
1736 pop_watch_cursor (ia);
1739 /* Parses the contents of the field at (ROW,COLUMN) according to
1740 its variable format. If OUTPUTP is non-null, then *OUTPUTP
1741 receives the formatted output for that field (which must be
1742 freed with free). If TOOLTIPP is non-null, then *TOOLTIPP
1743 receives a message suitable for use in a tooltip, if one is
1744 needed, or a null pointer otherwise. Returns true if a
1745 tooltip message is needed, otherwise false. */
1747 parse_field (struct import_assistant *ia,
1748 size_t row, size_t column,
1749 char **outputp, char **tooltipp)
1751 struct substring field;
1753 struct variable *var;
1754 const struct fmt_spec *in;
1755 struct fmt_spec out;
1759 field = ia->separators.columns[column].contents[row];
1760 var = dict_get_var (ia->formats.dict, column);
1761 val = value_create (var_get_width (var));
1762 in = var_get_print_format (var);
1763 out = fmt_for_output_from_input (in);
1765 if (field.string != NULL)
1768 if (!data_in (field, LEGACY_NATIVE, in->type, 0, 0, 0,
1769 val, var_get_width (var)))
1771 char fmt_string[FMT_STRING_LEN_MAX + 1];
1772 fmt_to_string (in, fmt_string);
1773 tooltip = xasprintf (_("Field content \"%.*s\" cannot be parsed in "
1775 (int) field.length, field.string,
1782 tooltip = xstrdup (_("This input line has too few separators "
1783 "to fill in this field."));
1784 value_set_missing (val, var_get_width (var));
1786 if (outputp != NULL)
1788 char *output = xmalloc (out.w + 1);
1789 data_out (val, &out, output);
1790 output[out.w] = '\0';
1795 ok = tooltip == NULL;
1796 if (tooltipp != NULL)
1797 *tooltipp = tooltip;
1803 /* Called to render one of the cells in the data preview tree
1806 render_output_cell (GtkTreeViewColumn *tree_column,
1807 GtkCellRenderer *cell,
1808 GtkTreeModel *model,
1812 struct import_assistant *ia = ia_;
1814 GValue gvalue = { 0, };
1817 ok = parse_field (ia,
1818 (text_import_model_iter_to_row (iter)
1819 + ia->first_line.skip_lines),
1820 GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1824 g_value_init (&gvalue, G_TYPE_STRING);
1825 g_value_take_string (&gvalue, output);
1826 g_object_set_property (G_OBJECT (cell), "text", &gvalue);
1827 g_value_unset (&gvalue);
1830 g_object_set (cell, "background-set", FALSE, (void *) NULL);
1833 "background", "red",
1834 "background-set", TRUE,
1838 /* Called to render a tooltip for one of the cells in the data
1839 preview tree view. */
1841 on_query_output_tooltip (GtkWidget *widget, gint wx, gint wy,
1842 gboolean keyboard_mode UNUSED,
1843 GtkTooltip *tooltip, struct import_assistant *ia)
1848 if (!get_tooltip_location (widget, wx, wy, ia, &row, &column))
1851 if (parse_field (ia, row, column, NULL, &text))
1854 gtk_tooltip_set_text (tooltip, text);
1859 /* Utility functions used by multiple pages of the assistant. */
1862 get_tooltip_location (GtkWidget *widget, gint wx, gint wy,
1863 const struct import_assistant *ia,
1864 size_t *row, size_t *column)
1866 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
1870 GtkTreeViewColumn *tree_column;
1871 GtkTreeModel *tree_model;
1874 /* Check that WIDGET is really visible on the screen before we
1875 do anything else. This is a bug fix for a sticky situation:
1876 when text_data_import_assistant() returns, it frees the data
1877 necessary to compose the tool tip message, but there may be
1878 a tool tip under preparation at that point (even if there is
1879 no visible tool tip) that will call back into us a little
1880 bit later. Perhaps the correct solution to this problem is
1881 to make the data related to the tool tips part of a GObject
1882 that only gets destroyed when all references are released,
1883 but this solution appears to be effective too. */
1884 if (!GTK_WIDGET_MAPPED (widget))
1887 gtk_tree_view_convert_widget_to_bin_window_coords (tree_view,
1889 if (!gtk_tree_view_get_path_at_pos (tree_view, bx, by,
1890 &path, &tree_column, NULL, NULL))
1893 *column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1896 tree_model = gtk_tree_view_get_model (tree_view);
1897 ok = gtk_tree_model_get_iter (tree_model, &iter, path);
1898 gtk_tree_path_free (path);
1902 *row = text_import_model_iter_to_row (&iter) + ia->first_line.skip_lines;
1907 make_tree_view (const struct import_assistant *ia,
1909 GtkTreeView **tree_view)
1911 GtkTreeModel *model;
1913 *tree_view = GTK_TREE_VIEW (gtk_tree_view_new ());
1914 model = GTK_TREE_MODEL (text_import_model_new (
1915 ia->file.lines + first_line,
1916 ia->file.line_cnt - first_line, first_line));
1917 gtk_tree_view_set_model (*tree_view, model);
1919 add_line_number_column (ia, *tree_view);
1923 add_line_number_column (const struct import_assistant *ia,
1924 GtkTreeView *treeview)
1926 GtkTreeViewColumn *column;
1928 column = gtk_tree_view_column_new_with_attributes (
1929 "Line", ia->asst.prop_renderer,
1930 "text", TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER,
1932 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
1933 gtk_tree_view_column_set_fixed_width (
1934 column, get_monospace_width (treeview, ia->asst.prop_renderer, 5));
1935 gtk_tree_view_append_column (treeview, column);
1939 get_monospace_width (GtkTreeView *treeview, GtkCellRenderer *renderer,
1946 ds_put_char_multiple (&s, '0', char_cnt);
1947 ds_put_char (&s, ' ');
1948 width = get_string_width (treeview, renderer, ds_cstr (&s));
1955 get_string_width (GtkTreeView *treeview, GtkCellRenderer *renderer,
1959 g_object_set (G_OBJECT (renderer), "text", string, (void *) NULL);
1960 gtk_cell_renderer_get_size (renderer, GTK_WIDGET (treeview),
1961 NULL, NULL, NULL, &width, NULL);
1965 static GtkTreeViewColumn *
1966 make_data_column (struct import_assistant *ia, GtkTreeView *tree_view,
1967 bool input, gint dict_idx)
1969 struct variable *var = NULL;
1970 struct column *column = NULL;
1971 char name[(VAR_NAME_LEN * 2) + 1];
1973 gint content_width, header_width;
1974 GtkTreeViewColumn *tree_column;
1977 column = &ia->separators.columns[dict_idx];
1979 var = dict_get_var (ia->formats.dict, dict_idx);
1981 escape_underscores (input ? column->name : var_get_name (var), name);
1982 char_cnt = input ? column->width : var_get_print_format (var)->w;
1983 content_width = get_monospace_width (tree_view, ia->asst.fixed_renderer,
1985 header_width = get_string_width (tree_view, ia->asst.prop_renderer,
1988 tree_column = gtk_tree_view_column_new ();
1989 g_object_set_data (G_OBJECT (tree_column), "column-number",
1990 GINT_TO_POINTER (dict_idx));
1991 gtk_tree_view_column_set_title (tree_column, name);
1992 gtk_tree_view_column_pack_start (tree_column, ia->asst.fixed_renderer,
1994 gtk_tree_view_column_set_cell_data_func (
1995 tree_column, ia->asst.fixed_renderer,
1996 input ? render_input_cell : render_output_cell, ia, NULL);
1997 gtk_tree_view_column_set_sizing (tree_column, GTK_TREE_VIEW_COLUMN_FIXED);
1998 gtk_tree_view_column_set_fixed_width (tree_column, MAX (content_width,
2004 static GtkTreeView *
2005 create_data_tree_view (bool input, GtkContainer *parent,
2006 struct import_assistant *ia)
2008 GtkTreeView *tree_view;
2011 make_tree_view (ia, ia->first_line.skip_lines, &tree_view);
2012 gtk_tree_selection_set_mode (gtk_tree_view_get_selection (tree_view),
2013 GTK_SELECTION_NONE);
2015 for (i = 0; i < ia->separators.column_cnt; i++)
2016 gtk_tree_view_append_column (tree_view,
2017 make_data_column (ia, tree_view, input, i));
2019 g_object_set (G_OBJECT (tree_view), "has-tooltip", TRUE, (void *) NULL);
2020 g_signal_connect (tree_view, "query-tooltip",
2021 G_CALLBACK (input ? on_query_input_tooltip
2022 : on_query_output_tooltip), ia);
2023 gtk_tree_view_set_fixed_height_mode (tree_view, true);
2025 gtk_container_add (parent, GTK_WIDGET (tree_view));
2026 gtk_widget_show (GTK_WIDGET (tree_view));
2032 escape_underscores (const char *in, char *out)
2034 for (; *in != '\0'; in++)
2043 /* TextImportModel, a GtkTreeModel implementation used by some
2044 pages of the assistant. */
2046 #define G_TYPE_TEXT_IMPORT_MODEL (text_import_model_get_type ())
2047 #define TEXT_IMPORT_MODEL(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), G_TYPE_TEXT_IMPORT_MODEL, TextImportModel))
2048 #define TEXT_IMPORT_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), G_TYPE_TEXT_IMPORT_MODEL, TextImportModelClass))
2049 #define IS_TEXT_IMPORT_MODEL(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), G_TYPE_TEXT_IMPORT_MODEL))
2050 #define IS_TEXT_IMPORT_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), G_TYPE_TEXT_IMPORT_MODEL))
2051 #define TEXT_IMPORT_MODEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), G_TYPE_TEXT_IMPORT_MODEL, TextImportModelClass))
2053 /* Random number used in 'stamp' member of GtkTreeIter. */
2054 #define TREE_MODEL_STAMP 0x7efd67d3
2056 struct TextImportModel
2059 struct string *lines;
2064 struct TextImportModelClass
2066 GObjectClass parent_class;
2069 GType text_import_model_get_type (void);
2070 static void text_import_model_tree_model_init (gpointer iface, gpointer data);
2073 text_import_model_get_type (void)
2075 static GType object_type = 0;
2079 static const GTypeInfo object_info = {
2080 sizeof (TextImportModelClass),
2081 (GBaseInitFunc) NULL,
2082 (GBaseFinalizeFunc) NULL,
2083 NULL, /* class_init */
2084 NULL, /* class_finalize */
2085 NULL, /* class_data */
2086 sizeof (TextImportModel),
2087 0, /* n_preallocs */
2088 NULL, /* instance_init */
2091 static const GInterfaceInfo tree_model_info = {
2092 text_import_model_tree_model_init,
2097 object_type = g_type_register_static (G_TYPE_OBJECT,
2101 g_type_add_interface_static (object_type, GTK_TYPE_TREE_MODEL,
2111 /* Creates and returns a new TextImportModel that contains the
2112 LINE_CNT lines in LINES. The lines before FIRST_LINE in LINES
2113 are not part of the model, but they are included in the line
2114 numbers in the TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER column.
2116 The caller retains responsibility for freeing LINES and must
2117 ensure that its lifetime and that of the strings that it
2118 contains exceeds that of the TextImportModel. */
2120 text_import_model_new (struct string *lines, size_t line_cnt,
2123 TextImportModel *new_text_import_model
2124 = g_object_new (G_TYPE_TEXT_IMPORT_MODEL, NULL);
2125 new_text_import_model->lines = lines;
2126 new_text_import_model->line_cnt = line_cnt;
2127 new_text_import_model->first_line = first_line;
2128 return new_text_import_model;
2133 tree_model_iter_has_child (GtkTreeModel *tree_model,
2140 tree_model_iter_parent (GtkTreeModel *tree_model,
2147 static GtkTreeModelFlags
2148 tree_model_get_flags (GtkTreeModel *model)
2150 g_return_val_if_fail (IS_TEXT_IMPORT_MODEL (model), (GtkTreeModelFlags) 0);
2152 return GTK_TREE_MODEL_LIST_ONLY | GTK_TREE_MODEL_ITERS_PERSIST;
2157 tree_model_n_columns (GtkTreeModel *model)
2163 tree_model_column_type (GtkTreeModel *model, gint index)
2165 return (index == TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER ? G_TYPE_INT
2166 : index == TEXT_IMPORT_MODEL_COLUMN_LINE ? G_TYPE_STRING
2171 init_iter (TextImportModel *list, gint idx, GtkTreeIter *iter)
2173 if (idx < 0 || idx >= list->line_cnt)
2176 iter->user_data = GINT_TO_POINTER (-1);
2181 iter->stamp = TREE_MODEL_STAMP;
2182 iter->user_data = GINT_TO_POINTER (idx);
2188 tree_model_get_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreePath *path)
2190 gint *indices, depth;
2192 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2194 g_return_val_if_fail (path, FALSE);
2196 indices = gtk_tree_path_get_indices (path);
2197 depth = gtk_tree_path_get_depth (path);
2199 g_return_val_if_fail (depth == 1, FALSE);
2201 return init_iter (list, indices[0], iter);
2206 tree_model_iter_next (GtkTreeModel *model, GtkTreeIter *iter)
2208 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2211 assert (iter->stamp == TREE_MODEL_STAMP);
2213 idx = GPOINTER_TO_INT (iter->user_data);
2214 return init_iter (list, idx == -1 ? -1 : idx + 1, iter);
2217 static GtkTreePath *
2218 tree_model_get_path (GtkTreeModel *model, GtkTreeIter *iter)
2222 g_return_val_if_fail (iter->stamp == TREE_MODEL_STAMP, FALSE);
2224 path = gtk_tree_path_new ();
2225 gtk_tree_path_append_index (path, GPOINTER_TO_INT (iter->user_data));
2231 tree_model_get_value (GtkTreeModel *model, GtkTreeIter *iter,
2232 gint column, GValue *value)
2234 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2237 g_return_if_fail (iter->stamp == TREE_MODEL_STAMP);
2239 idx = GPOINTER_TO_INT (iter->user_data);
2240 assert (idx >= 0 && idx < list->line_cnt);
2244 g_value_init (value, G_TYPE_INT);
2245 g_value_set_int (value, idx + list->first_line + 1);
2249 g_value_init (value, G_TYPE_STRING);
2250 g_value_set_static_string (value, ds_cstr (&list->lines[idx]));
2255 tree_model_iter_children (GtkTreeModel *tree_model,
2257 GtkTreeIter *parent)
2263 tree_model_n_children (GtkTreeModel *model, GtkTreeIter *iter)
2265 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2267 return iter == NULL ? list->line_cnt : 0;
2271 tree_model_nth_child (GtkTreeModel *model, GtkTreeIter *iter,
2272 GtkTreeIter *parent, gint n)
2274 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2275 g_return_val_if_fail (IS_TEXT_IMPORT_MODEL (model), FALSE);
2279 return init_iter (list, n, iter);
2283 text_import_model_tree_model_init (gpointer iface_, gpointer data UNUSED)
2285 GtkTreeModelIface *iface = (GtkTreeModelIface *) iface_;
2287 iface->get_flags = tree_model_get_flags;
2288 iface->get_n_columns = tree_model_n_columns;
2289 iface->get_column_type = tree_model_column_type;
2290 iface->get_iter = tree_model_get_iter;
2291 iface->iter_next = tree_model_iter_next;
2292 iface->get_path = tree_model_get_path;
2293 iface->get_value = tree_model_get_value;
2295 iface->iter_children = tree_model_iter_children;
2296 iface->iter_has_child = tree_model_iter_has_child;
2297 iface->iter_n_children = tree_model_n_children;
2298 iface->iter_nth_child = tree_model_nth_child;
2299 iface->iter_parent = tree_model_iter_parent;
2303 text_import_model_iter_to_row (const GtkTreeIter *iter)
2305 assert (iter->stamp == TREE_MODEL_STAMP);
2306 return GPOINTER_TO_INT (iter->user_data);
2309 /* Increments the "watch cursor" level, setting the cursor for
2310 the assistant window to a watch face to indicate to the user
2311 that the ongoing operation may take some time. */
2313 push_watch_cursor (struct import_assistant *ia)
2315 if (++ia->asst.watch_cursor == 1)
2317 GtkWidget *widget = GTK_WIDGET (ia->asst.assistant);
2318 GdkDisplay *display = gtk_widget_get_display (widget);
2319 GdkCursor *cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
2320 gdk_window_set_cursor (widget->window, cursor);
2321 gdk_cursor_unref (cursor);
2322 gdk_display_flush (display);
2326 /* Decrements the "watch cursor" level. If the level reaches
2327 zero, the cursor is reset to its default shape. */
2329 pop_watch_cursor (struct import_assistant *ia)
2331 if (--ia->asst.watch_cursor == 0)
2333 GtkWidget *widget = GTK_WIDGET (ia->asst.assistant);
2334 gdk_window_set_cursor (widget->window, NULL);