1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2008, 2009, 2010, 2011 Free Software Foundation
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19 #include "ui/gui/text-data-import-dialog.h"
22 #include <gtk-contrib/psppire-sheet.h>
28 #include "data/data-in.h"
29 #include "data/data-out.h"
30 #include "data/format-guesser.h"
31 #include "data/value-labels.h"
32 #include "language/data-io/data-parser.h"
33 #include "language/lexer/lexer.h"
34 #include "libpspp/assertion.h"
35 #include "libpspp/message.h"
36 #include "ui/gui/checkbox-treeview.h"
37 #include "ui/gui/descriptives-dialog.h"
38 #include "ui/gui/dialog-common.h"
39 #include "ui/gui/executor.h"
40 #include "ui/gui/helper.h"
41 #include "ui/gui/psppire-data-window.h"
42 #include "ui/gui/psppire-dialog.h"
43 #include "ui/gui/psppire-var-sheet.h"
44 #include "ui/gui/psppire-var-store.h"
45 #include "ui/gui/widget-io.h"
46 #include "ui/syntax-gen.h"
49 #include "gl/xalloc.h"
52 #define _(msgid) gettext (msgid)
53 #define N_(msgid) msgid
56 /* TextImportModel, a GtkTreeModel used by the text data import
60 TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER, /* 1-based line number in file */
61 TEXT_IMPORT_MODEL_COLUMN_LINE, /* The line from the file. */
63 typedef struct TextImportModel TextImportModel;
64 typedef struct TextImportModelClass TextImportModelClass;
66 TextImportModel *text_import_model_new (struct string *lines, size_t line_cnt,
68 gint text_import_model_iter_to_row (const GtkTreeIter *);
70 struct import_assistant;
72 /* The file to be imported. */
75 char *file_name; /* File name. */
76 unsigned long int total_lines; /* Number of lines in file. */
77 bool total_is_exact; /* Is total_lines exact (or an estimate)? */
79 /* The first several lines of the file. */
83 static bool init_file (struct import_assistant *, GtkWindow *parent);
84 static void destroy_file (struct import_assistant *);
86 /* The main body of the GTK+ assistant and related data. */
90 GtkAssistant *assistant;
92 GtkWidget *paste_button;
93 GtkWidget *reset_button;
97 GtkCellRenderer *prop_renderer;
98 GtkCellRenderer *fixed_renderer;
100 static void init_assistant (struct import_assistant *, GtkWindow *);
101 static void destroy_assistant (struct import_assistant *);
102 static GtkWidget *add_page_to_assistant (struct import_assistant *,
104 GtkAssistantPageType);
106 /* The introduction page of the assistant. */
110 GtkWidget *all_cases_button;
111 GtkWidget *n_cases_button;
112 GtkWidget *n_cases_spin;
113 GtkWidget *percent_button;
114 GtkWidget *percent_spin;
116 static void init_intro_page (struct import_assistant *);
117 static void reset_intro_page (struct import_assistant *);
119 /* Page where the user chooses the first line of data. */
120 struct first_line_page
122 int skip_lines; /* Number of initial lines to skip? */
123 bool variable_names; /* Variable names above first line of data? */
126 GtkTreeView *tree_view;
127 GtkWidget *variable_names_cb;
129 static void init_first_line_page (struct import_assistant *);
130 static void reset_first_line_page (struct import_assistant *);
132 /* Page where the user chooses field separators. */
133 struct separators_page
135 /* How to break lines into columns. */
136 struct string separators; /* Field separators. */
137 struct string quotes; /* Quote characters. */
138 bool escape; /* Doubled quotes yield a quote mark? */
140 /* The columns produced thereby. */
141 struct column *columns; /* Information about each column. */
142 size_t column_cnt; /* Number of columns. */
145 GtkWidget *custom_cb;
146 GtkWidget *custom_entry;
148 GtkWidget *quote_combo;
149 GtkEntry *quote_entry;
150 GtkWidget *escape_cb;
151 GtkTreeView *fields_tree_view;
153 /* The columns that the separators divide the data into. */
156 /* Variable name for this column. This is the variable name
157 used on the separators page; it can be overridden by the
158 user on the formats page. */
161 /* Maximum length of any row in this column. */
164 /* Contents of this column: contents[row] is the contents for
167 A null substring indicates a missing column for that row
168 (because the line contains an insufficient number of
171 contents[] elements may be substrings of the lines[]
172 strings that represent the whole lines of the file, to
173 save memory. Other elements are dynamically allocated
174 with ss_alloc_substring. */
175 struct substring *contents;
177 static void init_separators_page (struct import_assistant *);
178 static void destroy_separators_page (struct import_assistant *);
179 static void prepare_separators_page (struct import_assistant *);
180 static void reset_separators_page (struct import_assistant *);
182 /* Page where the user verifies and adjusts input formats. */
185 struct dictionary *dict;
188 GtkTreeView *data_tree_view;
189 PsppireDict *psppire_dict;
190 struct variable **modified_vars;
191 size_t modified_var_cnt;
193 static void init_formats_page (struct import_assistant *);
194 static void destroy_formats_page (struct import_assistant *);
195 static void prepare_formats_page (struct import_assistant *);
196 static void reset_formats_page (struct import_assistant *);
198 struct import_assistant
201 struct assistant asst;
202 struct intro_page intro;
203 struct first_line_page first_line;
204 struct separators_page separators;
205 struct formats_page formats;
208 static void apply_dict (const struct dictionary *, struct string *);
209 static char *generate_syntax (const struct import_assistant *);
211 static gboolean get_tooltip_location (GtkWidget *widget, gint wx, gint wy,
212 const struct import_assistant *,
213 size_t *row, size_t *column);
214 static void make_tree_view (const struct import_assistant *ia,
216 GtkTreeView **tree_view);
217 static void add_line_number_column (const struct import_assistant *,
219 static gint get_monospace_width (GtkTreeView *, GtkCellRenderer *,
221 static gint get_string_width (GtkTreeView *, GtkCellRenderer *,
223 static GtkTreeViewColumn *make_data_column (struct import_assistant *,
224 GtkTreeView *, bool input,
226 static GtkTreeView *create_data_tree_view (bool input, GtkContainer *parent,
227 struct import_assistant *);
228 static char *escape_underscores (const char *in);
229 static void push_watch_cursor (struct import_assistant *);
230 static void pop_watch_cursor (struct import_assistant *);
232 /* Pops up the Text Data Import assistant. */
234 text_data_import_assistant (GtkWindow *parent_window)
236 struct import_assistant *ia;
238 ia = xzalloc (sizeof *ia);
239 if (!init_file (ia, parent_window))
245 init_assistant (ia, parent_window);
246 init_intro_page (ia);
247 init_first_line_page (ia);
248 init_separators_page (ia);
249 init_formats_page (ia);
251 gtk_widget_show_all (GTK_WIDGET (ia->asst.assistant));
253 ia->asst.main_loop = g_main_loop_new (NULL, false);
254 g_main_loop_run (ia->asst.main_loop);
255 g_main_loop_unref (ia->asst.main_loop);
257 switch (ia->asst.response)
259 case GTK_RESPONSE_APPLY:
260 free (execute_syntax_string (generate_syntax (ia)));
262 case PSPPIRE_RESPONSE_PASTE:
263 free (paste_syntax_to_window (generate_syntax (ia)));
269 destroy_formats_page (ia);
270 destroy_separators_page (ia);
271 destroy_assistant (ia);
276 /* Emits PSPP syntax to S that applies the dictionary attributes
277 (such as missing values and value labels) of the variables in
280 apply_dict (const struct dictionary *dict, struct string *s)
282 size_t var_cnt = dict_get_var_cnt (dict);
285 for (i = 0; i < var_cnt; i++)
287 struct variable *var = dict_get_var (dict, i);
288 const char *name = var_get_name (var);
289 enum val_type type = var_get_type (var);
290 int width = var_get_width (var);
291 enum measure measure = var_get_measure (var);
292 enum alignment alignment = var_get_alignment (var);
293 const struct fmt_spec *format = var_get_print_format (var);
295 if (var_has_missing_values (var))
297 const struct missing_values *mv = var_get_missing_values (var);
300 syntax_gen_pspp (s, "MISSING VALUES %ss (", name);
301 for (j = 0; j < mv_n_values (mv); j++)
304 ds_put_cstr (s, ", ");
305 syntax_gen_value (s, mv_get_value (mv, j), width, format);
308 if (mv_has_range (mv))
311 if (mv_has_value (mv))
312 ds_put_cstr (s, ", ");
313 mv_get_range (mv, &low, &high);
314 syntax_gen_num_range (s, low, high, format);
316 ds_put_cstr (s, ").\n");
318 if (var_has_value_labels (var))
320 const struct val_labs *vls = var_get_value_labels (var);
321 const struct val_lab **labels = val_labs_sorted (vls);
322 size_t n_labels = val_labs_count (vls);
325 syntax_gen_pspp (s, "VALUE LABELS %ss", name);
326 for (i = 0; i < n_labels; i++)
328 const struct val_lab *vl = labels[i];
329 ds_put_cstr (s, "\n ");
330 syntax_gen_value (s, &vl->value, width, format);
331 ds_put_byte (s, ' ');
332 syntax_gen_string (s, ss_cstr (val_lab_get_label (vl)));
335 ds_put_cstr (s, ".\n");
337 if (var_has_label (var))
338 syntax_gen_pspp (s, "VARIABLE LABELS %ss %sq.\n",
339 name, var_get_label (var));
340 if (measure != var_default_measure (type))
341 syntax_gen_pspp (s, "VARIABLE LEVEL %ss (%ss).\n",
343 (measure == MEASURE_NOMINAL ? "NOMINAL"
344 : measure == MEASURE_ORDINAL ? "ORDINAL"
346 if (alignment != var_default_alignment (type))
347 syntax_gen_pspp (s, "VARIABLE ALIGNMENT %ss (%ss).\n",
349 (alignment == ALIGN_LEFT ? "LEFT"
350 : alignment == ALIGN_CENTRE ? "CENTER"
352 if (var_get_display_width (var) != var_default_display_width (width))
353 syntax_gen_pspp (s, "VARIABLE WIDTH %ss (%d).\n",
354 name, var_get_display_width (var));
358 /* Generates and returns PSPP syntax to execute the import
359 operation described by IA. The caller must free the syntax
362 generate_syntax (const struct import_assistant *ia)
364 struct string s = DS_EMPTY_INITIALIZER;
373 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (
374 ia->intro.n_cases_button)))
375 ds_put_format (&s, " /IMPORTCASES=FIRST %d\n",
376 gtk_spin_button_get_value_as_int (
377 GTK_SPIN_BUTTON (ia->intro.n_cases_spin)));
378 else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (
379 ia->intro.percent_button)))
380 ds_put_format (&s, " /IMPORTCASES=PERCENT %d\n",
381 gtk_spin_button_get_value_as_int (
382 GTK_SPIN_BUTTON (ia->intro.percent_spin)));
384 ds_put_cstr (&s, " /IMPORTCASES=ALL\n");
386 " /ARRANGEMENT=DELIMITED\n"
388 if (ia->first_line.skip_lines > 0)
389 ds_put_format (&s, " /FIRSTCASE=%d\n", ia->first_line.skip_lines + 1);
390 ds_put_cstr (&s, " /DELIMITERS=\"");
391 if (ds_find_byte (&ia->separators.separators, '\t') != SIZE_MAX)
392 ds_put_cstr (&s, "\\t");
393 if (ds_find_byte (&ia->separators.separators, '\\') != SIZE_MAX)
394 ds_put_cstr (&s, "\\\\");
395 for (i = 0; i < ds_length (&ia->separators.separators); i++)
397 char c = ds_at (&ia->separators.separators, i);
399 ds_put_cstr (&s, "\"\"");
400 else if (c != '\t' && c != '\\')
403 ds_put_cstr (&s, "\"\n");
404 if (!ds_is_empty (&ia->separators.quotes))
405 syntax_gen_pspp (&s, " /QUALIFIER=%sq\n", ds_cstr (&ia->separators.quotes));
406 if (!ds_is_empty (&ia->separators.quotes) && ia->separators.escape)
407 ds_put_cstr (&s, " /ESCAPE\n");
408 ds_put_cstr (&s, " /VARIABLES=\n");
410 var_cnt = dict_get_var_cnt (ia->formats.dict);
411 for (i = 0; i < var_cnt; i++)
413 struct variable *var = dict_get_var (ia->formats.dict, i);
414 char format_string[FMT_STRING_LEN_MAX + 1];
415 fmt_to_string (var_get_print_format (var), format_string);
416 ds_put_format (&s, " %s %s%s\n",
417 var_get_name (var), format_string,
418 i == var_cnt - 1 ? "." : "");
421 apply_dict (ia->formats.dict, &s);
426 /* Choosing a file and reading it. */
428 static char *choose_file (GtkWindow *parent_window);
430 /* Obtains the file to import from the user and initializes IA's
431 file substructure. PARENT_WINDOW must be the window to use
432 as the file chooser window's parent.
434 Returns true if successful, false if the file name could not
435 be obtained or the file could not be read. */
437 init_file (struct import_assistant *ia, GtkWindow *parent_window)
439 struct file *file = &ia->file;
440 enum { MAX_PREVIEW_LINES = 1000 }; /* Max number of lines to read. */
441 enum { MAX_LINE_LEN = 16384 }; /* Max length of an acceptable line. */
444 file->file_name = choose_file (parent_window);
445 if (file->file_name == NULL)
448 stream = fopen (file->file_name, "r");
451 msg (ME, _("Could not open `%s': %s"),
452 file->file_name, strerror (errno));
456 file->lines = xnmalloc (MAX_PREVIEW_LINES, sizeof *file->lines);
457 for (; file->line_cnt < MAX_PREVIEW_LINES; file->line_cnt++)
459 struct string *line = &file->lines[file->line_cnt];
461 ds_init_empty (line);
462 if (!ds_read_line (line, stream, MAX_LINE_LEN))
466 else if (ferror (stream))
467 msg (ME, _("Error reading `%s': %s"),
468 file->file_name, strerror (errno));
470 msg (ME, _("Failed to read `%s', because it contains a line "
471 "over %d bytes long and therefore appears not to be "
473 file->file_name, MAX_LINE_LEN);
478 ds_chomp (line, '\n');
479 ds_chomp (line, '\r');
482 if (file->line_cnt == 0)
484 msg (ME, _("`%s' is empty."), file->file_name);
490 /* Estimate the number of lines in the file. */
491 if (file->line_cnt < MAX_PREVIEW_LINES)
492 file->total_lines = file->line_cnt;
496 off_t position = ftello (stream);
497 if (fstat (fileno (stream), &s) == 0 && position > 0)
498 file->total_lines = (double) file->line_cnt / position * s.st_size;
500 file->total_lines = 0;
506 /* Frees IA's file substructure. */
508 destroy_file (struct import_assistant *ia)
510 struct file *f = &ia->file;
513 for (i = 0; i < f->line_cnt; i++)
514 ds_destroy (&f->lines[i]);
516 g_free (f->file_name);
519 /* Obtains the file to read from the user and returns the name of
520 the file as a string that must be freed with g_free if
521 successful, otherwise a null pointer. PARENT_WINDOW must be
522 the window to use as the file chooser window's parent. */
524 choose_file (GtkWindow *parent_window)
529 dialog = gtk_file_chooser_dialog_new (_("Import Delimited Text Data"),
531 GTK_FILE_CHOOSER_ACTION_OPEN,
532 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
533 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
536 switch (gtk_dialog_run (GTK_DIALOG (dialog)))
538 case GTK_RESPONSE_ACCEPT:
539 file_name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
545 gtk_widget_destroy (dialog);
552 static void close_assistant (struct import_assistant *, int response);
553 static void on_prepare (GtkAssistant *assistant, GtkWidget *page,
554 struct import_assistant *);
555 static void on_cancel (GtkAssistant *assistant, struct import_assistant *);
556 static void on_close (GtkAssistant *assistant, struct import_assistant *);
557 static void on_paste (GtkButton *button, struct import_assistant *);
558 static void on_reset (GtkButton *button, struct import_assistant *);
559 static void close_assistant (struct import_assistant *, int response);
561 /* Initializes IA's asst substructure. PARENT_WINDOW must be the
562 window to use as the assistant window's parent. */
564 init_assistant (struct import_assistant *ia, GtkWindow *parent_window)
566 struct assistant *a = &ia->asst;
568 a->builder = builder_new ("text-data-import.ui");
569 a->assistant = GTK_ASSISTANT (gtk_assistant_new ());
570 g_signal_connect (a->assistant, "prepare", G_CALLBACK (on_prepare), ia);
571 g_signal_connect (a->assistant, "cancel", G_CALLBACK (on_cancel), ia);
572 g_signal_connect (a->assistant, "close", G_CALLBACK (on_close), ia);
573 a->paste_button = gtk_button_new_from_stock (GTK_STOCK_PASTE);
574 gtk_assistant_add_action_widget (a->assistant, a->paste_button);
575 g_signal_connect (a->paste_button, "clicked", G_CALLBACK (on_paste), ia);
576 a->reset_button = gtk_button_new_from_stock ("pspp-stock-reset");
577 gtk_assistant_add_action_widget (a->assistant, a->reset_button);
578 g_signal_connect (a->reset_button, "clicked", G_CALLBACK (on_reset), ia);
579 gtk_window_set_title (GTK_WINDOW (a->assistant),
580 _("Importing Delimited Text Data"));
581 gtk_window_set_transient_for (GTK_WINDOW (a->assistant), parent_window);
582 gtk_window_set_icon_name (GTK_WINDOW (a->assistant), "psppicon");
584 a->prop_renderer = gtk_cell_renderer_text_new ();
585 g_object_ref_sink (a->prop_renderer);
586 a->fixed_renderer = gtk_cell_renderer_text_new ();
587 g_object_ref_sink (a->fixed_renderer);
588 g_object_set (G_OBJECT (a->fixed_renderer),
589 "family", "Monospace",
593 /* Frees IA's asst substructure. */
595 destroy_assistant (struct import_assistant *ia)
597 struct assistant *a = &ia->asst;
599 g_object_unref (a->prop_renderer);
600 g_object_unref (a->fixed_renderer);
601 g_object_unref (a->builder);
604 /* Appends a page of the given TYPE, with PAGE as its content, to
605 the GtkAssistant encapsulated by IA. Returns the GtkWidget
606 that represents the page. */
608 add_page_to_assistant (struct import_assistant *ia,
609 GtkWidget *page, GtkAssistantPageType type)
615 title = gtk_window_get_title (GTK_WINDOW (page));
616 title_copy = xstrdup (title ? title : "");
618 content = gtk_bin_get_child (GTK_BIN (page));
620 g_object_ref (content);
621 gtk_container_remove (GTK_CONTAINER (page), content);
623 gtk_widget_destroy (page);
625 gtk_assistant_append_page (ia->asst.assistant, content);
626 gtk_assistant_set_page_type (ia->asst.assistant, content, type);
627 gtk_assistant_set_page_title (ia->asst.assistant, content, title_copy);
628 gtk_assistant_set_page_complete (ia->asst.assistant, content, true);
635 /* Called just before PAGE is displayed as the current page of
636 ASSISTANT, this updates IA content according to the new
639 on_prepare (GtkAssistant *assistant, GtkWidget *page,
640 struct import_assistant *ia)
643 if (gtk_assistant_get_page_type (assistant, page)
644 == GTK_ASSISTANT_PAGE_CONFIRM)
645 gtk_widget_grab_focus (assistant->apply);
647 gtk_widget_grab_focus (assistant->forward);
649 if (page == ia->separators.page)
650 prepare_separators_page (ia);
651 else if (page == ia->formats.page)
652 prepare_formats_page (ia);
654 gtk_widget_show (ia->asst.reset_button);
655 if (page == ia->formats.page)
656 gtk_widget_show (ia->asst.paste_button);
658 gtk_widget_hide (ia->asst.paste_button);
661 /* Called when the Cancel button in the assistant is clicked. */
663 on_cancel (GtkAssistant *assistant, struct import_assistant *ia)
665 close_assistant (ia, GTK_RESPONSE_CANCEL);
668 /* Called when the Apply button on the last page of the assistant
671 on_close (GtkAssistant *assistant, struct import_assistant *ia)
673 close_assistant (ia, GTK_RESPONSE_APPLY);
676 /* Called when the Paste button on the last page of the assistant
679 on_paste (GtkButton *button, struct import_assistant *ia)
681 close_assistant (ia, PSPPIRE_RESPONSE_PASTE);
684 /* Called when the Reset button is clicked. */
686 on_reset (GtkButton *button, struct import_assistant *ia)
688 gint page_num = gtk_assistant_get_current_page (ia->asst.assistant);
689 GtkWidget *page = gtk_assistant_get_nth_page (ia->asst.assistant, page_num);
691 if (page == ia->intro.page)
692 reset_intro_page (ia);
693 else if (page == ia->first_line.page)
694 reset_first_line_page (ia);
695 else if (page == ia->separators.page)
696 reset_separators_page (ia);
697 else if (page == ia->formats.page)
698 reset_formats_page (ia);
701 /* Causes the assistant to close, returning RESPONSE for
702 interpretation by text_data_import_assistant. */
704 close_assistant (struct import_assistant *ia, int response)
706 ia->asst.response = response;
707 g_main_loop_quit (ia->asst.main_loop);
708 gtk_widget_hide (GTK_WIDGET (ia->asst.assistant));
711 /* The "intro" page of the assistant. */
713 static void on_intro_amount_changed (struct import_assistant *);
715 /* Initializes IA's intro substructure. */
717 init_intro_page (struct import_assistant *ia)
719 GtkBuilder *builder = ia->asst.builder;
720 struct intro_page *p = &ia->intro;
722 GtkWidget *hbox_n_cases ;
723 GtkWidget *hbox_percent ;
727 p->n_cases_spin = gtk_spin_button_new_with_range (0, INT_MAX, 100);
729 hbox_n_cases = widget_scanf (_("Only the first %4d cases"), &p->n_cases_spin);
731 table = get_widget_assert (builder, "button-table");
733 gtk_table_attach_defaults (GTK_TABLE (table), hbox_n_cases,
737 p->percent_spin = gtk_spin_button_new_with_range (0, 100, 10);
739 hbox_percent = widget_scanf (_("Only the first %3d %% of file (approximately)"), &p->percent_spin);
741 gtk_table_attach_defaults (GTK_TABLE (table), hbox_percent,
745 p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Intro"),
746 GTK_ASSISTANT_PAGE_INTRO);
748 p->all_cases_button = get_widget_assert (builder, "import-all-cases");
750 p->n_cases_button = get_widget_assert (builder, "import-n-cases");
752 p->percent_button = get_widget_assert (builder, "import-percent");
754 g_signal_connect_swapped (p->all_cases_button, "toggled",
755 G_CALLBACK (on_intro_amount_changed), ia);
756 g_signal_connect_swapped (p->n_cases_button, "toggled",
757 G_CALLBACK (on_intro_amount_changed), ia);
758 g_signal_connect_swapped (p->percent_button, "toggled",
759 G_CALLBACK (on_intro_amount_changed), ia);
761 on_intro_amount_changed (ia);
764 ds_put_cstr (&s, _("This assistant will guide you through the process of "
765 "importing data into PSPP from a text file with one line "
766 "per case, in which fields are separated by tabs, "
767 "commas, or other delimiters.\n\n"));
768 if (ia->file.total_is_exact)
770 &s, ngettext ("The selected file contains %zu line of text. ",
771 "The selected file contains %zu lines of text. ",
774 else if (ia->file.total_lines > 0)
778 "The selected file contains approximately %lu line of text. ",
779 "The selected file contains approximately %lu lines of text. ",
780 ia->file.total_lines),
781 ia->file.total_lines);
784 "Only the first %zu line of the file will be shown for "
785 "preview purposes in the following screens. ",
786 "Only the first %zu lines of the file will be shown for "
787 "preview purposes in the following screens. ",
791 ds_put_cstr (&s, _("You may choose below how much of the file should "
792 "actually be imported."));
793 gtk_label_set_text (GTK_LABEL (get_widget_assert (builder, "intro-label")),
798 /* Resets IA's intro page to its initial state. */
800 reset_intro_page (struct import_assistant *ia)
802 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->intro.all_cases_button),
806 /* Called when one of the radio buttons is clicked. */
808 on_intro_amount_changed (struct import_assistant *ia)
810 struct intro_page *p = &ia->intro;
812 gtk_widget_set_sensitive (p->n_cases_spin,
813 gtk_toggle_button_get_active (
814 GTK_TOGGLE_BUTTON (p->n_cases_button)));
816 gtk_widget_set_sensitive (p->percent_spin,
817 gtk_toggle_button_get_active (
818 GTK_TOGGLE_BUTTON (p->percent_button)));
821 /* The "first line" page of the assistant. */
823 static GtkTreeView *create_lines_tree_view (GtkContainer *parent_window,
824 struct import_assistant *);
825 static void on_first_line_change (GtkTreeSelection *,
826 struct import_assistant *);
827 static void on_variable_names_cb_toggle (GtkToggleButton *,
828 struct import_assistant *);
829 static void set_first_line (struct import_assistant *);
830 static void get_first_line (struct import_assistant *);
832 /* Initializes IA's first_line substructure. */
834 init_first_line_page (struct import_assistant *ia)
836 struct first_line_page *p = &ia->first_line;
837 GtkBuilder *builder = ia->asst.builder;
839 p->page = add_page_to_assistant (ia, get_widget_assert (builder, "FirstLine"),
840 GTK_ASSISTANT_PAGE_CONTENT);
841 gtk_widget_destroy (get_widget_assert (builder, "first-line"));
842 p->tree_view = create_lines_tree_view (
843 GTK_CONTAINER (get_widget_assert (builder, "first-line-scroller")), ia);
844 p->variable_names_cb = get_widget_assert (builder, "variable-names");
845 gtk_tree_selection_set_mode (
846 gtk_tree_view_get_selection (GTK_TREE_VIEW (p->tree_view)),
847 GTK_SELECTION_BROWSE);
849 g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (p->tree_view)),
850 "changed", G_CALLBACK (on_first_line_change), ia);
851 g_signal_connect (p->variable_names_cb, "toggled",
852 G_CALLBACK (on_variable_names_cb_toggle), ia);
855 /* Resets the first_line page to its initial content. */
857 reset_first_line_page (struct import_assistant *ia)
859 ia->first_line.skip_lines = 0;
860 ia->first_line.variable_names = false;
864 /* Creates and returns a tree view that contains each of the
865 lines in IA's file as a row. */
867 create_lines_tree_view (GtkContainer *parent, struct import_assistant *ia)
869 GtkTreeView *tree_view;
870 GtkTreeViewColumn *column;
871 size_t max_line_length;
872 gint content_width, header_width;
874 gchar *title = _("Text");
876 make_tree_view (ia, 0, &tree_view);
878 column = gtk_tree_view_column_new_with_attributes
880 title, ia->asst.fixed_renderer,
881 "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, title);
896 gtk_tree_view_column_set_fixed_width (column, MAX (content_width,
898 gtk_tree_view_append_column (tree_view, column);
900 gtk_tree_view_set_fixed_height_mode (tree_view, true);
902 gtk_container_add (parent, GTK_WIDGET (tree_view));
903 gtk_widget_show (GTK_WIDGET (tree_view));
908 /* Called when the line selected in the first_line tree view
911 on_first_line_change (GtkTreeSelection *selection UNUSED,
912 struct import_assistant *ia)
917 /* Called when the checkbox that indicates whether variable
918 names are in the row above the first line is toggled. */
920 on_variable_names_cb_toggle (GtkToggleButton *variable_names_cb UNUSED,
921 struct import_assistant *ia)
926 /* Sets the widgets to match IA's first_line substructure. */
928 set_first_line (struct import_assistant *ia)
932 path = gtk_tree_path_new_from_indices (ia->first_line.skip_lines, -1);
933 gtk_tree_view_set_cursor (GTK_TREE_VIEW (ia->first_line.tree_view),
935 gtk_tree_path_free (path);
937 gtk_toggle_button_set_active (
938 GTK_TOGGLE_BUTTON (ia->first_line.variable_names_cb),
939 ia->first_line.variable_names);
940 gtk_widget_set_sensitive (ia->first_line.variable_names_cb,
941 ia->first_line.skip_lines > 0);
944 /* Sets IA's first_line substructure to match the widgets. */
946 get_first_line (struct import_assistant *ia)
948 GtkTreeSelection *selection;
952 selection = gtk_tree_view_get_selection (ia->first_line.tree_view);
953 if (gtk_tree_selection_get_selected (selection, &model, &iter))
955 GtkTreePath *path = gtk_tree_model_get_path (model, &iter);
956 int row = gtk_tree_path_get_indices (path)[0];
957 gtk_tree_path_free (path);
959 ia->first_line.skip_lines = row;
960 ia->first_line.variable_names =
961 (ia->first_line.skip_lines > 0
962 && gtk_toggle_button_get_active (
963 GTK_TOGGLE_BUTTON (ia->first_line.variable_names_cb)));
965 gtk_widget_set_sensitive (ia->first_line.variable_names_cb,
966 ia->first_line.skip_lines > 0);
969 /* The "separators" page of the assistant. */
971 static void revise_fields_preview (struct import_assistant *ia);
972 static void choose_likely_separators (struct import_assistant *ia);
973 static void find_commonest_chars (unsigned long int histogram[UCHAR_MAX + 1],
974 const char *targets, const char *def,
975 struct string *result);
976 static void clear_fields (struct import_assistant *ia);
977 static void revise_fields_preview (struct import_assistant *);
978 static void set_separators (struct import_assistant *);
979 static void get_separators (struct import_assistant *);
980 static void on_separators_custom_entry_notify (GObject *UNUSED,
982 struct import_assistant *);
983 static void on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
984 struct import_assistant *);
985 static void on_quote_combo_change (GtkComboBox *combo,
986 struct import_assistant *);
987 static void on_quote_cb_toggle (GtkToggleButton *quote_cb,
988 struct import_assistant *);
989 static void on_separator_toggle (GtkToggleButton *, struct import_assistant *);
990 static void render_input_cell (GtkTreeViewColumn *tree_column,
991 GtkCellRenderer *cell,
992 GtkTreeModel *model, GtkTreeIter *iter,
994 static gboolean on_query_input_tooltip (GtkWidget *widget, gint wx, gint wy,
995 gboolean keyboard_mode UNUSED,
997 struct import_assistant *);
999 /* A common field separator and its identifying name. */
1002 const char *name; /* Name (for use with get_widget_assert). */
1003 int c; /* Separator character. */
1006 /* All the separators in the dialog box. */
1007 static const struct separator separators[] =
1019 #define SEPARATOR_CNT (sizeof separators / sizeof *separators)
1022 set_quote_list (GtkComboBoxEntry *cb)
1024 GtkListStore *list = gtk_list_store_new (1, G_TYPE_STRING);
1027 const gchar *seperator[3] = {"'\"", "\'", "\""};
1029 for (i = 0; i < 3; i++)
1031 const gchar *s = seperator[i];
1033 /* Add a new row to the model */
1034 gtk_list_store_append (list, &iter);
1035 gtk_list_store_set (list, &iter,
1041 gtk_combo_box_set_model (GTK_COMBO_BOX (cb), GTK_TREE_MODEL (list));
1043 gtk_combo_box_entry_set_text_column (cb, 0);
1046 /* Initializes IA's separators substructure. */
1048 init_separators_page (struct import_assistant *ia)
1050 GtkBuilder *builder = ia->asst.builder;
1051 struct separators_page *p = &ia->separators;
1054 choose_likely_separators (ia);
1056 p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Separators"),
1057 GTK_ASSISTANT_PAGE_CONTENT);
1058 p->custom_cb = get_widget_assert (builder, "custom-cb");
1059 p->custom_entry = get_widget_assert (builder, "custom-entry");
1060 p->quote_combo = get_widget_assert (builder, "quote-combo");
1061 p->quote_entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (p->quote_combo)));
1062 p->quote_cb = get_widget_assert (builder, "quote-cb");
1063 p->escape_cb = get_widget_assert (builder, "escape");
1065 set_separators (ia);
1066 set_quote_list (GTK_COMBO_BOX_ENTRY (p->quote_combo));
1067 p->fields_tree_view = GTK_TREE_VIEW (get_widget_assert (builder, "fields"));
1068 g_signal_connect (p->quote_combo, "changed",
1069 G_CALLBACK (on_quote_combo_change), ia);
1070 g_signal_connect (p->quote_cb, "toggled",
1071 G_CALLBACK (on_quote_cb_toggle), ia);
1072 g_signal_connect (p->custom_entry, "notify::text",
1073 G_CALLBACK (on_separators_custom_entry_notify), ia);
1074 g_signal_connect (p->custom_cb, "toggled",
1075 G_CALLBACK (on_separators_custom_cb_toggle), ia);
1076 for (i = 0; i < SEPARATOR_CNT; i++)
1077 g_signal_connect (get_widget_assert (builder, separators[i].name),
1078 "toggled", G_CALLBACK (on_separator_toggle), ia);
1079 g_signal_connect (p->escape_cb, "toggled",
1080 G_CALLBACK (on_separator_toggle), ia);
1083 /* Frees IA's separators substructure. */
1085 destroy_separators_page (struct import_assistant *ia)
1087 struct separators_page *s = &ia->separators;
1089 ds_destroy (&s->separators);
1090 ds_destroy (&s->quotes);
1094 /* Called just before the separators page becomes visible in the
1097 prepare_separators_page (struct import_assistant *ia)
1099 revise_fields_preview (ia);
1102 /* Called when the Reset button is clicked on the separators
1103 page, resets the separators to the defaults. */
1105 reset_separators_page (struct import_assistant *ia)
1107 choose_likely_separators (ia);
1108 set_separators (ia);
1111 /* Frees and clears the column data in IA's separators
1114 clear_fields (struct import_assistant *ia)
1116 struct separators_page *s = &ia->separators;
1118 if (s->column_cnt > 0)
1123 for (row = 0; row < ia->file.line_cnt; row++)
1125 const struct string *line = &ia->file.lines[row];
1126 const char *line_start = ds_data (line);
1127 const char *line_end = ds_end (line);
1129 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1131 char *s = ss_data (col->contents[row]);
1132 if (!(s >= line_start && s <= line_end))
1133 ss_dealloc (&col->contents[row]);
1137 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1140 free (col->contents);
1149 /* Breaks the file data in IA into columns based on the
1150 separators set in IA's separators substructure. */
1152 split_fields (struct import_assistant *ia)
1154 struct separators_page *s = &ia->separators;
1155 size_t columns_allocated;
1161 /* Is space in the set of separators? */
1162 space_sep = ss_find_byte (ds_ss (&s->separators), ' ') != SIZE_MAX;
1164 /* Split all the lines, not just those from
1165 ia->first_line.skip_lines on, so that we split the line that
1166 contains variables names if ia->first_line.variable_names is
1168 columns_allocated = 0;
1169 for (row = 0; row < ia->file.line_cnt; row++)
1171 struct string *line = &ia->file.lines[row];
1172 struct substring text = ds_ss (line);
1175 for (column_idx = 0; ; column_idx++)
1177 struct substring field;
1178 struct column *column;
1181 ss_ltrim (&text, ss_cstr (" "));
1182 if (ss_is_empty (text))
1184 if (column_idx != 0)
1188 else if (!ds_is_empty (&s->quotes)
1189 && ds_find_byte (&s->quotes, text.string[0]) != SIZE_MAX)
1191 int quote = ss_get_byte (&text);
1193 ss_get_until (&text, quote, &field);
1200 while ((c = ss_get_byte (&text)) != EOF)
1202 ds_put_byte (&s, c);
1203 else if (ss_match_byte (&text, quote))
1204 ds_put_byte (&s, quote);
1211 ss_get_bytes (&text, ss_cspan (text, ds_ss (&s->separators)),
1214 if (column_idx >= s->column_cnt)
1216 struct column *column;
1218 if (s->column_cnt >= columns_allocated)
1219 s->columns = x2nrealloc (s->columns, &columns_allocated,
1220 sizeof *s->columns);
1221 column = &s->columns[s->column_cnt++];
1222 column->name = NULL;
1224 column->contents = xcalloc (ia->file.line_cnt,
1225 sizeof *column->contents);
1227 column = &s->columns[column_idx];
1228 column->contents[row] = field;
1229 if (ss_length (field) > column->width)
1230 column->width = ss_length (field);
1233 ss_ltrim (&text, ss_cstr (" "));
1234 if (ss_is_empty (text))
1236 if (ss_find_byte (ds_ss (&s->separators), ss_first (text))
1238 ss_advance (&text, 1);
1243 /* Chooses a name for each column on the separators page */
1245 choose_column_names (struct import_assistant *ia)
1247 const struct first_line_page *f = &ia->first_line;
1248 struct separators_page *s = &ia->separators;
1249 struct dictionary *dict;
1250 unsigned long int generated_name_count = 0;
1254 dict = dict_create ();
1255 name_row = f->variable_names && f->skip_lines ? f->skip_lines : 0;
1256 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1260 hint = name_row ? ss_xstrdup (col->contents[name_row - 1]) : NULL;
1261 name = dict_make_unique_var_name (dict, hint, &generated_name_count);
1265 dict_create_var_assert (dict, name, 0);
1267 dict_destroy (dict);
1270 /* Picks the most likely separator and quote characters based on
1273 choose_likely_separators (struct import_assistant *ia)
1275 unsigned long int histogram[UCHAR_MAX + 1] = { 0 };
1278 /* Construct a histogram of all the characters used in the
1280 for (row = 0; row < ia->file.line_cnt; row++)
1282 struct substring line = ds_ss (&ia->file.lines[row]);
1283 size_t length = ss_length (line);
1285 for (i = 0; i < length; i++)
1286 histogram[(unsigned char) line.string[i]]++;
1289 find_commonest_chars (histogram, "\"'", "", &ia->separators.quotes);
1290 find_commonest_chars (histogram, ",;:/|!\t-", ",",
1291 &ia->separators.separators);
1292 ia->separators.escape = true;
1295 /* Chooses the most common character among those in TARGETS,
1296 based on the frequency data in HISTOGRAM, and stores it in
1297 RESULT. If there is a tie for the most common character among
1298 those in TARGETS, the earliest character is chosen. If none
1299 of the TARGETS appear at all, then DEF is used as a
1302 find_commonest_chars (unsigned long int histogram[UCHAR_MAX + 1],
1303 const char *targets, const char *def,
1304 struct string *result)
1306 unsigned char max = 0;
1307 unsigned long int max_count = 0;
1309 for (; *targets != '\0'; targets++)
1311 unsigned char c = *targets;
1312 unsigned long int count = histogram[c];
1313 if (count > max_count)
1322 ds_put_byte (result, max);
1325 ds_assign_cstr (result, def);
1328 /* Revises the contents of the fields tree view based on the
1329 currently chosen set of separators. */
1331 revise_fields_preview (struct import_assistant *ia)
1335 push_watch_cursor (ia);
1337 w = GTK_WIDGET (ia->separators.fields_tree_view);
1338 gtk_widget_destroy (w);
1339 get_separators (ia);
1341 choose_column_names (ia);
1342 ia->separators.fields_tree_view = create_data_tree_view (
1344 GTK_CONTAINER (get_widget_assert (ia->asst.builder, "fields-scroller")),
1347 pop_watch_cursor (ia);
1350 /* Sets the widgets to match IA's separators substructure. */
1352 set_separators (struct import_assistant *ia)
1354 struct separators_page *s = &ia->separators;
1356 struct string custom;
1361 ds_init_empty (&custom);
1363 for (i = 0; i < ds_length (&s->separators); i++)
1365 unsigned char c = ds_at (&s->separators, i);
1368 for (j = 0; j < SEPARATOR_CNT; j++)
1370 const struct separator *s = &separators[j];
1378 ds_put_byte (&custom, c);
1382 for (i = 0; i < SEPARATOR_CNT; i++)
1384 const struct separator *s = &separators[i];
1385 GtkWidget *button = get_widget_assert (ia->asst.builder, s->name);
1386 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
1387 (seps & (1u << i)) != 0);
1389 any_custom = !ds_is_empty (&custom);
1390 gtk_entry_set_text (GTK_ENTRY (s->custom_entry), ds_cstr (&custom));
1391 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->custom_cb),
1393 gtk_widget_set_sensitive (s->custom_entry, any_custom);
1394 ds_destroy (&custom);
1396 any_quotes = !ds_is_empty (&s->quotes);
1398 gtk_entry_set_text (s->quote_entry,
1399 any_quotes ? ds_cstr (&s->quotes) : "\"");
1400 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->quote_cb),
1402 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->escape_cb),
1404 gtk_widget_set_sensitive (s->quote_combo, any_quotes);
1405 gtk_widget_set_sensitive (s->escape_cb, any_quotes);
1408 /* Sets IA's separators substructure to match the widgets. */
1410 get_separators (struct import_assistant *ia)
1412 struct separators_page *s = &ia->separators;
1415 ds_clear (&s->separators);
1416 for (i = 0; i < SEPARATOR_CNT; i++)
1418 const struct separator *sep = &separators[i];
1419 GtkWidget *button = get_widget_assert (ia->asst.builder, sep->name);
1420 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
1421 ds_put_byte (&s->separators, sep->c);
1424 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->custom_cb)))
1425 ds_put_cstr (&s->separators,
1426 gtk_entry_get_text (GTK_ENTRY (s->custom_entry)));
1428 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->quote_cb)))
1430 gchar *text = gtk_combo_box_get_active_text (
1431 GTK_COMBO_BOX (s->quote_combo));
1432 ds_assign_cstr (&s->quotes, text);
1436 ds_clear (&s->quotes);
1437 s->escape = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->escape_cb));
1440 /* Called when the user changes the entry field for custom
1443 on_separators_custom_entry_notify (GObject *gobject UNUSED,
1444 GParamSpec *arg1 UNUSED,
1445 struct import_assistant *ia)
1447 revise_fields_preview (ia);
1450 /* Called when the user toggles the checkbox that enables custom
1453 on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
1454 struct import_assistant *ia)
1456 bool is_active = gtk_toggle_button_get_active (custom_cb);
1457 gtk_widget_set_sensitive (ia->separators.custom_entry, is_active);
1458 revise_fields_preview (ia);
1461 /* Called when the user changes the selection in the combo box
1462 that selects a quote character. */
1464 on_quote_combo_change (GtkComboBox *combo, struct import_assistant *ia)
1466 revise_fields_preview (ia);
1469 /* Called when the user toggles the checkbox that enables
1472 on_quote_cb_toggle (GtkToggleButton *quote_cb, struct import_assistant *ia)
1474 bool is_active = gtk_toggle_button_get_active (quote_cb);
1475 gtk_widget_set_sensitive (ia->separators.quote_combo, is_active);
1476 gtk_widget_set_sensitive (ia->separators.escape_cb, is_active);
1477 revise_fields_preview (ia);
1480 /* Called when the user toggles one of the separators
1483 on_separator_toggle (GtkToggleButton *toggle UNUSED,
1484 struct import_assistant *ia)
1486 revise_fields_preview (ia);
1489 /* Called to render one of the cells in the fields preview tree
1492 render_input_cell (GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1493 GtkTreeModel *model, GtkTreeIter *iter,
1496 struct import_assistant *ia = ia_;
1497 struct substring field;
1501 column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1503 row = text_import_model_iter_to_row (iter) + ia->first_line.skip_lines;
1504 field = ia->separators.columns[column].contents[row];
1505 if (field.string != NULL)
1507 GValue text = {0, };
1508 g_value_init (&text, G_TYPE_STRING);
1509 g_value_take_string (&text, ss_xstrdup (field));
1510 g_object_set_property (G_OBJECT (cell), "text", &text);
1511 g_value_unset (&text);
1512 g_object_set (cell, "background-set", FALSE, (void *) NULL);
1517 "background", "red",
1518 "background-set", TRUE,
1522 /* Called to render a tooltip on one of the cells in the fields
1523 preview tree view. */
1525 on_query_input_tooltip (GtkWidget *widget, gint wx, gint wy,
1526 gboolean keyboard_mode UNUSED,
1527 GtkTooltip *tooltip, struct import_assistant *ia)
1531 if (!get_tooltip_location (widget, wx, wy, ia, &row, &column))
1534 if (ia->separators.columns[column].contents[row].string != NULL)
1537 gtk_tooltip_set_text (tooltip,
1538 _("This input line has too few separators "
1539 "to fill in this field."));
1543 /* The "formats" page of the assistant. */
1545 static void on_variable_change (PsppireDict *dict, int idx,
1546 struct import_assistant *);
1547 static void clear_modified_vars (struct import_assistant *);
1549 /* Initializes IA's formats substructure. */
1551 init_formats_page (struct import_assistant *ia)
1553 GtkBuilder *builder = ia->asst.builder;
1554 struct formats_page *p = &ia->formats;
1556 p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Formats"),
1557 GTK_ASSISTANT_PAGE_CONFIRM);
1558 p->data_tree_view = GTK_TREE_VIEW (get_widget_assert (builder, "data"));
1559 p->modified_vars = NULL;
1560 p->modified_var_cnt = 0;
1564 /* Frees IA's formats substructure. */
1566 destroy_formats_page (struct import_assistant *ia)
1568 struct formats_page *p = &ia->formats;
1570 if (p->psppire_dict != NULL)
1572 /* This destroys p->dict also. */
1573 g_object_unref (p->psppire_dict);
1575 clear_modified_vars (ia);
1578 /* Called just before the formats page of the assistant is
1581 prepare_formats_page (struct import_assistant *ia)
1583 struct dictionary *dict;
1584 PsppireDict *psppire_dict;
1585 PsppireVarStore *var_store;
1586 GtkBin *vars_scroller;
1587 GtkWidget *old_var_sheet;
1588 PsppireVarSheet *var_sheet;
1589 struct separators_page *s = &ia->separators;
1590 struct formats_page *p = &ia->formats;
1591 struct fmt_guesser *fg;
1592 unsigned long int number = 0;
1595 push_watch_cursor (ia);
1597 dict = dict_create ();
1598 fg = fmt_guesser_create ();
1599 for (column_idx = 0; column_idx < s->column_cnt; column_idx++)
1601 struct variable *modified_var;
1603 modified_var = (column_idx < p->modified_var_cnt
1604 ? p->modified_vars[column_idx] : NULL);
1605 if (modified_var == NULL)
1607 struct column *column = &s->columns[column_idx];
1608 struct variable *var;
1609 struct fmt_spec format;
1613 /* Choose variable name. */
1614 name = dict_make_unique_var_name (dict, column->name, &number);
1616 /* Choose variable format. */
1617 fmt_guesser_clear (fg);
1618 for (row = ia->first_line.skip_lines; row < ia->file.line_cnt; row++)
1619 fmt_guesser_add (fg, column->contents[row]);
1620 fmt_guesser_guess (fg, &format);
1621 fmt_fix_input (&format);
1623 /* Create variable. */
1624 var = dict_create_var_assert (dict, name, fmt_var_width (&format));
1625 var_set_both_formats (var, &format);
1633 name = dict_make_unique_var_name (dict, var_get_name (modified_var),
1635 dict_clone_var_as_assert (dict, modified_var, name);
1639 fmt_guesser_destroy (fg);
1641 psppire_dict = psppire_dict_new_from_dict (dict);
1642 g_signal_connect (psppire_dict, "variable_changed",
1643 G_CALLBACK (on_variable_change), ia);
1644 ia->formats.dict = dict;
1645 ia->formats.psppire_dict = psppire_dict;
1647 /* XXX: PsppireVarStore doesn't hold a reference to
1648 psppire_dict for now, but it should. After it does, we
1649 should g_object_ref the psppire_dict here, since we also
1650 hold a reference via ia->formats.dict. */
1651 var_store = psppire_var_store_new (psppire_dict);
1652 g_object_set (var_store,
1653 "format-type", PSPPIRE_VAR_STORE_INPUT_FORMATS,
1655 var_sheet = PSPPIRE_VAR_SHEET (psppire_var_sheet_new ());
1656 g_object_set (var_sheet,
1658 "may-create-vars", FALSE,
1661 vars_scroller = GTK_BIN (get_widget_assert (ia->asst.builder, "vars-scroller"));
1662 old_var_sheet = gtk_bin_get_child (vars_scroller);
1663 if (old_var_sheet != NULL)
1664 gtk_widget_destroy (old_var_sheet);
1665 gtk_container_add (GTK_CONTAINER (vars_scroller), GTK_WIDGET (var_sheet));
1666 gtk_widget_show (GTK_WIDGET (var_sheet));
1668 gtk_widget_destroy (GTK_WIDGET (ia->formats.data_tree_view));
1669 ia->formats.data_tree_view = create_data_tree_view (
1671 GTK_CONTAINER (get_widget_assert (ia->asst.builder, "data-scroller")),
1674 pop_watch_cursor (ia);
1677 /* Clears the set of user-modified variables from IA's formats
1678 substructure. This discards user modifications to variable
1679 formats, thereby causing formats to revert to their
1682 clear_modified_vars (struct import_assistant *ia)
1684 struct formats_page *p = &ia->formats;
1687 for (i = 0; i < p->modified_var_cnt; i++)
1688 var_destroy (p->modified_vars[i]);
1689 free (p->modified_vars);
1690 p->modified_vars = NULL;
1691 p->modified_var_cnt = 0;
1694 /* Resets the formats page to its defaults, discarding user
1697 reset_formats_page (struct import_assistant *ia)
1699 clear_modified_vars (ia);
1700 prepare_formats_page (ia);
1703 /* Called when the user changes one of the variables in the
1706 on_variable_change (PsppireDict *dict, int dict_idx,
1707 struct import_assistant *ia)
1709 struct formats_page *p = &ia->formats;
1710 GtkTreeView *tv = ia->formats.data_tree_view;
1711 gint column_idx = dict_idx + 1;
1713 push_watch_cursor (ia);
1715 /* Remove previous column and replace with new column. */
1716 gtk_tree_view_remove_column (tv, gtk_tree_view_get_column (tv, column_idx));
1717 gtk_tree_view_insert_column (tv, make_data_column (ia, tv, false, dict_idx),
1720 /* Save a copy of the modified variable in modified_vars, so
1721 that its attributes will be preserved if we back up to the
1722 previous page with the Prev button and then come back
1724 if (dict_idx >= p->modified_var_cnt)
1727 p->modified_vars = xnrealloc (p->modified_vars, dict_idx + 1,
1728 sizeof *p->modified_vars);
1729 for (i = 0; i <= dict_idx; i++)
1730 p->modified_vars[i] = NULL;
1731 p->modified_var_cnt = dict_idx + 1;
1733 if (p->modified_vars[dict_idx])
1734 var_destroy (p->modified_vars[dict_idx]);
1735 p->modified_vars[dict_idx]
1736 = var_clone (psppire_dict_get_variable (dict, dict_idx));
1738 pop_watch_cursor (ia);
1741 /* Parses the contents of the field at (ROW,COLUMN) according to
1742 its variable format. If OUTPUTP is non-null, then *OUTPUTP
1743 receives the formatted output for that field (which must be
1744 freed with free). If TOOLTIPP is non-null, then *TOOLTIPP
1745 receives a message suitable for use in a tooltip, if one is
1746 needed, or a null pointer otherwise. Returns true if a
1747 tooltip message is needed, otherwise false. */
1749 parse_field (struct import_assistant *ia,
1750 size_t row, size_t column,
1751 char **outputp, char **tooltipp)
1753 struct substring field;
1755 struct variable *var;
1756 const struct fmt_spec *in;
1757 struct fmt_spec out;
1761 field = ia->separators.columns[column].contents[row];
1762 var = dict_get_var (ia->formats.dict, column);
1763 value_init (&val, var_get_width (var));
1764 in = var_get_print_format (var);
1765 out = fmt_for_output_from_input (in);
1767 if (field.string != NULL)
1771 error = data_in (field, LEGACY_NATIVE, in->type, &val,
1772 var_get_width (var),
1773 dict_get_encoding (ia->formats.dict));
1776 tooltip = xasprintf (_("Cannot parse field content `%.*s' as "
1778 (int) field.length, field.string,
1779 fmt_name (in->type), error);
1785 tooltip = xstrdup (_("This input line has too few separators "
1786 "to fill in this field."));
1787 value_set_missing (&val, var_get_width (var));
1789 if (outputp != NULL)
1791 *outputp = data_out (&val, dict_get_encoding (ia->formats.dict), &out);
1793 value_destroy (&val, var_get_width (var));
1795 ok = tooltip == NULL;
1796 if (tooltipp != NULL)
1797 *tooltipp = tooltip;
1803 /* Called to render one of the cells in the data preview tree
1806 render_output_cell (GtkTreeViewColumn *tree_column,
1807 GtkCellRenderer *cell,
1808 GtkTreeModel *model,
1812 struct import_assistant *ia = ia_;
1814 GValue gvalue = { 0, };
1817 ok = parse_field (ia,
1818 (text_import_model_iter_to_row (iter)
1819 + ia->first_line.skip_lines),
1820 GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1824 g_value_init (&gvalue, G_TYPE_STRING);
1825 g_value_take_string (&gvalue, output);
1826 g_object_set_property (G_OBJECT (cell), "text", &gvalue);
1827 g_value_unset (&gvalue);
1830 g_object_set (cell, "background-set", FALSE, (void *) NULL);
1833 "background", "red",
1834 "background-set", TRUE,
1838 /* Called to render a tooltip for one of the cells in the data
1839 preview tree view. */
1841 on_query_output_tooltip (GtkWidget *widget, gint wx, gint wy,
1842 gboolean keyboard_mode UNUSED,
1843 GtkTooltip *tooltip, struct import_assistant *ia)
1848 if (!get_tooltip_location (widget, wx, wy, ia, &row, &column))
1851 if (parse_field (ia, row, column, NULL, &text))
1854 gtk_tooltip_set_text (tooltip, text);
1859 /* Utility functions used by multiple pages of the assistant. */
1862 get_tooltip_location (GtkWidget *widget, gint wx, gint wy,
1863 const struct import_assistant *ia,
1864 size_t *row, size_t *column)
1866 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
1870 GtkTreeViewColumn *tree_column;
1871 GtkTreeModel *tree_model;
1874 /* Check that WIDGET is really visible on the screen before we
1875 do anything else. This is a bug fix for a sticky situation:
1876 when text_data_import_assistant() returns, it frees the data
1877 necessary to compose the tool tip message, but there may be
1878 a tool tip under preparation at that point (even if there is
1879 no visible tool tip) that will call back into us a little
1880 bit later. Perhaps the correct solution to this problem is
1881 to make the data related to the tool tips part of a GObject
1882 that only gets destroyed when all references are released,
1883 but this solution appears to be effective too. */
1884 if (!GTK_WIDGET_MAPPED (widget))
1887 gtk_tree_view_convert_widget_to_bin_window_coords (tree_view,
1889 if (!gtk_tree_view_get_path_at_pos (tree_view, bx, by,
1890 &path, &tree_column, NULL, NULL))
1893 *column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1896 tree_model = gtk_tree_view_get_model (tree_view);
1897 ok = gtk_tree_model_get_iter (tree_model, &iter, path);
1898 gtk_tree_path_free (path);
1902 *row = text_import_model_iter_to_row (&iter) + ia->first_line.skip_lines;
1907 make_tree_view (const struct import_assistant *ia,
1909 GtkTreeView **tree_view)
1911 GtkTreeModel *model;
1913 *tree_view = GTK_TREE_VIEW (gtk_tree_view_new ());
1914 model = GTK_TREE_MODEL (text_import_model_new (
1915 ia->file.lines + first_line,
1916 ia->file.line_cnt - first_line, first_line));
1917 gtk_tree_view_set_model (*tree_view, model);
1919 add_line_number_column (ia, *tree_view);
1923 add_line_number_column (const struct import_assistant *ia,
1924 GtkTreeView *treeview)
1926 GtkTreeViewColumn *column;
1928 column = gtk_tree_view_column_new_with_attributes (
1929 _("Line"), ia->asst.prop_renderer,
1930 "text", TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER,
1932 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
1933 gtk_tree_view_column_set_fixed_width (
1934 column, get_monospace_width (treeview, ia->asst.prop_renderer, 5));
1935 gtk_tree_view_append_column (treeview, column);
1939 get_monospace_width (GtkTreeView *treeview, GtkCellRenderer *renderer,
1946 ds_put_byte_multiple (&s, '0', char_cnt);
1947 ds_put_byte (&s, ' ');
1948 width = get_string_width (treeview, renderer, ds_cstr (&s));
1955 get_string_width (GtkTreeView *treeview, GtkCellRenderer *renderer,
1959 g_object_set (G_OBJECT (renderer), "text", string, (void *) NULL);
1960 gtk_cell_renderer_get_size (renderer, GTK_WIDGET (treeview),
1961 NULL, NULL, NULL, &width, NULL);
1965 static GtkTreeViewColumn *
1966 make_data_column (struct import_assistant *ia, GtkTreeView *tree_view,
1967 bool input, gint dict_idx)
1969 struct variable *var = NULL;
1970 struct column *column = NULL;
1972 gint content_width, header_width;
1973 GtkTreeViewColumn *tree_column;
1977 column = &ia->separators.columns[dict_idx];
1979 var = dict_get_var (ia->formats.dict, dict_idx);
1981 name = escape_underscores (input ? column->name : var_get_name (var));
1982 char_cnt = input ? column->width : var_get_print_format (var)->w;
1983 content_width = get_monospace_width (tree_view, ia->asst.fixed_renderer,
1985 header_width = get_string_width (tree_view, ia->asst.prop_renderer,
1988 tree_column = gtk_tree_view_column_new ();
1989 g_object_set_data (G_OBJECT (tree_column), "column-number",
1990 GINT_TO_POINTER (dict_idx));
1991 gtk_tree_view_column_set_title (tree_column, name);
1992 gtk_tree_view_column_pack_start (tree_column, ia->asst.fixed_renderer,
1994 gtk_tree_view_column_set_cell_data_func (
1995 tree_column, ia->asst.fixed_renderer,
1996 input ? render_input_cell : render_output_cell, ia, NULL);
1997 gtk_tree_view_column_set_sizing (tree_column, GTK_TREE_VIEW_COLUMN_FIXED);
1998 gtk_tree_view_column_set_fixed_width (tree_column, MAX (content_width,
2006 static GtkTreeView *
2007 create_data_tree_view (bool input, GtkContainer *parent,
2008 struct import_assistant *ia)
2010 GtkTreeView *tree_view;
2013 make_tree_view (ia, ia->first_line.skip_lines, &tree_view);
2014 gtk_tree_selection_set_mode (gtk_tree_view_get_selection (tree_view),
2015 GTK_SELECTION_NONE);
2017 for (i = 0; i < ia->separators.column_cnt; i++)
2018 gtk_tree_view_append_column (tree_view,
2019 make_data_column (ia, tree_view, input, i));
2021 g_object_set (G_OBJECT (tree_view), "has-tooltip", TRUE, (void *) NULL);
2022 g_signal_connect (tree_view, "query-tooltip",
2023 G_CALLBACK (input ? on_query_input_tooltip
2024 : on_query_output_tooltip), ia);
2025 gtk_tree_view_set_fixed_height_mode (tree_view, true);
2027 gtk_container_add (parent, GTK_WIDGET (tree_view));
2028 gtk_widget_show (GTK_WIDGET (tree_view));
2034 escape_underscores (const char *in)
2036 char *out = xmalloc (2 * strlen (in) + 1);
2040 for (; *in != '\0'; in++)
2051 /* TextImportModel, a GtkTreeModel implementation used by some
2052 pages of the assistant. */
2054 #define G_TYPE_TEXT_IMPORT_MODEL (text_import_model_get_type ())
2055 #define TEXT_IMPORT_MODEL(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), G_TYPE_TEXT_IMPORT_MODEL, TextImportModel))
2056 #define TEXT_IMPORT_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), G_TYPE_TEXT_IMPORT_MODEL, TextImportModelClass))
2057 #define IS_TEXT_IMPORT_MODEL(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), G_TYPE_TEXT_IMPORT_MODEL))
2058 #define IS_TEXT_IMPORT_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), G_TYPE_TEXT_IMPORT_MODEL))
2059 #define TEXT_IMPORT_MODEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), G_TYPE_TEXT_IMPORT_MODEL, TextImportModelClass))
2061 /* Random number used in 'stamp' member of GtkTreeIter. */
2062 #define TREE_MODEL_STAMP 0x7efd67d3
2064 struct TextImportModel
2067 struct string *lines;
2072 struct TextImportModelClass
2074 GObjectClass parent_class;
2077 GType text_import_model_get_type (void);
2078 static void text_import_model_tree_model_init (gpointer iface, gpointer data);
2081 text_import_model_get_type (void)
2083 static GType object_type = 0;
2087 static const GTypeInfo object_info = {
2088 sizeof (TextImportModelClass),
2089 (GBaseInitFunc) NULL,
2090 (GBaseFinalizeFunc) NULL,
2091 NULL, /* class_init */
2092 NULL, /* class_finalize */
2093 NULL, /* class_data */
2094 sizeof (TextImportModel),
2095 0, /* n_preallocs */
2096 NULL, /* instance_init */
2099 static const GInterfaceInfo tree_model_info = {
2100 text_import_model_tree_model_init,
2105 object_type = g_type_register_static (G_TYPE_OBJECT,
2109 g_type_add_interface_static (object_type, GTK_TYPE_TREE_MODEL,
2119 /* Creates and returns a new TextImportModel that contains the
2120 LINE_CNT lines in LINES. The lines before FIRST_LINE in LINES
2121 are not part of the model, but they are included in the line
2122 numbers in the TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER column.
2124 The caller retains responsibility for freeing LINES and must
2125 ensure that its lifetime and that of the strings that it
2126 contains exceeds that of the TextImportModel. */
2128 text_import_model_new (struct string *lines, size_t line_cnt,
2131 TextImportModel *new_text_import_model
2132 = g_object_new (G_TYPE_TEXT_IMPORT_MODEL, NULL);
2133 new_text_import_model->lines = lines;
2134 new_text_import_model->line_cnt = line_cnt;
2135 new_text_import_model->first_line = first_line;
2136 return new_text_import_model;
2141 tree_model_iter_has_child (GtkTreeModel *tree_model,
2148 tree_model_iter_parent (GtkTreeModel *tree_model,
2155 static GtkTreeModelFlags
2156 tree_model_get_flags (GtkTreeModel *model)
2158 g_return_val_if_fail (IS_TEXT_IMPORT_MODEL (model), (GtkTreeModelFlags) 0);
2160 return GTK_TREE_MODEL_LIST_ONLY | GTK_TREE_MODEL_ITERS_PERSIST;
2165 tree_model_n_columns (GtkTreeModel *model)
2171 tree_model_column_type (GtkTreeModel *model, gint index)
2173 return (index == TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER ? G_TYPE_INT
2174 : index == TEXT_IMPORT_MODEL_COLUMN_LINE ? G_TYPE_STRING
2179 init_iter (TextImportModel *list, gint idx, GtkTreeIter *iter)
2181 if (idx < 0 || idx >= list->line_cnt)
2184 iter->user_data = GINT_TO_POINTER (-1);
2189 iter->stamp = TREE_MODEL_STAMP;
2190 iter->user_data = GINT_TO_POINTER (idx);
2196 tree_model_get_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreePath *path)
2198 gint *indices, depth;
2200 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2202 g_return_val_if_fail (path, FALSE);
2204 indices = gtk_tree_path_get_indices (path);
2205 depth = gtk_tree_path_get_depth (path);
2207 g_return_val_if_fail (depth == 1, FALSE);
2209 return init_iter (list, indices[0], iter);
2214 tree_model_iter_next (GtkTreeModel *model, GtkTreeIter *iter)
2216 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2219 assert (iter->stamp == TREE_MODEL_STAMP);
2221 idx = GPOINTER_TO_INT (iter->user_data);
2222 return init_iter (list, idx == -1 ? -1 : idx + 1, iter);
2225 static GtkTreePath *
2226 tree_model_get_path (GtkTreeModel *model, GtkTreeIter *iter)
2230 g_return_val_if_fail (iter->stamp == TREE_MODEL_STAMP, FALSE);
2232 path = gtk_tree_path_new ();
2233 gtk_tree_path_append_index (path, GPOINTER_TO_INT (iter->user_data));
2239 tree_model_get_value (GtkTreeModel *model, GtkTreeIter *iter,
2240 gint column, GValue *value)
2242 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2245 g_return_if_fail (iter->stamp == TREE_MODEL_STAMP);
2247 idx = GPOINTER_TO_INT (iter->user_data);
2248 assert (idx >= 0 && idx < list->line_cnt);
2252 g_value_init (value, G_TYPE_INT);
2253 g_value_set_int (value, idx + list->first_line + 1);
2257 g_value_init (value, G_TYPE_STRING);
2258 g_value_set_static_string (value, ds_cstr (&list->lines[idx]));
2263 tree_model_iter_children (GtkTreeModel *tree_model,
2265 GtkTreeIter *parent)
2271 tree_model_n_children (GtkTreeModel *model, GtkTreeIter *iter)
2273 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2275 return iter == NULL ? list->line_cnt : 0;
2279 tree_model_nth_child (GtkTreeModel *model, GtkTreeIter *iter,
2280 GtkTreeIter *parent, gint n)
2282 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2283 g_return_val_if_fail (IS_TEXT_IMPORT_MODEL (model), FALSE);
2287 return init_iter (list, n, iter);
2291 text_import_model_tree_model_init (gpointer iface_, gpointer data UNUSED)
2293 GtkTreeModelIface *iface = (GtkTreeModelIface *) iface_;
2295 iface->get_flags = tree_model_get_flags;
2296 iface->get_n_columns = tree_model_n_columns;
2297 iface->get_column_type = tree_model_column_type;
2298 iface->get_iter = tree_model_get_iter;
2299 iface->iter_next = tree_model_iter_next;
2300 iface->get_path = tree_model_get_path;
2301 iface->get_value = tree_model_get_value;
2303 iface->iter_children = tree_model_iter_children;
2304 iface->iter_has_child = tree_model_iter_has_child;
2305 iface->iter_n_children = tree_model_n_children;
2306 iface->iter_nth_child = tree_model_nth_child;
2307 iface->iter_parent = tree_model_iter_parent;
2311 text_import_model_iter_to_row (const GtkTreeIter *iter)
2313 assert (iter->stamp == TREE_MODEL_STAMP);
2314 return GPOINTER_TO_INT (iter->user_data);
2317 /* Increments the "watch cursor" level, setting the cursor for
2318 the assistant window to a watch face to indicate to the user
2319 that the ongoing operation may take some time. */
2321 push_watch_cursor (struct import_assistant *ia)
2323 if (++ia->asst.watch_cursor == 1)
2325 GtkWidget *widget = GTK_WIDGET (ia->asst.assistant);
2326 GdkDisplay *display = gtk_widget_get_display (widget);
2327 GdkCursor *cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
2328 gdk_window_set_cursor (widget->window, cursor);
2329 gdk_cursor_unref (cursor);
2330 gdk_display_flush (display);
2334 /* Decrements the "watch cursor" level. If the level reaches
2335 zero, the cursor is reset to its default shape. */
2337 pop_watch_cursor (struct import_assistant *ia)
2339 if (--ia->asst.watch_cursor == 0)
2341 GtkWidget *widget = GTK_WIDGET (ia->asst.assistant);
2342 gdk_window_set_cursor (widget->window, NULL);