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/psppire-syntax-window.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);
297 GtkWidget *se = psppire_syntax_window_new ();
299 gtk_text_buffer_insert_at_cursor (PSPPIRE_SYNTAX_WINDOW (se)->buffer, syntax, -1);
301 gtk_widget_show (se);
310 destroy_formats_page (ia);
311 destroy_separators_page (ia);
312 destroy_assistant (ia);
317 /* Emits PSPP syntax to S that applies the dictionary attributes
318 (such as missing values and value labels) of the variables in
321 apply_dict (const struct dictionary *dict, struct string *s)
323 size_t var_cnt = dict_get_var_cnt (dict);
326 for (i = 0; i < var_cnt; i++)
328 struct variable *var = dict_get_var (dict, i);
329 const char *name = var_get_name (var);
330 enum val_type type = var_get_type (var);
331 int width = var_get_width (var);
332 enum measure measure = var_get_measure (var);
333 enum alignment alignment = var_get_alignment (var);
334 const struct fmt_spec *format = var_get_print_format (var);
336 if (var_has_missing_values (var))
338 const struct missing_values *mv = var_get_missing_values (var);
341 syntax_gen_pspp (s, "MISSING VALUES %ss (", name);
342 for (j = 0; j < mv_n_values (mv); j++)
346 ds_put_cstr (s, ", ");
347 mv_get_value (mv, &value, j);
348 syntax_gen_value (s, &value, width, format);
351 if (mv_has_range (mv))
354 if (mv_has_value (mv))
355 ds_put_cstr (s, ", ");
356 mv_get_range (mv, &low, &high);
357 syntax_gen_num_range (s, low, high, format);
359 ds_put_cstr (s, ").\n");
361 if (var_has_value_labels (var))
363 const struct val_labs *vls = var_get_value_labels (var);
364 struct val_labs_iterator *iter;
367 syntax_gen_pspp (s, "VALUE LABELS %ss", name);
368 for (vl = val_labs_first_sorted (vls, &iter); vl != NULL;
369 vl = val_labs_next (vls, &iter))
371 ds_put_cstr (s, "\n ");
372 syntax_gen_value (s, &vl->value, width, format);
373 ds_put_char (s, ' ');
374 syntax_gen_string (s, ss_cstr (vl->label));
376 ds_put_cstr (s, ".\n");
378 if (var_has_label (var))
379 syntax_gen_pspp (s, "VARIABLE LABELS %ss %sq.\n",
380 name, var_get_label (var));
381 if (measure != var_default_measure (type))
382 syntax_gen_pspp (s, "VARIABLE LEVEL %ss (%ss).\n",
384 (measure == MEASURE_NOMINAL ? "NOMINAL"
385 : measure == MEASURE_ORDINAL ? "ORDINAL"
387 if (alignment != var_default_alignment (type))
388 syntax_gen_pspp (s, "VARIABLE ALIGNMENT %ss (%ss).\n",
390 (alignment == ALIGN_LEFT ? "LEFT"
391 : alignment == ALIGN_CENTRE ? "CENTER"
393 if (var_get_display_width (var) != var_default_display_width (width))
394 syntax_gen_pspp (s, "VARIABLE WIDTH %ss (%d).\n",
395 name, var_get_display_width (var));
399 /* Generates and returns PSPP syntax to execute the import
400 operation described by IA. The caller must free the syntax
403 generate_syntax (const struct import_assistant *ia)
405 struct string s = DS_EMPTY_INITIALIZER;
414 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (
415 ia->intro.n_cases_button)))
416 ds_put_format (&s, " /IMPORTCASES=FIRST %d\n",
417 gtk_spin_button_get_value_as_int (
418 GTK_SPIN_BUTTON (ia->intro.n_cases_spin)));
419 else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (
420 ia->intro.percent_button)))
421 ds_put_format (&s, " /IMPORTCASES=PERCENT %d\n",
422 gtk_spin_button_get_value_as_int (
423 GTK_SPIN_BUTTON (ia->intro.percent_spin)));
425 ds_put_cstr (&s, " /IMPORTCASES=ALL\n");
427 " /ARRANGEMENT=DELIMITED\n"
429 if (ia->first_line.skip_lines > 0)
430 ds_put_format (&s, " /FIRSTCASE=%d\n", ia->first_line.skip_lines + 1);
431 ds_put_cstr (&s, " /DELIMITERS=\"");
432 if (ds_find_char (&ia->separators.separators, '\t') != SIZE_MAX)
433 ds_put_cstr (&s, "\\t");
434 if (ds_find_char (&ia->separators.separators, '\\') != SIZE_MAX)
435 ds_put_cstr (&s, "\\\\");
436 for (i = 0; i < ds_length (&ia->separators.separators); i++)
438 char c = ds_at (&ia->separators.separators, i);
440 ds_put_cstr (&s, "\"\"");
441 else if (c != '\t' && c != '\\')
444 ds_put_cstr (&s, "\"\n");
445 if (!ds_is_empty (&ia->separators.quotes))
446 syntax_gen_pspp (&s, " /QUALIFIER=%sq\n", ds_cstr (&ia->separators.quotes));
447 if (!ds_is_empty (&ia->separators.quotes) && ia->separators.escape)
448 ds_put_cstr (&s, " /ESCAPE\n");
449 ds_put_cstr (&s, " /VARIABLES=\n");
451 var_cnt = dict_get_var_cnt (ia->formats.dict);
452 for (i = 0; i < var_cnt; i++)
454 struct variable *var = dict_get_var (ia->formats.dict, i);
455 char format_string[FMT_STRING_LEN_MAX + 1];
456 fmt_to_string (var_get_print_format (var), format_string);
457 ds_put_format (&s, " %s %s%s\n",
458 var_get_name (var), format_string,
459 i == var_cnt - 1 ? "." : "");
462 apply_dict (ia->formats.dict, &s);
467 /* Choosing a file and reading it. */
469 static char *choose_file (GtkWindow *parent_window);
471 /* Obtains the file to import from the user and initializes IA's
472 file substructure. PARENT_WINDOW must be the window to use
473 as the file chooser window's parent.
475 Returns true if successful, false if the file name could not
476 be obtained or the file could not be read. */
478 init_file (struct import_assistant *ia, GtkWindow *parent_window)
480 struct file *file = &ia->file;
481 enum { MAX_PREVIEW_LINES = 1000 }; /* Max number of lines to read. */
482 enum { MAX_LINE_LEN = 16384 }; /* Max length of an acceptable line. */
485 file->file_name = choose_file (parent_window);
486 if (file->file_name == NULL)
489 stream = fopen (file->file_name, "r");
492 msg (ME, _("Could not open \"%s\": %s"),
493 file->file_name, strerror (errno));
497 file->lines = xnmalloc (MAX_PREVIEW_LINES, sizeof *file->lines);
498 for (; file->line_cnt < MAX_PREVIEW_LINES; file->line_cnt++)
500 struct string *line = &file->lines[file->line_cnt];
502 ds_init_empty (line);
503 if (!ds_read_line (line, stream, MAX_LINE_LEN))
507 else if (ferror (stream))
508 msg (ME, _("Error reading \"%s\": %s"),
509 file->file_name, strerror (errno));
511 msg (ME, _("Failed to read \"%s\", because it contains a line "
512 "over %d bytes long and therefore appears not to be "
514 file->file_name, MAX_LINE_LEN);
519 ds_chomp (line, '\n');
520 ds_chomp (line, '\r');
523 if (file->line_cnt == 0)
525 msg (ME, _("\"%s\" is empty."), file->file_name);
531 /* Estimate the number of lines in the file. */
532 if (file->line_cnt < MAX_PREVIEW_LINES)
533 file->total_lines = file->line_cnt;
537 off_t position = ftello (stream);
538 if (fstat (fileno (stream), &s) == 0 && position > 0)
539 file->total_lines = (double) file->line_cnt / position * s.st_size;
541 file->total_lines = 0;
547 /* Frees IA's file substructure. */
549 destroy_file (struct import_assistant *ia)
551 struct file *f = &ia->file;
554 for (i = 0; i < f->line_cnt; i++)
555 ds_destroy (&f->lines[i]);
557 g_free (f->file_name);
560 /* Obtains the file to read from the user and returns the name of
561 the file as a string that must be freed with g_free if
562 successful, otherwise a null pointer. PARENT_WINDOW must be
563 the window to use as the file chooser window's parent. */
565 choose_file (GtkWindow *parent_window)
570 dialog = gtk_file_chooser_dialog_new (_("Import Delimited Text Data"),
572 GTK_FILE_CHOOSER_ACTION_OPEN,
573 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
574 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
577 switch (gtk_dialog_run (GTK_DIALOG (dialog)))
579 case GTK_RESPONSE_ACCEPT:
580 file_name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
586 gtk_widget_destroy (dialog);
593 static void close_assistant (struct import_assistant *, int response);
594 static void on_prepare (GtkAssistant *assistant, GtkWidget *page,
595 struct import_assistant *);
596 static void on_cancel (GtkAssistant *assistant, struct import_assistant *);
597 static void on_close (GtkAssistant *assistant, struct import_assistant *);
598 static void on_paste (GtkButton *button, struct import_assistant *);
599 static void on_reset (GtkButton *button, struct import_assistant *);
600 static void close_assistant (struct import_assistant *, int response);
602 /* Initializes IA's asst substructure. PARENT_WINDOW must be the
603 window to use as the assistant window's parent. */
605 init_assistant (struct import_assistant *ia, GtkWindow *parent_window)
607 struct assistant *a = &ia->asst;
609 a->xml = XML_NEW ("text-data-import.glade");
610 a->assistant = GTK_ASSISTANT (gtk_assistant_new ());
611 g_signal_connect (a->assistant, "prepare", G_CALLBACK (on_prepare), ia);
612 g_signal_connect (a->assistant, "cancel", G_CALLBACK (on_cancel), ia);
613 g_signal_connect (a->assistant, "close", G_CALLBACK (on_close), ia);
614 a->paste_button = gtk_button_new_from_stock (GTK_STOCK_PASTE);
615 gtk_assistant_add_action_widget (a->assistant, a->paste_button);
616 g_signal_connect (a->paste_button, "clicked", G_CALLBACK (on_paste), ia);
617 a->reset_button = gtk_button_new_from_stock ("pspp-stock-reset");
618 gtk_assistant_add_action_widget (a->assistant, a->reset_button);
619 g_signal_connect (a->reset_button, "clicked", G_CALLBACK (on_reset), ia);
620 gtk_window_set_title (GTK_WINDOW (a->assistant),
621 _("Importing Delimited Text Data"));
622 gtk_window_set_transient_for (GTK_WINDOW (a->assistant), parent_window);
624 a->prop_renderer = gtk_cell_renderer_text_new ();
625 g_object_ref_sink (a->prop_renderer);
626 a->fixed_renderer = gtk_cell_renderer_text_new ();
627 g_object_ref_sink (a->fixed_renderer);
628 g_object_set (G_OBJECT (a->fixed_renderer),
629 "family", "Monospace",
633 /* Frees IA's asst substructure. */
635 destroy_assistant (struct import_assistant *ia)
637 struct assistant *a = &ia->asst;
639 g_object_unref (a->prop_renderer);
640 g_object_unref (a->fixed_renderer);
641 g_object_unref (a->xml);
644 /* Appends a page of the given TYPE, with PAGE as its content, to
645 the GtkAssistant encapsulated by IA. Returns the GtkWidget
646 that represents the page. */
648 add_page_to_assistant (struct import_assistant *ia,
649 GtkWidget *page, GtkAssistantPageType type)
655 title = gtk_window_get_title (GTK_WINDOW (page));
656 title_copy = xstrdup (title ? title : "");
658 content = gtk_bin_get_child (GTK_BIN (page));
660 g_object_ref (content);
661 gtk_container_remove (GTK_CONTAINER (page), content);
663 gtk_widget_destroy (page);
665 gtk_assistant_append_page (ia->asst.assistant, content);
666 gtk_assistant_set_page_type (ia->asst.assistant, content, type);
667 gtk_assistant_set_page_title (ia->asst.assistant, content, title_copy);
668 gtk_assistant_set_page_complete (ia->asst.assistant, content, true);
675 /* Called just before PAGE is displayed as the current page of
676 ASSISTANT, this updates IA content according to the new
679 on_prepare (GtkAssistant *assistant, GtkWidget *page,
680 struct import_assistant *ia)
682 if (page == ia->separators.page)
683 prepare_separators_page (ia);
684 else if (page == ia->formats.page)
685 prepare_formats_page (ia);
687 gtk_widget_show (ia->asst.reset_button);
688 if (page == ia->formats.page)
689 gtk_widget_show (ia->asst.paste_button);
691 gtk_widget_hide (ia->asst.paste_button);
694 /* Called when the Cancel button in the assistant is clicked. */
696 on_cancel (GtkAssistant *assistant, struct import_assistant *ia)
698 close_assistant (ia, GTK_RESPONSE_CANCEL);
701 /* Called when the Apply button on the last page of the assistant
704 on_close (GtkAssistant *assistant, struct import_assistant *ia)
706 close_assistant (ia, GTK_RESPONSE_APPLY);
709 /* Called when the Paste button on the last page of the assistant
712 on_paste (GtkButton *button, struct import_assistant *ia)
714 close_assistant (ia, PSPPIRE_RESPONSE_PASTE);
717 /* Called when the Reset button is clicked. */
719 on_reset (GtkButton *button, struct import_assistant *ia)
721 gint page_num = gtk_assistant_get_current_page (ia->asst.assistant);
722 GtkWidget *page = gtk_assistant_get_nth_page (ia->asst.assistant, page_num);
724 if (page == ia->intro.page)
725 reset_intro_page (ia);
726 else if (page == ia->first_line.page)
727 reset_first_line_page (ia);
728 else if (page == ia->separators.page)
729 reset_separators_page (ia);
730 else if (page == ia->formats.page)
731 reset_formats_page (ia);
734 /* Causes the assistant to close, returning RESPONSE for
735 interpretation by text_data_import_assistant. */
737 close_assistant (struct import_assistant *ia, int response)
739 ia->asst.response = response;
740 g_main_loop_quit (ia->asst.main_loop);
741 gtk_widget_hide (GTK_WIDGET (ia->asst.assistant));
744 /* The "intro" page of the assistant. */
746 static void on_intro_amount_changed (GtkToggleButton *button,
747 struct import_assistant *);
749 /* Initializes IA's intro substructure. */
751 init_intro_page (struct import_assistant *ia)
753 GladeXML *xml = ia->asst.xml;
754 struct intro_page *p = &ia->intro;
757 p->page = add_page_to_assistant (ia, get_widget_assert (xml, "Intro"),
758 GTK_ASSISTANT_PAGE_INTRO);
759 p->all_cases_button = get_widget_assert (xml, "import-all-cases");
760 p->n_cases_button = get_widget_assert (xml, "import-n-cases");
761 p->n_cases_spin = get_widget_assert (xml, "n-cases-spin");
762 p->percent_button = get_widget_assert (xml, "import-percent");
763 p->percent_spin = get_widget_assert (xml, "percent-spin");
764 g_signal_connect (p->all_cases_button, "toggled",
765 G_CALLBACK (on_intro_amount_changed), ia);
766 g_signal_connect (p->n_cases_button, "toggled",
767 G_CALLBACK (on_intro_amount_changed), ia);
768 g_signal_connect (p->percent_button, "toggled",
769 G_CALLBACK (on_intro_amount_changed), ia);
772 ds_put_cstr (&s, _("This assistant will guide you through the process of "
773 "importing data into PSPP from a text file with one line "
774 "per case, in which fields are separated by tabs, "
775 "commas, or other delimiters.\n\n"));
776 if (ia->file.total_is_exact)
778 &s, ngettext ("The selected file contains %zu line of text. ",
779 "The selected file contains %zu lines of text. ",
782 else if (ia->file.total_lines > 0)
786 "The selected file contains approximately %lu line of text. ",
787 "The selected file contains approximately %lu lines of text. ",
788 ia->file.total_lines),
789 ia->file.total_lines);
792 "Only the first %zu line of the file will be shown for "
793 "preview purposes in the following screens. ",
794 "Only the first %zu lines of the file will be shown for "
795 "preview purposes in the following screens. ",
799 ds_put_cstr (&s, _("You may choose below how much of the file should "
800 "actually be imported."));
801 gtk_label_set_text (GTK_LABEL (get_widget_assert (xml, "intro-label")),
806 /* Resets IA's intro page to its initial state. */
808 reset_intro_page (struct import_assistant *ia)
810 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->intro.all_cases_button),
814 /* Called when one of the radio buttons is clicked. */
816 on_intro_amount_changed (GtkToggleButton *button UNUSED,
817 struct import_assistant *ia)
819 struct intro_page *p = &ia->intro;
821 gtk_widget_set_sensitive (p->n_cases_spin,
822 gtk_toggle_button_get_active (
823 GTK_TOGGLE_BUTTON (p->n_cases_button)));
825 gtk_widget_set_sensitive (ia->intro.percent_spin,
826 gtk_toggle_button_get_active (
827 GTK_TOGGLE_BUTTON (p->percent_button)));
830 /* The "first line" page of the assistant. */
832 static GtkTreeView *create_lines_tree_view (GtkContainer *parent_window,
833 struct import_assistant *);
834 static void on_first_line_change (GtkTreeSelection *,
835 struct import_assistant *);
836 static void on_variable_names_cb_toggle (GtkToggleButton *,
837 struct import_assistant *);
838 static void set_first_line (struct import_assistant *);
839 static void get_first_line (struct import_assistant *);
841 /* Initializes IA's first_line substructure. */
843 init_first_line_page (struct import_assistant *ia)
845 struct first_line_page *p = &ia->first_line;
846 GladeXML *xml = ia->asst.xml;
848 p->page = add_page_to_assistant (ia, get_widget_assert (xml, "FirstLine"),
849 GTK_ASSISTANT_PAGE_CONTENT);
850 gtk_widget_destroy (get_widget_assert (xml, "first-line"));
851 p->tree_view = create_lines_tree_view (
852 GTK_CONTAINER (get_widget_assert (xml, "first-line-scroller")), ia);
853 p->variable_names_cb = get_widget_assert (xml, "variable-names");
854 gtk_tree_selection_set_mode (
855 gtk_tree_view_get_selection (GTK_TREE_VIEW (p->tree_view)),
856 GTK_SELECTION_BROWSE);
858 g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (p->tree_view)),
859 "changed", G_CALLBACK (on_first_line_change), ia);
860 g_signal_connect (p->variable_names_cb, "toggled",
861 G_CALLBACK (on_variable_names_cb_toggle), ia);
864 /* Resets the first_line page to its initial content. */
866 reset_first_line_page (struct import_assistant *ia)
868 ia->first_line.skip_lines = 0;
869 ia->first_line.variable_names = false;
873 /* Creates and returns a tree view that contains each of the
874 lines in IA's file as a row. */
876 create_lines_tree_view (GtkContainer *parent, struct import_assistant *ia)
878 GtkTreeView *tree_view;
879 GtkTreeViewColumn *column;
880 size_t max_line_length;
881 gint content_width, header_width;
884 make_tree_view (ia, 0, &tree_view);
886 column = gtk_tree_view_column_new_with_attributes (
887 "Text", ia->asst.fixed_renderer,
888 "text", TEXT_IMPORT_MODEL_COLUMN_LINE,
890 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
893 for (i = 0; i < ia->file.line_cnt; i++)
895 size_t w = ds_length (&ia->file.lines[i]);
896 max_line_length = MAX (max_line_length, w);
899 content_width = get_monospace_width (tree_view, ia->asst.fixed_renderer,
901 header_width = get_string_width (tree_view, ia->asst.prop_renderer, "Text");
902 gtk_tree_view_column_set_fixed_width (column, MAX (content_width,
904 gtk_tree_view_append_column (tree_view, column);
906 gtk_tree_view_set_fixed_height_mode (tree_view, true);
908 gtk_container_add (parent, GTK_WIDGET (tree_view));
909 gtk_widget_show (GTK_WIDGET (tree_view));
914 /* Called when the line selected in the first_line tree view
917 on_first_line_change (GtkTreeSelection *selection UNUSED,
918 struct import_assistant *ia)
923 /* Called when the checkbox that indicates whether variable
924 names are in the row above the first line is toggled. */
926 on_variable_names_cb_toggle (GtkToggleButton *variable_names_cb UNUSED,
927 struct import_assistant *ia)
932 /* Sets the widgets to match IA's first_line substructure. */
934 set_first_line (struct import_assistant *ia)
938 path = gtk_tree_path_new_from_indices (ia->first_line.skip_lines, -1);
939 gtk_tree_view_set_cursor (GTK_TREE_VIEW (ia->first_line.tree_view),
941 gtk_tree_path_free (path);
943 gtk_toggle_button_set_active (
944 GTK_TOGGLE_BUTTON (ia->first_line.variable_names_cb),
945 ia->first_line.variable_names);
946 gtk_widget_set_sensitive (ia->first_line.variable_names_cb,
947 ia->first_line.skip_lines > 0);
950 /* Sets IA's first_line substructure to match the widgets. */
952 get_first_line (struct import_assistant *ia)
954 GtkTreeSelection *selection;
958 selection = gtk_tree_view_get_selection (ia->first_line.tree_view);
959 if (gtk_tree_selection_get_selected (selection, &model, &iter))
961 GtkTreePath *path = gtk_tree_model_get_path (model, &iter);
962 int row = gtk_tree_path_get_indices (path)[0];
963 gtk_tree_path_free (path);
965 ia->first_line.skip_lines = row;
966 ia->first_line.variable_names =
967 (ia->first_line.skip_lines > 0
968 && gtk_toggle_button_get_active (
969 GTK_TOGGLE_BUTTON (ia->first_line.variable_names_cb)));
971 gtk_widget_set_sensitive (ia->first_line.variable_names_cb,
972 ia->first_line.skip_lines > 0);
975 /* The "separators" page of the assistant. */
977 static void revise_fields_preview (struct import_assistant *ia);
978 static void choose_likely_separators (struct import_assistant *ia);
979 static void find_commonest_chars (unsigned long int histogram[UCHAR_MAX + 1],
980 const char *targets, const char *def,
981 struct string *result);
982 static void clear_fields (struct import_assistant *ia);
983 static void revise_fields_preview (struct import_assistant *);
984 static void set_separators (struct import_assistant *);
985 static void get_separators (struct import_assistant *);
986 static void on_separators_custom_entry_notify (GObject *UNUSED,
988 struct import_assistant *);
989 static void on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
990 struct import_assistant *);
991 static void on_quote_combo_change (GtkComboBox *combo,
992 struct import_assistant *);
993 static void on_quote_cb_toggle (GtkToggleButton *quote_cb,
994 struct import_assistant *);
995 static void on_separator_toggle (GtkToggleButton *, struct import_assistant *);
996 static void render_input_cell (GtkTreeViewColumn *tree_column,
997 GtkCellRenderer *cell,
998 GtkTreeModel *model, GtkTreeIter *iter,
1000 static gboolean on_query_input_tooltip (GtkWidget *widget, gint wx, gint wy,
1001 gboolean keyboard_mode UNUSED,
1002 GtkTooltip *tooltip,
1003 struct import_assistant *);
1005 /* A common field separator and its identifying name. */
1008 const char *name; /* Name (for use with get_widget_assert). */
1009 int c; /* Separator character. */
1012 /* All the separators in the dialog box. */
1013 static const struct separator separators[] =
1025 #define SEPARATOR_CNT (sizeof separators / sizeof *separators)
1027 /* Initializes IA's separators substructure. */
1029 init_separators_page (struct import_assistant *ia)
1031 GladeXML *xml = ia->asst.xml;
1032 struct separators_page *p = &ia->separators;
1035 choose_likely_separators (ia);
1037 p->page = add_page_to_assistant (ia, get_widget_assert (xml, "Separators"),
1038 GTK_ASSISTANT_PAGE_CONTENT);
1039 p->custom_cb = get_widget_assert (xml, "custom-cb");
1040 p->custom_entry = get_widget_assert (xml, "custom-entry");
1041 p->quote_combo = get_widget_assert (xml, "quote-combo");
1042 p->quote_entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (p->quote_combo)));
1043 p->quote_cb = get_widget_assert (xml, "quote-cb");
1044 p->escape_cb = get_widget_assert (xml, "escape");
1046 set_separators (ia);
1047 p->fields_tree_view = GTK_TREE_VIEW (get_widget_assert (xml, "fields"));
1048 g_signal_connect (GTK_COMBO_BOX (p->quote_combo), "changed",
1049 G_CALLBACK (on_quote_combo_change), ia);
1050 g_signal_connect (p->quote_cb, "toggled",
1051 G_CALLBACK (on_quote_cb_toggle), ia);
1052 g_signal_connect (GTK_ENTRY (p->custom_entry), "notify::text",
1053 G_CALLBACK (on_separators_custom_entry_notify), ia);
1054 g_signal_connect (p->custom_cb, "toggled",
1055 G_CALLBACK (on_separators_custom_cb_toggle), ia);
1056 for (i = 0; i < SEPARATOR_CNT; i++)
1057 g_signal_connect (get_widget_assert (xml, separators[i].name),
1058 "toggled", G_CALLBACK (on_separator_toggle), ia);
1059 g_signal_connect (p->escape_cb, "toggled",
1060 G_CALLBACK (on_separator_toggle), ia);
1063 /* Frees IA's separators substructure. */
1065 destroy_separators_page (struct import_assistant *ia)
1067 struct separators_page *s = &ia->separators;
1069 ds_destroy (&s->separators);
1070 ds_destroy (&s->quotes);
1074 /* Called just before the separators page becomes visible in the
1077 prepare_separators_page (struct import_assistant *ia)
1079 revise_fields_preview (ia);
1082 /* Called when the Reset button is clicked on the separators
1083 page, resets the separators to the defaults. */
1085 reset_separators_page (struct import_assistant *ia)
1087 choose_likely_separators (ia);
1088 set_separators (ia);
1091 /* Frees and clears the column data in IA's separators
1094 clear_fields (struct import_assistant *ia)
1096 struct separators_page *s = &ia->separators;
1098 if (s->column_cnt > 0)
1103 for (row = 0; row < ia->file.line_cnt; row++)
1105 const struct string *line = &ia->file.lines[row];
1106 const char *line_start = ds_data (line);
1107 const char *line_end = ds_end (line);
1109 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1111 char *s = ss_data (col->contents[row]);
1112 if (!(s >= line_start && s <= line_end))
1113 ss_dealloc (&col->contents[row]);
1117 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1120 free (col->contents);
1129 /* Breaks the file data in IA into columns based on the
1130 separators set in IA's separators substructure. */
1132 split_fields (struct import_assistant *ia)
1134 struct separators_page *s = &ia->separators;
1135 size_t columns_allocated;
1141 /* Is space in the set of separators? */
1142 space_sep = ss_find_char (ds_ss (&s->separators), ' ') != SIZE_MAX;
1144 /* Split all the lines, not just those from
1145 ia->first_line.skip_lines on, so that we split the line that
1146 contains variables names if ia->first_line.variable_names is
1148 columns_allocated = 0;
1149 for (row = 0; row < ia->file.line_cnt; row++)
1151 struct string *line = &ia->file.lines[row];
1152 struct substring text = ds_ss (line);
1155 for (column_idx = 0; ; column_idx++)
1157 struct substring field;
1158 struct column *column;
1161 ss_ltrim (&text, ss_cstr (" "));
1162 if (ss_is_empty (text))
1164 if (column_idx != 0)
1168 else if (!ds_is_empty (&s->quotes)
1169 && ds_find_char (&s->quotes, text.string[0]) != SIZE_MAX)
1171 int quote = ss_get_char (&text);
1173 ss_get_until (&text, quote, &field);
1180 while ((c = ss_get_char (&text)) != EOF)
1182 ds_put_char (&s, c);
1183 else if (ss_match_char (&text, quote))
1184 ds_put_char (&s, quote);
1191 ss_get_chars (&text, ss_cspan (text, ds_ss (&s->separators)),
1194 if (column_idx >= s->column_cnt)
1196 struct column *column;
1198 if (s->column_cnt >= columns_allocated)
1199 s->columns = x2nrealloc (s->columns, &columns_allocated,
1200 sizeof *s->columns);
1201 column = &s->columns[s->column_cnt++];
1202 column->name = NULL;
1204 column->contents = xcalloc (ia->file.line_cnt,
1205 sizeof *column->contents);
1207 column = &s->columns[column_idx];
1208 column->contents[row] = field;
1209 if (ss_length (field) > column->width)
1210 column->width = ss_length (field);
1213 ss_ltrim (&text, ss_cstr (" "));
1214 if (ss_is_empty (text))
1216 if (ss_find_char (ds_ss (&s->separators), ss_first (text))
1218 ss_advance (&text, 1);
1223 /* Chooses a name for each column on the separators page */
1225 choose_column_names (struct import_assistant *ia)
1227 const struct first_line_page *f = &ia->first_line;
1228 struct separators_page *s = &ia->separators;
1229 struct dictionary *dict;
1230 unsigned long int generated_name_count = 0;
1234 dict = dict_create ();
1235 name_row = f->variable_names && f->skip_lines ? f->skip_lines : 0;
1236 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1238 char name[VAR_NAME_LEN + 1];
1241 hint = name_row ? ss_xstrdup (col->contents[name_row - 1]) : NULL;
1242 if (!dict_make_unique_var_name (dict, hint, &generated_name_count, name))
1246 col->name = xstrdup (name);
1247 dict_create_var_assert (dict, name, 0);
1249 dict_destroy (dict);
1252 /* Picks the most likely separator and quote characters based on
1255 choose_likely_separators (struct import_assistant *ia)
1257 unsigned long int histogram[UCHAR_MAX + 1] = { 0 };
1260 /* Construct a histogram of all the characters used in the
1262 for (row = 0; row < ia->file.line_cnt; row++)
1264 struct substring line = ds_ss (&ia->file.lines[row]);
1265 size_t length = ss_length (line);
1267 for (i = 0; i < length; i++)
1268 histogram[(unsigned char) line.string[i]]++;
1271 find_commonest_chars (histogram, "\"'", "", &ia->separators.quotes);
1272 find_commonest_chars (histogram, ",;:/|!\t-", ",",
1273 &ia->separators.separators);
1274 ia->separators.escape = true;
1277 /* Chooses the most common character among those in TARGETS,
1278 based on the frequency data in HISTOGRAM, and stores it in
1279 RESULT. If there is a tie for the most common character among
1280 those in TARGETS, the earliest character is chosen. If none
1281 of the TARGETS appear at all, then DEF is used as a
1284 find_commonest_chars (unsigned long int histogram[UCHAR_MAX + 1],
1285 const char *targets, const char *def,
1286 struct string *result)
1288 unsigned char max = 0;
1289 unsigned long int max_count = 0;
1291 for (; *targets != '\0'; targets++)
1293 unsigned char c = *targets;
1294 unsigned long int count = histogram[c];
1295 if (count > max_count)
1304 ds_put_char (result, max);
1307 ds_assign_cstr (result, def);
1310 /* Revises the contents of the fields tree view based on the
1311 currently chosen set of separators. */
1313 revise_fields_preview (struct import_assistant *ia)
1317 push_watch_cursor (ia);
1319 w = GTK_WIDGET (ia->separators.fields_tree_view);
1320 gtk_widget_destroy (w);
1321 get_separators (ia);
1323 choose_column_names (ia);
1324 ia->separators.fields_tree_view = create_data_tree_view (
1326 GTK_CONTAINER (get_widget_assert (ia->asst.xml, "fields-scroller")),
1329 pop_watch_cursor (ia);
1332 /* Sets the widgets to match IA's separators substructure. */
1334 set_separators (struct import_assistant *ia)
1336 struct separators_page *s = &ia->separators;
1338 struct string custom;
1343 ds_init_empty (&custom);
1345 for (i = 0; i < ds_length (&s->separators); i++)
1347 unsigned char c = ds_at (&s->separators, i);
1350 for (j = 0; j < SEPARATOR_CNT; j++)
1352 const struct separator *s = &separators[j];
1360 ds_put_char (&custom, c);
1364 for (i = 0; i < SEPARATOR_CNT; i++)
1366 const struct separator *s = &separators[i];
1367 GtkWidget *button = get_widget_assert (ia->asst.xml, s->name);
1368 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
1369 (seps & (1u << i)) != 0);
1371 any_custom = !ds_is_empty (&custom);
1372 gtk_entry_set_text (GTK_ENTRY (s->custom_entry), ds_cstr (&custom));
1373 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->custom_cb),
1375 gtk_widget_set_sensitive (s->custom_entry, any_custom);
1376 ds_destroy (&custom);
1378 any_quotes = !ds_is_empty (&s->quotes);
1379 gtk_entry_set_text (s->quote_entry,
1380 any_quotes ? ds_cstr (&s->quotes) : "\"");
1381 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->quote_cb),
1383 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->escape_cb),
1385 gtk_widget_set_sensitive (s->quote_combo, any_quotes);
1386 gtk_widget_set_sensitive (s->escape_cb, any_quotes);
1389 /* Sets IA's separators substructure to match the widgets. */
1391 get_separators (struct import_assistant *ia)
1393 struct separators_page *s = &ia->separators;
1396 ds_clear (&s->separators);
1397 for (i = 0; i < SEPARATOR_CNT; i++)
1399 const struct separator *sep = &separators[i];
1400 GtkWidget *button = get_widget_assert (ia->asst.xml, sep->name);
1401 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
1402 ds_put_char (&s->separators, sep->c);
1405 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->custom_cb)))
1406 ds_put_cstr (&s->separators,
1407 gtk_entry_get_text (GTK_ENTRY (s->custom_entry)));
1409 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->quote_cb)))
1411 gchar *text = gtk_combo_box_get_active_text (
1412 GTK_COMBO_BOX (s->quote_combo));
1413 ds_assign_cstr (&s->quotes, text);
1417 ds_clear (&s->quotes);
1418 s->escape = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->escape_cb));
1421 /* Called when the user changes the entry field for custom
1424 on_separators_custom_entry_notify (GObject *gobject UNUSED,
1425 GParamSpec *arg1 UNUSED,
1426 struct import_assistant *ia)
1428 revise_fields_preview (ia);
1431 /* Called when the user toggles the checkbox that enables custom
1434 on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
1435 struct import_assistant *ia)
1437 bool is_active = gtk_toggle_button_get_active (custom_cb);
1438 gtk_widget_set_sensitive (ia->separators.custom_entry, is_active);
1439 revise_fields_preview (ia);
1442 /* Called when the user changes the selection in the combo box
1443 that selects a quote character. */
1445 on_quote_combo_change (GtkComboBox *combo, struct import_assistant *ia)
1447 revise_fields_preview (ia);
1450 /* Called when the user toggles the checkbox that enables
1453 on_quote_cb_toggle (GtkToggleButton *quote_cb, struct import_assistant *ia)
1455 bool is_active = gtk_toggle_button_get_active (quote_cb);
1456 gtk_widget_set_sensitive (ia->separators.quote_combo, is_active);
1457 gtk_widget_set_sensitive (ia->separators.escape_cb, is_active);
1458 revise_fields_preview (ia);
1461 /* Called when the user toggles one of the separators
1464 on_separator_toggle (GtkToggleButton *toggle UNUSED,
1465 struct import_assistant *ia)
1467 revise_fields_preview (ia);
1470 /* Called to render one of the cells in the fields preview tree
1473 render_input_cell (GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1474 GtkTreeModel *model, GtkTreeIter *iter,
1477 struct import_assistant *ia = ia_;
1478 struct substring field;
1482 column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1484 row = text_import_model_iter_to_row (iter) + ia->first_line.skip_lines;
1485 field = ia->separators.columns[column].contents[row];
1486 if (field.string != NULL)
1488 GValue text = {0, };
1489 g_value_init (&text, G_TYPE_STRING);
1490 g_value_take_string (&text, ss_xstrdup (field));
1491 g_object_set_property (G_OBJECT (cell), "text", &text);
1492 g_value_unset (&text);
1493 g_object_set (cell, "background-set", FALSE, (void *) NULL);
1498 "background", "red",
1499 "background-set", TRUE,
1503 /* Called to render a tooltip on one of the cells in the fields
1504 preview tree view. */
1506 on_query_input_tooltip (GtkWidget *widget, gint wx, gint wy,
1507 gboolean keyboard_mode UNUSED,
1508 GtkTooltip *tooltip, struct import_assistant *ia)
1512 if (!get_tooltip_location (widget, wx, wy, ia, &row, &column))
1515 if (ia->separators.columns[column].contents[row].string != NULL)
1518 gtk_tooltip_set_text (tooltip,
1519 _("This input line has too few separators "
1520 "to fill in this field."));
1524 /* The "formats" page of the assistant. */
1526 static void on_variable_change (PsppireDict *dict, int idx,
1527 struct import_assistant *);
1528 static void clear_modified_vars (struct import_assistant *);
1530 /* Initializes IA's formats substructure. */
1532 init_formats_page (struct import_assistant *ia)
1534 GladeXML *xml = ia->asst.xml;
1535 struct formats_page *p = &ia->formats;
1537 p->page = add_page_to_assistant (ia, get_widget_assert (xml, "Formats"),
1538 GTK_ASSISTANT_PAGE_CONFIRM);
1539 p->data_tree_view = GTK_TREE_VIEW (get_widget_assert (xml, "data"));
1540 p->modified_vars = NULL;
1541 p->modified_var_cnt = 0;
1544 /* Frees IA's formats substructure. */
1546 destroy_formats_page (struct import_assistant *ia)
1548 struct formats_page *p = &ia->formats;
1550 if (p->psppire_dict != NULL)
1552 /* This destroys p->dict also. */
1553 g_object_unref (p->psppire_dict);
1555 clear_modified_vars (ia);
1558 /* Called just before the formats page of the assistant is
1561 prepare_formats_page (struct import_assistant *ia)
1563 struct dictionary *dict;
1564 PsppireDict *psppire_dict;
1565 PsppireVarStore *var_store;
1566 GtkBin *vars_scroller;
1567 GtkWidget *old_var_sheet;
1568 PsppireVarSheet *var_sheet;
1569 struct separators_page *s = &ia->separators;
1570 struct formats_page *p = &ia->formats;
1571 struct fmt_guesser *fg;
1572 unsigned long int number = 0;
1575 push_watch_cursor (ia);
1577 dict = dict_create ();
1578 fg = fmt_guesser_create ();
1579 for (column_idx = 0; column_idx < s->column_cnt; column_idx++)
1581 struct variable *modified_var;
1582 char name[VAR_NAME_LEN + 1];
1584 modified_var = (column_idx < p->modified_var_cnt
1585 ? p->modified_vars[column_idx] : NULL);
1586 if (modified_var == NULL)
1588 struct column *column = &s->columns[column_idx];
1589 struct variable *var;
1590 struct fmt_spec format;
1593 /* Choose variable name. */
1594 if (!dict_make_unique_var_name (dict, column->name, &number, name))
1597 /* Choose variable format. */
1598 fmt_guesser_clear (fg);
1599 for (row = ia->first_line.skip_lines; row < ia->file.line_cnt; row++)
1600 fmt_guesser_add (fg, column->contents[row]);
1601 fmt_guesser_guess (fg, &format);
1602 fmt_fix_input (&format);
1604 /* Create variable. */
1605 var = dict_create_var_assert (dict, name, fmt_var_width (&format));
1606 var_set_both_formats (var, &format);
1610 if (!dict_make_unique_var_name (dict, var_get_name (modified_var),
1613 dict_clone_var_assert (dict, modified_var, name);
1616 fmt_guesser_destroy (fg);
1618 psppire_dict = psppire_dict_new_from_dict (dict);
1619 g_signal_connect (psppire_dict, "variable_changed",
1620 G_CALLBACK (on_variable_change), ia);
1621 ia->formats.dict = dict;
1622 ia->formats.psppire_dict = psppire_dict;
1624 /* XXX: PsppireVarStore doesn't hold a reference to
1625 psppire_dict for now, but it should. After it does, we
1626 should g_object_ref the psppire_dict here, since we also
1627 hold a reference via ia->formats.dict. */
1628 var_store = psppire_var_store_new (psppire_dict);
1629 g_object_set (var_store,
1630 "format-type", PSPPIRE_VAR_STORE_INPUT_FORMATS,
1632 var_sheet = PSPPIRE_VAR_SHEET (psppire_var_sheet_new ());
1633 g_object_set (var_sheet,
1635 "may-create-vars", FALSE,
1638 vars_scroller = GTK_BIN (get_widget_assert (ia->asst.xml, "vars-scroller"));
1639 old_var_sheet = gtk_bin_get_child (vars_scroller);
1640 if (old_var_sheet != NULL)
1641 gtk_widget_destroy (old_var_sheet);
1642 gtk_container_add (GTK_CONTAINER (vars_scroller), GTK_WIDGET (var_sheet));
1643 gtk_widget_show (GTK_WIDGET (var_sheet));
1645 gtk_widget_destroy (GTK_WIDGET (ia->formats.data_tree_view));
1646 ia->formats.data_tree_view = create_data_tree_view (
1648 GTK_CONTAINER (get_widget_assert (ia->asst.xml, "data-scroller")),
1651 pop_watch_cursor (ia);
1654 /* Clears the set of user-modified variables from IA's formats
1655 substructure. This discards user modifications to variable
1656 formats, thereby causing formats to revert to their
1659 clear_modified_vars (struct import_assistant *ia)
1661 struct formats_page *p = &ia->formats;
1664 for (i = 0; i < p->modified_var_cnt; i++)
1665 var_destroy (p->modified_vars[i]);
1666 free (p->modified_vars);
1667 p->modified_vars = NULL;
1668 p->modified_var_cnt = 0;
1671 /* Resets the formats page to its defaults, discarding user
1674 reset_formats_page (struct import_assistant *ia)
1676 clear_modified_vars (ia);
1677 prepare_formats_page (ia);
1680 /* Called when the user changes one of the variables in the
1683 on_variable_change (PsppireDict *dict, int dict_idx,
1684 struct import_assistant *ia)
1686 struct formats_page *p = &ia->formats;
1687 GtkTreeView *tv = ia->formats.data_tree_view;
1688 gint column_idx = dict_idx + 1;
1690 push_watch_cursor (ia);
1692 /* Remove previous column and replace with new column. */
1693 gtk_tree_view_remove_column (tv, gtk_tree_view_get_column (tv, column_idx));
1694 gtk_tree_view_insert_column (tv, make_data_column (ia, tv, false, dict_idx),
1697 /* Save a copy of the modified variable in modified_vars, so
1698 that its attributes will be preserved if we back up to the
1699 previous page with the Prev button and then come back
1701 if (dict_idx >= p->modified_var_cnt)
1704 p->modified_vars = xnrealloc (p->modified_vars, dict_idx + 1,
1705 sizeof *p->modified_vars);
1706 for (i = 0; i <= dict_idx; i++)
1707 p->modified_vars[i] = NULL;
1708 p->modified_var_cnt = dict_idx + 1;
1710 if (p->modified_vars[dict_idx])
1711 var_destroy (p->modified_vars[dict_idx]);
1712 p->modified_vars[dict_idx]
1713 = var_clone (psppire_dict_get_variable (dict, dict_idx));
1715 pop_watch_cursor (ia);
1718 /* Parses the contents of the field at (ROW,COLUMN) according to
1719 its variable format. If OUTPUTP is non-null, then *OUTPUTP
1720 receives the formatted output for that field (which must be
1721 freed with free). If TOOLTIPP is non-null, then *TOOLTIPP
1722 receives a message suitable for use in a tooltip, if one is
1723 needed, or a null pointer otherwise. Returns true if a
1724 tooltip message is needed, otherwise false. */
1726 parse_field (struct import_assistant *ia,
1727 size_t row, size_t column,
1728 char **outputp, char **tooltipp)
1730 struct substring field;
1732 struct variable *var;
1733 const struct fmt_spec *in;
1734 struct fmt_spec out;
1738 field = ia->separators.columns[column].contents[row];
1739 var = dict_get_var (ia->formats.dict, column);
1740 val = value_create (var_get_width (var));
1741 in = var_get_print_format (var);
1742 out = fmt_for_output_from_input (in);
1744 if (field.string != NULL)
1747 if (!data_in (field, LEGACY_NATIVE, in->type, 0, 0, 0,
1748 val, var_get_width (var)))
1750 char fmt_string[FMT_STRING_LEN_MAX + 1];
1751 fmt_to_string (in, fmt_string);
1752 tooltip = xasprintf (_("Field content \"%.*s\" cannot be parsed in "
1754 (int) field.length, field.string,
1761 tooltip = xstrdup (_("This input line has too few separators "
1762 "to fill in this field."));
1763 value_set_missing (val, var_get_width (var));
1765 if (outputp != NULL)
1767 char *output = xmalloc (out.w + 1);
1768 data_out (val, &out, output);
1769 output[out.w] = '\0';
1774 ok = tooltip == NULL;
1775 if (tooltipp != NULL)
1776 *tooltipp = tooltip;
1782 /* Called to render one of the cells in the data preview tree
1785 render_output_cell (GtkTreeViewColumn *tree_column,
1786 GtkCellRenderer *cell,
1787 GtkTreeModel *model,
1791 struct import_assistant *ia = ia_;
1793 GValue gvalue = { 0, };
1796 ok = parse_field (ia,
1797 (text_import_model_iter_to_row (iter)
1798 + ia->first_line.skip_lines),
1799 GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1803 g_value_init (&gvalue, G_TYPE_STRING);
1804 g_value_take_string (&gvalue, output);
1805 g_object_set_property (G_OBJECT (cell), "text", &gvalue);
1806 g_value_unset (&gvalue);
1809 g_object_set (cell, "background-set", FALSE, (void *) NULL);
1812 "background", "red",
1813 "background-set", TRUE,
1817 /* Called to render a tooltip for one of the cells in the data
1818 preview tree view. */
1820 on_query_output_tooltip (GtkWidget *widget, gint wx, gint wy,
1821 gboolean keyboard_mode UNUSED,
1822 GtkTooltip *tooltip, struct import_assistant *ia)
1827 if (!get_tooltip_location (widget, wx, wy, ia, &row, &column))
1830 if (parse_field (ia, row, column, NULL, &text))
1833 gtk_tooltip_set_text (tooltip, text);
1838 /* Utility functions used by multiple pages of the assistant. */
1841 get_tooltip_location (GtkWidget *widget, gint wx, gint wy,
1842 const struct import_assistant *ia,
1843 size_t *row, size_t *column)
1845 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
1849 GtkTreeViewColumn *tree_column;
1850 GtkTreeModel *tree_model;
1853 /* Check that WIDGET is really visible on the screen before we
1854 do anything else. This is a bug fix for a sticky situation:
1855 when text_data_import_assistant() returns, it frees the data
1856 necessary to compose the tool tip message, but there may be
1857 a tool tip under preparation at that point (even if there is
1858 no visible tool tip) that will call back into us a little
1859 bit later. Perhaps the correct solution to this problem is
1860 to make the data related to the tool tips part of a GObject
1861 that only gets destroyed when all references are released,
1862 but this solution appears to be effective too. */
1863 if (!GTK_WIDGET_MAPPED (widget))
1866 gtk_tree_view_convert_widget_to_bin_window_coords (tree_view,
1868 if (!gtk_tree_view_get_path_at_pos (tree_view, bx, by,
1869 &path, &tree_column, NULL, NULL))
1872 *column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1875 tree_model = gtk_tree_view_get_model (tree_view);
1876 ok = gtk_tree_model_get_iter (tree_model, &iter, path);
1877 gtk_tree_path_free (path);
1881 *row = text_import_model_iter_to_row (&iter) + ia->first_line.skip_lines;
1886 make_tree_view (const struct import_assistant *ia,
1888 GtkTreeView **tree_view)
1890 GtkTreeModel *model;
1892 *tree_view = GTK_TREE_VIEW (gtk_tree_view_new ());
1893 model = GTK_TREE_MODEL (text_import_model_new (
1894 ia->file.lines + first_line,
1895 ia->file.line_cnt - first_line, first_line));
1896 gtk_tree_view_set_model (*tree_view, model);
1898 add_line_number_column (ia, *tree_view);
1902 add_line_number_column (const struct import_assistant *ia,
1903 GtkTreeView *treeview)
1905 GtkTreeViewColumn *column;
1907 column = gtk_tree_view_column_new_with_attributes (
1908 "Line", ia->asst.prop_renderer,
1909 "text", TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER,
1911 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
1912 gtk_tree_view_column_set_fixed_width (
1913 column, get_monospace_width (treeview, ia->asst.prop_renderer, 5));
1914 gtk_tree_view_append_column (treeview, column);
1918 get_monospace_width (GtkTreeView *treeview, GtkCellRenderer *renderer,
1925 ds_put_char_multiple (&s, '0', char_cnt);
1926 ds_put_char (&s, ' ');
1927 width = get_string_width (treeview, renderer, ds_cstr (&s));
1934 get_string_width (GtkTreeView *treeview, GtkCellRenderer *renderer,
1938 g_object_set (G_OBJECT (renderer), "text", string, (void *) NULL);
1939 gtk_cell_renderer_get_size (renderer, GTK_WIDGET (treeview),
1940 NULL, NULL, NULL, &width, NULL);
1944 static GtkTreeViewColumn *
1945 make_data_column (struct import_assistant *ia, GtkTreeView *tree_view,
1946 bool input, gint dict_idx)
1948 struct variable *var = NULL;
1949 struct column *column = NULL;
1950 char name[(VAR_NAME_LEN * 2) + 1];
1952 gint content_width, header_width;
1953 GtkTreeViewColumn *tree_column;
1956 column = &ia->separators.columns[dict_idx];
1958 var = dict_get_var (ia->formats.dict, dict_idx);
1960 escape_underscores (input ? column->name : var_get_name (var), name);
1961 char_cnt = input ? column->width : var_get_print_format (var)->w;
1962 content_width = get_monospace_width (tree_view, ia->asst.fixed_renderer,
1964 header_width = get_string_width (tree_view, ia->asst.prop_renderer,
1967 tree_column = gtk_tree_view_column_new ();
1968 g_object_set_data (G_OBJECT (tree_column), "column-number",
1969 GINT_TO_POINTER (dict_idx));
1970 gtk_tree_view_column_set_title (tree_column, name);
1971 gtk_tree_view_column_pack_start (tree_column, ia->asst.fixed_renderer,
1973 gtk_tree_view_column_set_cell_data_func (
1974 tree_column, ia->asst.fixed_renderer,
1975 input ? render_input_cell : render_output_cell, ia, NULL);
1976 gtk_tree_view_column_set_sizing (tree_column, GTK_TREE_VIEW_COLUMN_FIXED);
1977 gtk_tree_view_column_set_fixed_width (tree_column, MAX (content_width,
1983 static GtkTreeView *
1984 create_data_tree_view (bool input, GtkContainer *parent,
1985 struct import_assistant *ia)
1987 GtkTreeView *tree_view;
1990 make_tree_view (ia, ia->first_line.skip_lines, &tree_view);
1991 gtk_tree_selection_set_mode (gtk_tree_view_get_selection (tree_view),
1992 GTK_SELECTION_NONE);
1994 for (i = 0; i < ia->separators.column_cnt; i++)
1995 gtk_tree_view_append_column (tree_view,
1996 make_data_column (ia, tree_view, input, i));
1998 g_object_set (G_OBJECT (tree_view), "has-tooltip", TRUE, (void *) NULL);
1999 g_signal_connect (tree_view, "query-tooltip",
2000 G_CALLBACK (input ? on_query_input_tooltip
2001 : on_query_output_tooltip), ia);
2002 gtk_tree_view_set_fixed_height_mode (tree_view, true);
2004 gtk_container_add (parent, GTK_WIDGET (tree_view));
2005 gtk_widget_show (GTK_WIDGET (tree_view));
2011 escape_underscores (const char *in, char *out)
2013 for (; *in != '\0'; in++)
2022 /* TextImportModel, a GtkTreeModel implementation used by some
2023 pages of the assistant. */
2025 #define G_TYPE_TEXT_IMPORT_MODEL (text_import_model_get_type ())
2026 #define TEXT_IMPORT_MODEL(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), G_TYPE_TEXT_IMPORT_MODEL, TextImportModel))
2027 #define TEXT_IMPORT_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), G_TYPE_TEXT_IMPORT_MODEL, TextImportModelClass))
2028 #define IS_TEXT_IMPORT_MODEL(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), G_TYPE_TEXT_IMPORT_MODEL))
2029 #define IS_TEXT_IMPORT_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), G_TYPE_TEXT_IMPORT_MODEL))
2030 #define TEXT_IMPORT_MODEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), G_TYPE_TEXT_IMPORT_MODEL, TextImportModelClass))
2032 /* Random number used in 'stamp' member of GtkTreeIter. */
2033 #define TREE_MODEL_STAMP 0x7efd67d3
2035 struct TextImportModel
2038 struct string *lines;
2043 struct TextImportModelClass
2045 GObjectClass parent_class;
2048 GType text_import_model_get_type (void);
2049 static void text_import_model_tree_model_init (gpointer iface, gpointer data);
2052 text_import_model_get_type (void)
2054 static GType object_type = 0;
2058 static const GTypeInfo object_info = {
2059 sizeof (TextImportModelClass),
2060 (GBaseInitFunc) NULL,
2061 (GBaseFinalizeFunc) NULL,
2062 NULL, /* class_init */
2063 NULL, /* class_finalize */
2064 NULL, /* class_data */
2065 sizeof (TextImportModel),
2066 0, /* n_preallocs */
2067 NULL, /* instance_init */
2070 static const GInterfaceInfo tree_model_info = {
2071 text_import_model_tree_model_init,
2076 object_type = g_type_register_static (G_TYPE_OBJECT,
2080 g_type_add_interface_static (object_type, GTK_TYPE_TREE_MODEL,
2090 /* Creates and returns a new TextImportModel that contains the
2091 LINE_CNT lines in LINES. The lines before FIRST_LINE in LINES
2092 are not part of the model, but they are included in the line
2093 numbers in the TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER column.
2095 The caller retains responsibility for freeing LINES and must
2096 ensure that its lifetime and that of the strings that it
2097 contains exceeds that of the TextImportModel. */
2099 text_import_model_new (struct string *lines, size_t line_cnt,
2102 TextImportModel *new_text_import_model
2103 = g_object_new (G_TYPE_TEXT_IMPORT_MODEL, NULL);
2104 new_text_import_model->lines = lines;
2105 new_text_import_model->line_cnt = line_cnt;
2106 new_text_import_model->first_line = first_line;
2107 return new_text_import_model;
2112 tree_model_iter_has_child (GtkTreeModel *tree_model,
2119 tree_model_iter_parent (GtkTreeModel *tree_model,
2126 static GtkTreeModelFlags
2127 tree_model_get_flags (GtkTreeModel *model)
2129 g_return_val_if_fail (IS_TEXT_IMPORT_MODEL (model), (GtkTreeModelFlags) 0);
2131 return GTK_TREE_MODEL_LIST_ONLY | GTK_TREE_MODEL_ITERS_PERSIST;
2136 tree_model_n_columns (GtkTreeModel *model)
2142 tree_model_column_type (GtkTreeModel *model, gint index)
2144 return (index == TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER ? G_TYPE_INT
2145 : index == TEXT_IMPORT_MODEL_COLUMN_LINE ? G_TYPE_STRING
2150 init_iter (TextImportModel *list, gint idx, GtkTreeIter *iter)
2152 if (idx < 0 || idx >= list->line_cnt)
2155 iter->user_data = GINT_TO_POINTER (-1);
2160 iter->stamp = TREE_MODEL_STAMP;
2161 iter->user_data = GINT_TO_POINTER (idx);
2167 tree_model_get_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreePath *path)
2169 gint *indices, depth;
2171 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2173 g_return_val_if_fail (path, FALSE);
2175 indices = gtk_tree_path_get_indices (path);
2176 depth = gtk_tree_path_get_depth (path);
2178 g_return_val_if_fail (depth == 1, FALSE);
2180 return init_iter (list, indices[0], iter);
2185 tree_model_iter_next (GtkTreeModel *model, GtkTreeIter *iter)
2187 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2190 assert (iter->stamp == TREE_MODEL_STAMP);
2192 idx = GPOINTER_TO_INT (iter->user_data);
2193 return init_iter (list, idx == -1 ? -1 : idx + 1, iter);
2196 static GtkTreePath *
2197 tree_model_get_path (GtkTreeModel *model, GtkTreeIter *iter)
2201 g_return_val_if_fail (iter->stamp == TREE_MODEL_STAMP, FALSE);
2203 path = gtk_tree_path_new ();
2204 gtk_tree_path_append_index (path, GPOINTER_TO_INT (iter->user_data));
2210 tree_model_get_value (GtkTreeModel *model, GtkTreeIter *iter,
2211 gint column, GValue *value)
2213 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2216 g_return_if_fail (iter->stamp == TREE_MODEL_STAMP);
2218 idx = GPOINTER_TO_INT (iter->user_data);
2219 assert (idx >= 0 && idx < list->line_cnt);
2223 g_value_init (value, G_TYPE_INT);
2224 g_value_set_int (value, idx + list->first_line + 1);
2228 g_value_init (value, G_TYPE_STRING);
2229 g_value_set_static_string (value, ds_cstr (&list->lines[idx]));
2234 tree_model_iter_children (GtkTreeModel *tree_model,
2236 GtkTreeIter *parent)
2242 tree_model_n_children (GtkTreeModel *model, GtkTreeIter *iter)
2244 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2246 return iter == NULL ? list->line_cnt : 0;
2250 tree_model_nth_child (GtkTreeModel *model, GtkTreeIter *iter,
2251 GtkTreeIter *parent, gint n)
2253 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2254 g_return_val_if_fail (IS_TEXT_IMPORT_MODEL (model), FALSE);
2258 return init_iter (list, n, iter);
2262 text_import_model_tree_model_init (gpointer iface_, gpointer data UNUSED)
2264 GtkTreeModelIface *iface = (GtkTreeModelIface *) iface_;
2266 iface->get_flags = tree_model_get_flags;
2267 iface->get_n_columns = tree_model_n_columns;
2268 iface->get_column_type = tree_model_column_type;
2269 iface->get_iter = tree_model_get_iter;
2270 iface->iter_next = tree_model_iter_next;
2271 iface->get_path = tree_model_get_path;
2272 iface->get_value = tree_model_get_value;
2274 iface->iter_children = tree_model_iter_children;
2275 iface->iter_has_child = tree_model_iter_has_child;
2276 iface->iter_n_children = tree_model_n_children;
2277 iface->iter_nth_child = tree_model_nth_child;
2278 iface->iter_parent = tree_model_iter_parent;
2282 text_import_model_iter_to_row (const GtkTreeIter *iter)
2284 assert (iter->stamp == TREE_MODEL_STAMP);
2285 return GPOINTER_TO_INT (iter->user_data);
2288 /* Increments the "watch cursor" level, setting the cursor for
2289 the assistant window to a watch face to indicate to the user
2290 that the ongoing operation may take some time. */
2292 push_watch_cursor (struct import_assistant *ia)
2294 if (++ia->asst.watch_cursor == 1)
2296 GtkWidget *widget = GTK_WIDGET (ia->asst.assistant);
2297 GdkDisplay *display = gtk_widget_get_display (widget);
2298 GdkCursor *cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
2299 gdk_window_set_cursor (widget->window, cursor);
2300 gdk_cursor_unref (cursor);
2301 gdk_display_flush (display);
2305 /* Decrements the "watch cursor" level. If the level reaches
2306 zero, the cursor is reset to its default shape. */
2308 pop_watch_cursor (struct import_assistant *ia)
2310 if (--ia->asst.watch_cursor == 0)
2312 GtkWidget *widget = GTK_WIDGET (ia->asst.assistant);
2313 gdk_window_set_cursor (widget->window, NULL);