1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2008 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->xml = XML_NEW ("text-data-import.glade");
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->xml);
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 GladeXML *xml = ia->asst.xml;
748 struct intro_page *p = &ia->intro;
751 p->page = add_page_to_assistant (ia, get_widget_assert (xml, "Intro"),
752 GTK_ASSISTANT_PAGE_INTRO);
753 p->all_cases_button = get_widget_assert (xml, "import-all-cases");
754 p->n_cases_button = get_widget_assert (xml, "import-n-cases");
755 p->n_cases_spin = get_widget_assert (xml, "n-cases-spin");
756 p->percent_button = get_widget_assert (xml, "import-percent");
757 p->percent_spin = get_widget_assert (xml, "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 (xml, "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 GladeXML *xml = ia->asst.xml;
842 p->page = add_page_to_assistant (ia, get_widget_assert (xml, "FirstLine"),
843 GTK_ASSISTANT_PAGE_CONTENT);
844 gtk_widget_destroy (get_widget_assert (xml, "first-line"));
845 p->tree_view = create_lines_tree_view (
846 GTK_CONTAINER (get_widget_assert (xml, "first-line-scroller")), ia);
847 p->variable_names_cb = get_widget_assert (xml, "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)
1021 /* Initializes IA's separators substructure. */
1023 init_separators_page (struct import_assistant *ia)
1025 GladeXML *xml = ia->asst.xml;
1026 struct separators_page *p = &ia->separators;
1029 choose_likely_separators (ia);
1031 p->page = add_page_to_assistant (ia, get_widget_assert (xml, "Separators"),
1032 GTK_ASSISTANT_PAGE_CONTENT);
1033 p->custom_cb = get_widget_assert (xml, "custom-cb");
1034 p->custom_entry = get_widget_assert (xml, "custom-entry");
1035 p->quote_combo = get_widget_assert (xml, "quote-combo");
1036 p->quote_entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (p->quote_combo)));
1037 p->quote_cb = get_widget_assert (xml, "quote-cb");
1038 p->escape_cb = get_widget_assert (xml, "escape");
1040 set_separators (ia);
1041 p->fields_tree_view = GTK_TREE_VIEW (get_widget_assert (xml, "fields"));
1042 g_signal_connect (GTK_COMBO_BOX (p->quote_combo), "changed",
1043 G_CALLBACK (on_quote_combo_change), ia);
1044 g_signal_connect (p->quote_cb, "toggled",
1045 G_CALLBACK (on_quote_cb_toggle), ia);
1046 g_signal_connect (GTK_ENTRY (p->custom_entry), "notify::text",
1047 G_CALLBACK (on_separators_custom_entry_notify), ia);
1048 g_signal_connect (p->custom_cb, "toggled",
1049 G_CALLBACK (on_separators_custom_cb_toggle), ia);
1050 for (i = 0; i < SEPARATOR_CNT; i++)
1051 g_signal_connect (get_widget_assert (xml, separators[i].name),
1052 "toggled", G_CALLBACK (on_separator_toggle), ia);
1053 g_signal_connect (p->escape_cb, "toggled",
1054 G_CALLBACK (on_separator_toggle), ia);
1057 /* Frees IA's separators substructure. */
1059 destroy_separators_page (struct import_assistant *ia)
1061 struct separators_page *s = &ia->separators;
1063 ds_destroy (&s->separators);
1064 ds_destroy (&s->quotes);
1068 /* Called just before the separators page becomes visible in the
1071 prepare_separators_page (struct import_assistant *ia)
1073 revise_fields_preview (ia);
1076 /* Called when the Reset button is clicked on the separators
1077 page, resets the separators to the defaults. */
1079 reset_separators_page (struct import_assistant *ia)
1081 choose_likely_separators (ia);
1082 set_separators (ia);
1085 /* Frees and clears the column data in IA's separators
1088 clear_fields (struct import_assistant *ia)
1090 struct separators_page *s = &ia->separators;
1092 if (s->column_cnt > 0)
1097 for (row = 0; row < ia->file.line_cnt; row++)
1099 const struct string *line = &ia->file.lines[row];
1100 const char *line_start = ds_data (line);
1101 const char *line_end = ds_end (line);
1103 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1105 char *s = ss_data (col->contents[row]);
1106 if (!(s >= line_start && s <= line_end))
1107 ss_dealloc (&col->contents[row]);
1111 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1114 free (col->contents);
1123 /* Breaks the file data in IA into columns based on the
1124 separators set in IA's separators substructure. */
1126 split_fields (struct import_assistant *ia)
1128 struct separators_page *s = &ia->separators;
1129 size_t columns_allocated;
1135 /* Is space in the set of separators? */
1136 space_sep = ss_find_char (ds_ss (&s->separators), ' ') != SIZE_MAX;
1138 /* Split all the lines, not just those from
1139 ia->first_line.skip_lines on, so that we split the line that
1140 contains variables names if ia->first_line.variable_names is
1142 columns_allocated = 0;
1143 for (row = 0; row < ia->file.line_cnt; row++)
1145 struct string *line = &ia->file.lines[row];
1146 struct substring text = ds_ss (line);
1149 for (column_idx = 0; ; column_idx++)
1151 struct substring field;
1152 struct column *column;
1155 ss_ltrim (&text, ss_cstr (" "));
1156 if (ss_is_empty (text))
1158 if (column_idx != 0)
1162 else if (!ds_is_empty (&s->quotes)
1163 && ds_find_char (&s->quotes, text.string[0]) != SIZE_MAX)
1165 int quote = ss_get_char (&text);
1167 ss_get_until (&text, quote, &field);
1174 while ((c = ss_get_char (&text)) != EOF)
1176 ds_put_char (&s, c);
1177 else if (ss_match_char (&text, quote))
1178 ds_put_char (&s, quote);
1185 ss_get_chars (&text, ss_cspan (text, ds_ss (&s->separators)),
1188 if (column_idx >= s->column_cnt)
1190 struct column *column;
1192 if (s->column_cnt >= columns_allocated)
1193 s->columns = x2nrealloc (s->columns, &columns_allocated,
1194 sizeof *s->columns);
1195 column = &s->columns[s->column_cnt++];
1196 column->name = NULL;
1198 column->contents = xcalloc (ia->file.line_cnt,
1199 sizeof *column->contents);
1201 column = &s->columns[column_idx];
1202 column->contents[row] = field;
1203 if (ss_length (field) > column->width)
1204 column->width = ss_length (field);
1207 ss_ltrim (&text, ss_cstr (" "));
1208 if (ss_is_empty (text))
1210 if (ss_find_char (ds_ss (&s->separators), ss_first (text))
1212 ss_advance (&text, 1);
1217 /* Chooses a name for each column on the separators page */
1219 choose_column_names (struct import_assistant *ia)
1221 const struct first_line_page *f = &ia->first_line;
1222 struct separators_page *s = &ia->separators;
1223 struct dictionary *dict;
1224 unsigned long int generated_name_count = 0;
1228 dict = dict_create ();
1229 name_row = f->variable_names && f->skip_lines ? f->skip_lines : 0;
1230 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1232 char name[VAR_NAME_LEN + 1];
1235 hint = name_row ? ss_xstrdup (col->contents[name_row - 1]) : NULL;
1236 if (!dict_make_unique_var_name (dict, hint, &generated_name_count, name))
1240 col->name = xstrdup (name);
1241 dict_create_var_assert (dict, name, 0);
1243 dict_destroy (dict);
1246 /* Picks the most likely separator and quote characters based on
1249 choose_likely_separators (struct import_assistant *ia)
1251 unsigned long int histogram[UCHAR_MAX + 1] = { 0 };
1254 /* Construct a histogram of all the characters used in the
1256 for (row = 0; row < ia->file.line_cnt; row++)
1258 struct substring line = ds_ss (&ia->file.lines[row]);
1259 size_t length = ss_length (line);
1261 for (i = 0; i < length; i++)
1262 histogram[(unsigned char) line.string[i]]++;
1265 find_commonest_chars (histogram, "\"'", "", &ia->separators.quotes);
1266 find_commonest_chars (histogram, ",;:/|!\t-", ",",
1267 &ia->separators.separators);
1268 ia->separators.escape = true;
1271 /* Chooses the most common character among those in TARGETS,
1272 based on the frequency data in HISTOGRAM, and stores it in
1273 RESULT. If there is a tie for the most common character among
1274 those in TARGETS, the earliest character is chosen. If none
1275 of the TARGETS appear at all, then DEF is used as a
1278 find_commonest_chars (unsigned long int histogram[UCHAR_MAX + 1],
1279 const char *targets, const char *def,
1280 struct string *result)
1282 unsigned char max = 0;
1283 unsigned long int max_count = 0;
1285 for (; *targets != '\0'; targets++)
1287 unsigned char c = *targets;
1288 unsigned long int count = histogram[c];
1289 if (count > max_count)
1298 ds_put_char (result, max);
1301 ds_assign_cstr (result, def);
1304 /* Revises the contents of the fields tree view based on the
1305 currently chosen set of separators. */
1307 revise_fields_preview (struct import_assistant *ia)
1311 push_watch_cursor (ia);
1313 w = GTK_WIDGET (ia->separators.fields_tree_view);
1314 gtk_widget_destroy (w);
1315 get_separators (ia);
1317 choose_column_names (ia);
1318 ia->separators.fields_tree_view = create_data_tree_view (
1320 GTK_CONTAINER (get_widget_assert (ia->asst.xml, "fields-scroller")),
1323 pop_watch_cursor (ia);
1326 /* Sets the widgets to match IA's separators substructure. */
1328 set_separators (struct import_assistant *ia)
1330 struct separators_page *s = &ia->separators;
1332 struct string custom;
1337 ds_init_empty (&custom);
1339 for (i = 0; i < ds_length (&s->separators); i++)
1341 unsigned char c = ds_at (&s->separators, i);
1344 for (j = 0; j < SEPARATOR_CNT; j++)
1346 const struct separator *s = &separators[j];
1354 ds_put_char (&custom, c);
1358 for (i = 0; i < SEPARATOR_CNT; i++)
1360 const struct separator *s = &separators[i];
1361 GtkWidget *button = get_widget_assert (ia->asst.xml, s->name);
1362 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
1363 (seps & (1u << i)) != 0);
1365 any_custom = !ds_is_empty (&custom);
1366 gtk_entry_set_text (GTK_ENTRY (s->custom_entry), ds_cstr (&custom));
1367 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->custom_cb),
1369 gtk_widget_set_sensitive (s->custom_entry, any_custom);
1370 ds_destroy (&custom);
1372 any_quotes = !ds_is_empty (&s->quotes);
1373 gtk_entry_set_text (s->quote_entry,
1374 any_quotes ? ds_cstr (&s->quotes) : "\"");
1375 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->quote_cb),
1377 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->escape_cb),
1379 gtk_widget_set_sensitive (s->quote_combo, any_quotes);
1380 gtk_widget_set_sensitive (s->escape_cb, any_quotes);
1383 /* Sets IA's separators substructure to match the widgets. */
1385 get_separators (struct import_assistant *ia)
1387 struct separators_page *s = &ia->separators;
1390 ds_clear (&s->separators);
1391 for (i = 0; i < SEPARATOR_CNT; i++)
1393 const struct separator *sep = &separators[i];
1394 GtkWidget *button = get_widget_assert (ia->asst.xml, sep->name);
1395 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
1396 ds_put_char (&s->separators, sep->c);
1399 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->custom_cb)))
1400 ds_put_cstr (&s->separators,
1401 gtk_entry_get_text (GTK_ENTRY (s->custom_entry)));
1403 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->quote_cb)))
1405 gchar *text = gtk_combo_box_get_active_text (
1406 GTK_COMBO_BOX (s->quote_combo));
1407 ds_assign_cstr (&s->quotes, text);
1411 ds_clear (&s->quotes);
1412 s->escape = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->escape_cb));
1415 /* Called when the user changes the entry field for custom
1418 on_separators_custom_entry_notify (GObject *gobject UNUSED,
1419 GParamSpec *arg1 UNUSED,
1420 struct import_assistant *ia)
1422 revise_fields_preview (ia);
1425 /* Called when the user toggles the checkbox that enables custom
1428 on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
1429 struct import_assistant *ia)
1431 bool is_active = gtk_toggle_button_get_active (custom_cb);
1432 gtk_widget_set_sensitive (ia->separators.custom_entry, is_active);
1433 revise_fields_preview (ia);
1436 /* Called when the user changes the selection in the combo box
1437 that selects a quote character. */
1439 on_quote_combo_change (GtkComboBox *combo, struct import_assistant *ia)
1441 revise_fields_preview (ia);
1444 /* Called when the user toggles the checkbox that enables
1447 on_quote_cb_toggle (GtkToggleButton *quote_cb, struct import_assistant *ia)
1449 bool is_active = gtk_toggle_button_get_active (quote_cb);
1450 gtk_widget_set_sensitive (ia->separators.quote_combo, is_active);
1451 gtk_widget_set_sensitive (ia->separators.escape_cb, is_active);
1452 revise_fields_preview (ia);
1455 /* Called when the user toggles one of the separators
1458 on_separator_toggle (GtkToggleButton *toggle UNUSED,
1459 struct import_assistant *ia)
1461 revise_fields_preview (ia);
1464 /* Called to render one of the cells in the fields preview tree
1467 render_input_cell (GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1468 GtkTreeModel *model, GtkTreeIter *iter,
1471 struct import_assistant *ia = ia_;
1472 struct substring field;
1476 column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1478 row = text_import_model_iter_to_row (iter) + ia->first_line.skip_lines;
1479 field = ia->separators.columns[column].contents[row];
1480 if (field.string != NULL)
1482 GValue text = {0, };
1483 g_value_init (&text, G_TYPE_STRING);
1484 g_value_take_string (&text, ss_xstrdup (field));
1485 g_object_set_property (G_OBJECT (cell), "text", &text);
1486 g_value_unset (&text);
1487 g_object_set (cell, "background-set", FALSE, (void *) NULL);
1492 "background", "red",
1493 "background-set", TRUE,
1497 /* Called to render a tooltip on one of the cells in the fields
1498 preview tree view. */
1500 on_query_input_tooltip (GtkWidget *widget, gint wx, gint wy,
1501 gboolean keyboard_mode UNUSED,
1502 GtkTooltip *tooltip, struct import_assistant *ia)
1506 if (!get_tooltip_location (widget, wx, wy, ia, &row, &column))
1509 if (ia->separators.columns[column].contents[row].string != NULL)
1512 gtk_tooltip_set_text (tooltip,
1513 _("This input line has too few separators "
1514 "to fill in this field."));
1518 /* The "formats" page of the assistant. */
1520 static void on_variable_change (PsppireDict *dict, int idx,
1521 struct import_assistant *);
1522 static void clear_modified_vars (struct import_assistant *);
1524 /* Initializes IA's formats substructure. */
1526 init_formats_page (struct import_assistant *ia)
1528 GladeXML *xml = ia->asst.xml;
1529 struct formats_page *p = &ia->formats;
1531 p->page = add_page_to_assistant (ia, get_widget_assert (xml, "Formats"),
1532 GTK_ASSISTANT_PAGE_CONFIRM);
1533 p->data_tree_view = GTK_TREE_VIEW (get_widget_assert (xml, "data"));
1534 p->modified_vars = NULL;
1535 p->modified_var_cnt = 0;
1538 /* Frees IA's formats substructure. */
1540 destroy_formats_page (struct import_assistant *ia)
1542 struct formats_page *p = &ia->formats;
1544 if (p->psppire_dict != NULL)
1546 /* This destroys p->dict also. */
1547 g_object_unref (p->psppire_dict);
1549 clear_modified_vars (ia);
1552 /* Called just before the formats page of the assistant is
1555 prepare_formats_page (struct import_assistant *ia)
1557 struct dictionary *dict;
1558 PsppireDict *psppire_dict;
1559 PsppireVarStore *var_store;
1560 GtkBin *vars_scroller;
1561 GtkWidget *old_var_sheet;
1562 PsppireVarSheet *var_sheet;
1563 struct separators_page *s = &ia->separators;
1564 struct formats_page *p = &ia->formats;
1565 struct fmt_guesser *fg;
1566 unsigned long int number = 0;
1569 push_watch_cursor (ia);
1571 dict = dict_create ();
1572 fg = fmt_guesser_create ();
1573 for (column_idx = 0; column_idx < s->column_cnt; column_idx++)
1575 struct variable *modified_var;
1576 char name[VAR_NAME_LEN + 1];
1578 modified_var = (column_idx < p->modified_var_cnt
1579 ? p->modified_vars[column_idx] : NULL);
1580 if (modified_var == NULL)
1582 struct column *column = &s->columns[column_idx];
1583 struct variable *var;
1584 struct fmt_spec format;
1587 /* Choose variable name. */
1588 if (!dict_make_unique_var_name (dict, column->name, &number, name))
1591 /* Choose variable format. */
1592 fmt_guesser_clear (fg);
1593 for (row = ia->first_line.skip_lines; row < ia->file.line_cnt; row++)
1594 fmt_guesser_add (fg, column->contents[row]);
1595 fmt_guesser_guess (fg, &format);
1596 fmt_fix_input (&format);
1598 /* Create variable. */
1599 var = dict_create_var_assert (dict, name, fmt_var_width (&format));
1600 var_set_both_formats (var, &format);
1604 if (!dict_make_unique_var_name (dict, var_get_name (modified_var),
1607 dict_clone_var_assert (dict, modified_var, name);
1610 fmt_guesser_destroy (fg);
1612 psppire_dict = psppire_dict_new_from_dict (dict);
1613 g_signal_connect (psppire_dict, "variable_changed",
1614 G_CALLBACK (on_variable_change), ia);
1615 ia->formats.dict = dict;
1616 ia->formats.psppire_dict = psppire_dict;
1618 /* XXX: PsppireVarStore doesn't hold a reference to
1619 psppire_dict for now, but it should. After it does, we
1620 should g_object_ref the psppire_dict here, since we also
1621 hold a reference via ia->formats.dict. */
1622 var_store = psppire_var_store_new (psppire_dict);
1623 g_object_set (var_store,
1624 "format-type", PSPPIRE_VAR_STORE_INPUT_FORMATS,
1626 var_sheet = PSPPIRE_VAR_SHEET (psppire_var_sheet_new ());
1627 g_object_set (var_sheet,
1629 "may-create-vars", FALSE,
1632 vars_scroller = GTK_BIN (get_widget_assert (ia->asst.xml, "vars-scroller"));
1633 old_var_sheet = gtk_bin_get_child (vars_scroller);
1634 if (old_var_sheet != NULL)
1635 gtk_widget_destroy (old_var_sheet);
1636 gtk_container_add (GTK_CONTAINER (vars_scroller), GTK_WIDGET (var_sheet));
1637 gtk_widget_show (GTK_WIDGET (var_sheet));
1639 gtk_widget_destroy (GTK_WIDGET (ia->formats.data_tree_view));
1640 ia->formats.data_tree_view = create_data_tree_view (
1642 GTK_CONTAINER (get_widget_assert (ia->asst.xml, "data-scroller")),
1645 pop_watch_cursor (ia);
1648 /* Clears the set of user-modified variables from IA's formats
1649 substructure. This discards user modifications to variable
1650 formats, thereby causing formats to revert to their
1653 clear_modified_vars (struct import_assistant *ia)
1655 struct formats_page *p = &ia->formats;
1658 for (i = 0; i < p->modified_var_cnt; i++)
1659 var_destroy (p->modified_vars[i]);
1660 free (p->modified_vars);
1661 p->modified_vars = NULL;
1662 p->modified_var_cnt = 0;
1665 /* Resets the formats page to its defaults, discarding user
1668 reset_formats_page (struct import_assistant *ia)
1670 clear_modified_vars (ia);
1671 prepare_formats_page (ia);
1674 /* Called when the user changes one of the variables in the
1677 on_variable_change (PsppireDict *dict, int dict_idx,
1678 struct import_assistant *ia)
1680 struct formats_page *p = &ia->formats;
1681 GtkTreeView *tv = ia->formats.data_tree_view;
1682 gint column_idx = dict_idx + 1;
1684 push_watch_cursor (ia);
1686 /* Remove previous column and replace with new column. */
1687 gtk_tree_view_remove_column (tv, gtk_tree_view_get_column (tv, column_idx));
1688 gtk_tree_view_insert_column (tv, make_data_column (ia, tv, false, dict_idx),
1691 /* Save a copy of the modified variable in modified_vars, so
1692 that its attributes will be preserved if we back up to the
1693 previous page with the Prev button and then come back
1695 if (dict_idx >= p->modified_var_cnt)
1698 p->modified_vars = xnrealloc (p->modified_vars, dict_idx + 1,
1699 sizeof *p->modified_vars);
1700 for (i = 0; i <= dict_idx; i++)
1701 p->modified_vars[i] = NULL;
1702 p->modified_var_cnt = dict_idx + 1;
1704 if (p->modified_vars[dict_idx])
1705 var_destroy (p->modified_vars[dict_idx]);
1706 p->modified_vars[dict_idx]
1707 = var_clone (psppire_dict_get_variable (dict, dict_idx));
1709 pop_watch_cursor (ia);
1712 /* Parses the contents of the field at (ROW,COLUMN) according to
1713 its variable format. If OUTPUTP is non-null, then *OUTPUTP
1714 receives the formatted output for that field (which must be
1715 freed with free). If TOOLTIPP is non-null, then *TOOLTIPP
1716 receives a message suitable for use in a tooltip, if one is
1717 needed, or a null pointer otherwise. Returns true if a
1718 tooltip message is needed, otherwise false. */
1720 parse_field (struct import_assistant *ia,
1721 size_t row, size_t column,
1722 char **outputp, char **tooltipp)
1724 struct substring field;
1726 struct variable *var;
1727 const struct fmt_spec *in;
1728 struct fmt_spec out;
1732 field = ia->separators.columns[column].contents[row];
1733 var = dict_get_var (ia->formats.dict, column);
1734 val = value_create (var_get_width (var));
1735 in = var_get_print_format (var);
1736 out = fmt_for_output_from_input (in);
1738 if (field.string != NULL)
1741 if (!data_in (field, LEGACY_NATIVE, in->type, 0, 0, 0,
1742 val, var_get_width (var)))
1744 char fmt_string[FMT_STRING_LEN_MAX + 1];
1745 fmt_to_string (in, fmt_string);
1746 tooltip = xasprintf (_("Field content \"%.*s\" cannot be parsed in "
1748 (int) field.length, field.string,
1755 tooltip = xstrdup (_("This input line has too few separators "
1756 "to fill in this field."));
1757 value_set_missing (val, var_get_width (var));
1759 if (outputp != NULL)
1761 char *output = xmalloc (out.w + 1);
1762 data_out (val, &out, output);
1763 output[out.w] = '\0';
1768 ok = tooltip == NULL;
1769 if (tooltipp != NULL)
1770 *tooltipp = tooltip;
1776 /* Called to render one of the cells in the data preview tree
1779 render_output_cell (GtkTreeViewColumn *tree_column,
1780 GtkCellRenderer *cell,
1781 GtkTreeModel *model,
1785 struct import_assistant *ia = ia_;
1787 GValue gvalue = { 0, };
1790 ok = parse_field (ia,
1791 (text_import_model_iter_to_row (iter)
1792 + ia->first_line.skip_lines),
1793 GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1797 g_value_init (&gvalue, G_TYPE_STRING);
1798 g_value_take_string (&gvalue, output);
1799 g_object_set_property (G_OBJECT (cell), "text", &gvalue);
1800 g_value_unset (&gvalue);
1803 g_object_set (cell, "background-set", FALSE, (void *) NULL);
1806 "background", "red",
1807 "background-set", TRUE,
1811 /* Called to render a tooltip for one of the cells in the data
1812 preview tree view. */
1814 on_query_output_tooltip (GtkWidget *widget, gint wx, gint wy,
1815 gboolean keyboard_mode UNUSED,
1816 GtkTooltip *tooltip, struct import_assistant *ia)
1821 if (!get_tooltip_location (widget, wx, wy, ia, &row, &column))
1824 if (parse_field (ia, row, column, NULL, &text))
1827 gtk_tooltip_set_text (tooltip, text);
1832 /* Utility functions used by multiple pages of the assistant. */
1835 get_tooltip_location (GtkWidget *widget, gint wx, gint wy,
1836 const struct import_assistant *ia,
1837 size_t *row, size_t *column)
1839 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
1843 GtkTreeViewColumn *tree_column;
1844 GtkTreeModel *tree_model;
1847 /* Check that WIDGET is really visible on the screen before we
1848 do anything else. This is a bug fix for a sticky situation:
1849 when text_data_import_assistant() returns, it frees the data
1850 necessary to compose the tool tip message, but there may be
1851 a tool tip under preparation at that point (even if there is
1852 no visible tool tip) that will call back into us a little
1853 bit later. Perhaps the correct solution to this problem is
1854 to make the data related to the tool tips part of a GObject
1855 that only gets destroyed when all references are released,
1856 but this solution appears to be effective too. */
1857 if (!GTK_WIDGET_MAPPED (widget))
1860 gtk_tree_view_convert_widget_to_bin_window_coords (tree_view,
1862 if (!gtk_tree_view_get_path_at_pos (tree_view, bx, by,
1863 &path, &tree_column, NULL, NULL))
1866 *column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1869 tree_model = gtk_tree_view_get_model (tree_view);
1870 ok = gtk_tree_model_get_iter (tree_model, &iter, path);
1871 gtk_tree_path_free (path);
1875 *row = text_import_model_iter_to_row (&iter) + ia->first_line.skip_lines;
1880 make_tree_view (const struct import_assistant *ia,
1882 GtkTreeView **tree_view)
1884 GtkTreeModel *model;
1886 *tree_view = GTK_TREE_VIEW (gtk_tree_view_new ());
1887 model = GTK_TREE_MODEL (text_import_model_new (
1888 ia->file.lines + first_line,
1889 ia->file.line_cnt - first_line, first_line));
1890 gtk_tree_view_set_model (*tree_view, model);
1892 add_line_number_column (ia, *tree_view);
1896 add_line_number_column (const struct import_assistant *ia,
1897 GtkTreeView *treeview)
1899 GtkTreeViewColumn *column;
1901 column = gtk_tree_view_column_new_with_attributes (
1902 "Line", ia->asst.prop_renderer,
1903 "text", TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER,
1905 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
1906 gtk_tree_view_column_set_fixed_width (
1907 column, get_monospace_width (treeview, ia->asst.prop_renderer, 5));
1908 gtk_tree_view_append_column (treeview, column);
1912 get_monospace_width (GtkTreeView *treeview, GtkCellRenderer *renderer,
1919 ds_put_char_multiple (&s, '0', char_cnt);
1920 ds_put_char (&s, ' ');
1921 width = get_string_width (treeview, renderer, ds_cstr (&s));
1928 get_string_width (GtkTreeView *treeview, GtkCellRenderer *renderer,
1932 g_object_set (G_OBJECT (renderer), "text", string, (void *) NULL);
1933 gtk_cell_renderer_get_size (renderer, GTK_WIDGET (treeview),
1934 NULL, NULL, NULL, &width, NULL);
1938 static GtkTreeViewColumn *
1939 make_data_column (struct import_assistant *ia, GtkTreeView *tree_view,
1940 bool input, gint dict_idx)
1942 struct variable *var = NULL;
1943 struct column *column = NULL;
1944 char name[(VAR_NAME_LEN * 2) + 1];
1946 gint content_width, header_width;
1947 GtkTreeViewColumn *tree_column;
1950 column = &ia->separators.columns[dict_idx];
1952 var = dict_get_var (ia->formats.dict, dict_idx);
1954 escape_underscores (input ? column->name : var_get_name (var), name);
1955 char_cnt = input ? column->width : var_get_print_format (var)->w;
1956 content_width = get_monospace_width (tree_view, ia->asst.fixed_renderer,
1958 header_width = get_string_width (tree_view, ia->asst.prop_renderer,
1961 tree_column = gtk_tree_view_column_new ();
1962 g_object_set_data (G_OBJECT (tree_column), "column-number",
1963 GINT_TO_POINTER (dict_idx));
1964 gtk_tree_view_column_set_title (tree_column, name);
1965 gtk_tree_view_column_pack_start (tree_column, ia->asst.fixed_renderer,
1967 gtk_tree_view_column_set_cell_data_func (
1968 tree_column, ia->asst.fixed_renderer,
1969 input ? render_input_cell : render_output_cell, ia, NULL);
1970 gtk_tree_view_column_set_sizing (tree_column, GTK_TREE_VIEW_COLUMN_FIXED);
1971 gtk_tree_view_column_set_fixed_width (tree_column, MAX (content_width,
1977 static GtkTreeView *
1978 create_data_tree_view (bool input, GtkContainer *parent,
1979 struct import_assistant *ia)
1981 GtkTreeView *tree_view;
1984 make_tree_view (ia, ia->first_line.skip_lines, &tree_view);
1985 gtk_tree_selection_set_mode (gtk_tree_view_get_selection (tree_view),
1986 GTK_SELECTION_NONE);
1988 for (i = 0; i < ia->separators.column_cnt; i++)
1989 gtk_tree_view_append_column (tree_view,
1990 make_data_column (ia, tree_view, input, i));
1992 g_object_set (G_OBJECT (tree_view), "has-tooltip", TRUE, (void *) NULL);
1993 g_signal_connect (tree_view, "query-tooltip",
1994 G_CALLBACK (input ? on_query_input_tooltip
1995 : on_query_output_tooltip), ia);
1996 gtk_tree_view_set_fixed_height_mode (tree_view, true);
1998 gtk_container_add (parent, GTK_WIDGET (tree_view));
1999 gtk_widget_show (GTK_WIDGET (tree_view));
2005 escape_underscores (const char *in, char *out)
2007 for (; *in != '\0'; in++)
2016 /* TextImportModel, a GtkTreeModel implementation used by some
2017 pages of the assistant. */
2019 #define G_TYPE_TEXT_IMPORT_MODEL (text_import_model_get_type ())
2020 #define TEXT_IMPORT_MODEL(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), G_TYPE_TEXT_IMPORT_MODEL, TextImportModel))
2021 #define TEXT_IMPORT_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), G_TYPE_TEXT_IMPORT_MODEL, TextImportModelClass))
2022 #define IS_TEXT_IMPORT_MODEL(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), G_TYPE_TEXT_IMPORT_MODEL))
2023 #define IS_TEXT_IMPORT_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), G_TYPE_TEXT_IMPORT_MODEL))
2024 #define TEXT_IMPORT_MODEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), G_TYPE_TEXT_IMPORT_MODEL, TextImportModelClass))
2026 /* Random number used in 'stamp' member of GtkTreeIter. */
2027 #define TREE_MODEL_STAMP 0x7efd67d3
2029 struct TextImportModel
2032 struct string *lines;
2037 struct TextImportModelClass
2039 GObjectClass parent_class;
2042 GType text_import_model_get_type (void);
2043 static void text_import_model_tree_model_init (gpointer iface, gpointer data);
2046 text_import_model_get_type (void)
2048 static GType object_type = 0;
2052 static const GTypeInfo object_info = {
2053 sizeof (TextImportModelClass),
2054 (GBaseInitFunc) NULL,
2055 (GBaseFinalizeFunc) NULL,
2056 NULL, /* class_init */
2057 NULL, /* class_finalize */
2058 NULL, /* class_data */
2059 sizeof (TextImportModel),
2060 0, /* n_preallocs */
2061 NULL, /* instance_init */
2064 static const GInterfaceInfo tree_model_info = {
2065 text_import_model_tree_model_init,
2070 object_type = g_type_register_static (G_TYPE_OBJECT,
2074 g_type_add_interface_static (object_type, GTK_TYPE_TREE_MODEL,
2084 /* Creates and returns a new TextImportModel that contains the
2085 LINE_CNT lines in LINES. The lines before FIRST_LINE in LINES
2086 are not part of the model, but they are included in the line
2087 numbers in the TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER column.
2089 The caller retains responsibility for freeing LINES and must
2090 ensure that its lifetime and that of the strings that it
2091 contains exceeds that of the TextImportModel. */
2093 text_import_model_new (struct string *lines, size_t line_cnt,
2096 TextImportModel *new_text_import_model
2097 = g_object_new (G_TYPE_TEXT_IMPORT_MODEL, NULL);
2098 new_text_import_model->lines = lines;
2099 new_text_import_model->line_cnt = line_cnt;
2100 new_text_import_model->first_line = first_line;
2101 return new_text_import_model;
2106 tree_model_iter_has_child (GtkTreeModel *tree_model,
2113 tree_model_iter_parent (GtkTreeModel *tree_model,
2120 static GtkTreeModelFlags
2121 tree_model_get_flags (GtkTreeModel *model)
2123 g_return_val_if_fail (IS_TEXT_IMPORT_MODEL (model), (GtkTreeModelFlags) 0);
2125 return GTK_TREE_MODEL_LIST_ONLY | GTK_TREE_MODEL_ITERS_PERSIST;
2130 tree_model_n_columns (GtkTreeModel *model)
2136 tree_model_column_type (GtkTreeModel *model, gint index)
2138 return (index == TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER ? G_TYPE_INT
2139 : index == TEXT_IMPORT_MODEL_COLUMN_LINE ? G_TYPE_STRING
2144 init_iter (TextImportModel *list, gint idx, GtkTreeIter *iter)
2146 if (idx < 0 || idx >= list->line_cnt)
2149 iter->user_data = GINT_TO_POINTER (-1);
2154 iter->stamp = TREE_MODEL_STAMP;
2155 iter->user_data = GINT_TO_POINTER (idx);
2161 tree_model_get_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreePath *path)
2163 gint *indices, depth;
2165 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2167 g_return_val_if_fail (path, FALSE);
2169 indices = gtk_tree_path_get_indices (path);
2170 depth = gtk_tree_path_get_depth (path);
2172 g_return_val_if_fail (depth == 1, FALSE);
2174 return init_iter (list, indices[0], iter);
2179 tree_model_iter_next (GtkTreeModel *model, GtkTreeIter *iter)
2181 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2184 assert (iter->stamp == TREE_MODEL_STAMP);
2186 idx = GPOINTER_TO_INT (iter->user_data);
2187 return init_iter (list, idx == -1 ? -1 : idx + 1, iter);
2190 static GtkTreePath *
2191 tree_model_get_path (GtkTreeModel *model, GtkTreeIter *iter)
2195 g_return_val_if_fail (iter->stamp == TREE_MODEL_STAMP, FALSE);
2197 path = gtk_tree_path_new ();
2198 gtk_tree_path_append_index (path, GPOINTER_TO_INT (iter->user_data));
2204 tree_model_get_value (GtkTreeModel *model, GtkTreeIter *iter,
2205 gint column, GValue *value)
2207 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2210 g_return_if_fail (iter->stamp == TREE_MODEL_STAMP);
2212 idx = GPOINTER_TO_INT (iter->user_data);
2213 assert (idx >= 0 && idx < list->line_cnt);
2217 g_value_init (value, G_TYPE_INT);
2218 g_value_set_int (value, idx + list->first_line + 1);
2222 g_value_init (value, G_TYPE_STRING);
2223 g_value_set_static_string (value, ds_cstr (&list->lines[idx]));
2228 tree_model_iter_children (GtkTreeModel *tree_model,
2230 GtkTreeIter *parent)
2236 tree_model_n_children (GtkTreeModel *model, GtkTreeIter *iter)
2238 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2240 return iter == NULL ? list->line_cnt : 0;
2244 tree_model_nth_child (GtkTreeModel *model, GtkTreeIter *iter,
2245 GtkTreeIter *parent, gint n)
2247 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2248 g_return_val_if_fail (IS_TEXT_IMPORT_MODEL (model), FALSE);
2252 return init_iter (list, n, iter);
2256 text_import_model_tree_model_init (gpointer iface_, gpointer data UNUSED)
2258 GtkTreeModelIface *iface = (GtkTreeModelIface *) iface_;
2260 iface->get_flags = tree_model_get_flags;
2261 iface->get_n_columns = tree_model_n_columns;
2262 iface->get_column_type = tree_model_column_type;
2263 iface->get_iter = tree_model_get_iter;
2264 iface->iter_next = tree_model_iter_next;
2265 iface->get_path = tree_model_get_path;
2266 iface->get_value = tree_model_get_value;
2268 iface->iter_children = tree_model_iter_children;
2269 iface->iter_has_child = tree_model_iter_has_child;
2270 iface->iter_n_children = tree_model_n_children;
2271 iface->iter_nth_child = tree_model_nth_child;
2272 iface->iter_parent = tree_model_iter_parent;
2276 text_import_model_iter_to_row (const GtkTreeIter *iter)
2278 assert (iter->stamp == TREE_MODEL_STAMP);
2279 return GPOINTER_TO_INT (iter->user_data);
2282 /* Increments the "watch cursor" level, setting the cursor for
2283 the assistant window to a watch face to indicate to the user
2284 that the ongoing operation may take some time. */
2286 push_watch_cursor (struct import_assistant *ia)
2288 if (++ia->asst.watch_cursor == 1)
2290 GtkWidget *widget = GTK_WIDGET (ia->asst.assistant);
2291 GdkDisplay *display = gtk_widget_get_display (widget);
2292 GdkCursor *cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
2293 gdk_window_set_cursor (widget->window, cursor);
2294 gdk_cursor_unref (cursor);
2295 gdk_display_flush (display);
2299 /* Decrements the "watch cursor" level. If the level reaches
2300 zero, the cursor is reset to its default shape. */
2302 pop_watch_cursor (struct import_assistant *ia)
2304 if (--ia->asst.watch_cursor == 0)
2306 GtkWidget *widget = GTK_WIDGET (ia->asst.assistant);
2307 gdk_window_set_cursor (widget->window, NULL);