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/i18n.h"
36 #include "libpspp/message.h"
37 #include "ui/gui/checkbox-treeview.h"
38 #include "ui/gui/descriptives-dialog.h"
39 #include "ui/gui/dialog-common.h"
40 #include "ui/gui/executor.h"
41 #include "ui/gui/helper.h"
42 #include "ui/gui/psppire-data-window.h"
43 #include "ui/gui/psppire-dialog.h"
44 #include "ui/gui/psppire-var-sheet.h"
45 #include "ui/gui/psppire-var-store.h"
46 #include "ui/gui/widget-io.h"
47 #include "ui/syntax-gen.h"
50 #include "gl/xalloc.h"
53 #define _(msgid) gettext (msgid)
54 #define N_(msgid) msgid
57 /* TextImportModel, a GtkTreeModel used by the text data import
61 TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER, /* 1-based line number in file */
62 TEXT_IMPORT_MODEL_COLUMN_LINE, /* The line from the file. */
64 typedef struct TextImportModel TextImportModel;
65 typedef struct TextImportModelClass TextImportModelClass;
67 TextImportModel *text_import_model_new (struct string *lines, size_t line_cnt,
69 gint text_import_model_iter_to_row (const GtkTreeIter *);
71 struct import_assistant;
73 /* The file to be imported. */
76 char *file_name; /* File name. */
77 unsigned long int total_lines; /* Number of lines in file. */
78 bool total_is_exact; /* Is total_lines exact (or an estimate)? */
80 /* The first several lines of the file. */
84 static bool init_file (struct import_assistant *, GtkWindow *parent);
85 static void destroy_file (struct import_assistant *);
87 /* The main body of the GTK+ assistant and related data. */
91 GtkAssistant *assistant;
93 GtkWidget *paste_button;
94 GtkWidget *reset_button;
98 GtkCellRenderer *prop_renderer;
99 GtkCellRenderer *fixed_renderer;
101 static void init_assistant (struct import_assistant *, GtkWindow *);
102 static void destroy_assistant (struct import_assistant *);
103 static GtkWidget *add_page_to_assistant (struct import_assistant *,
105 GtkAssistantPageType);
107 /* The introduction page of the assistant. */
111 GtkWidget *all_cases_button;
112 GtkWidget *n_cases_button;
113 GtkWidget *n_cases_spin;
114 GtkWidget *percent_button;
115 GtkWidget *percent_spin;
117 static void init_intro_page (struct import_assistant *);
118 static void reset_intro_page (struct import_assistant *);
120 /* Page where the user chooses the first line of data. */
121 struct first_line_page
123 int skip_lines; /* Number of initial lines to skip? */
124 bool variable_names; /* Variable names above first line of data? */
127 GtkTreeView *tree_view;
128 GtkWidget *variable_names_cb;
130 static void init_first_line_page (struct import_assistant *);
131 static void reset_first_line_page (struct import_assistant *);
133 /* Page where the user chooses field separators. */
134 struct separators_page
136 /* How to break lines into columns. */
137 struct string separators; /* Field separators. */
138 struct string quotes; /* Quote characters. */
139 bool escape; /* Doubled quotes yield a quote mark? */
141 /* The columns produced thereby. */
142 struct column *columns; /* Information about each column. */
143 size_t column_cnt; /* Number of columns. */
146 GtkWidget *custom_cb;
147 GtkWidget *custom_entry;
149 GtkWidget *quote_combo;
150 GtkEntry *quote_entry;
151 GtkWidget *escape_cb;
152 GtkTreeView *fields_tree_view;
154 /* The columns that the separators divide the data into. */
157 /* Variable name for this column. This is the variable name
158 used on the separators page; it can be overridden by the
159 user on the formats page. */
162 /* Maximum length of any row in this column. */
165 /* Contents of this column: contents[row] is the contents for
168 A null substring indicates a missing column for that row
169 (because the line contains an insufficient number of
172 contents[] elements may be substrings of the lines[]
173 strings that represent the whole lines of the file, to
174 save memory. Other elements are dynamically allocated
175 with ss_alloc_substring. */
176 struct substring *contents;
178 static void init_separators_page (struct import_assistant *);
179 static void destroy_separators_page (struct import_assistant *);
180 static void prepare_separators_page (struct import_assistant *);
181 static void reset_separators_page (struct import_assistant *);
183 /* Page where the user verifies and adjusts input formats. */
186 struct dictionary *dict;
189 GtkTreeView *data_tree_view;
190 PsppireDict *psppire_dict;
191 struct variable **modified_vars;
192 size_t modified_var_cnt;
194 static void init_formats_page (struct import_assistant *);
195 static void destroy_formats_page (struct import_assistant *);
196 static void prepare_formats_page (struct import_assistant *);
197 static void reset_formats_page (struct import_assistant *);
199 struct import_assistant
202 struct assistant asst;
203 struct intro_page intro;
204 struct first_line_page first_line;
205 struct separators_page separators;
206 struct formats_page formats;
209 static void apply_dict (const struct dictionary *, struct string *);
210 static char *generate_syntax (const struct import_assistant *);
212 static gboolean get_tooltip_location (GtkWidget *widget, gint wx, gint wy,
213 const struct import_assistant *,
214 size_t *row, size_t *column);
215 static void make_tree_view (const struct import_assistant *ia,
217 GtkTreeView **tree_view);
218 static void add_line_number_column (const struct import_assistant *,
220 static gint get_monospace_width (GtkTreeView *, GtkCellRenderer *,
222 static gint get_string_width (GtkTreeView *, GtkCellRenderer *,
224 static GtkTreeViewColumn *make_data_column (struct import_assistant *,
225 GtkTreeView *, bool input,
227 static GtkTreeView *create_data_tree_view (bool input, GtkContainer *parent,
228 struct import_assistant *);
229 static char *escape_underscores (const char *in);
230 static void push_watch_cursor (struct import_assistant *);
231 static void pop_watch_cursor (struct import_assistant *);
233 /* Pops up the Text Data Import assistant. */
235 text_data_import_assistant (PsppireDataWindow *dw)
237 GtkWindow *parent_window = GTK_WINDOW (dw);
238 struct import_assistant *ia;
240 ia = xzalloc (sizeof *ia);
241 if (!init_file (ia, parent_window))
247 init_assistant (ia, parent_window);
248 init_intro_page (ia);
249 init_first_line_page (ia);
250 init_separators_page (ia);
251 init_formats_page (ia);
253 gtk_widget_show_all (GTK_WIDGET (ia->asst.assistant));
255 ia->asst.main_loop = g_main_loop_new (NULL, false);
256 g_main_loop_run (ia->asst.main_loop);
257 g_main_loop_unref (ia->asst.main_loop);
259 switch (ia->asst.response)
261 case GTK_RESPONSE_APPLY:
262 free (execute_syntax_string (dw, generate_syntax (ia)));
264 case PSPPIRE_RESPONSE_PASTE:
265 free (paste_syntax_to_window (generate_syntax (ia)));
271 destroy_formats_page (ia);
272 destroy_separators_page (ia);
273 destroy_assistant (ia);
278 /* Emits PSPP syntax to S that applies the dictionary attributes
279 (such as missing values and value labels) of the variables in
282 apply_dict (const struct dictionary *dict, struct string *s)
284 size_t var_cnt = dict_get_var_cnt (dict);
287 for (i = 0; i < var_cnt; i++)
289 struct variable *var = dict_get_var (dict, i);
290 const char *name = var_get_name (var);
291 enum val_type type = var_get_type (var);
292 int width = var_get_width (var);
293 enum measure measure = var_get_measure (var);
294 enum alignment alignment = var_get_alignment (var);
295 const struct fmt_spec *format = var_get_print_format (var);
297 if (var_has_missing_values (var))
299 const struct missing_values *mv = var_get_missing_values (var);
302 syntax_gen_pspp (s, "MISSING VALUES %ss (", name);
303 for (j = 0; j < mv_n_values (mv); j++)
306 ds_put_cstr (s, ", ");
307 syntax_gen_value (s, mv_get_value (mv, j), width, format);
310 if (mv_has_range (mv))
313 if (mv_has_value (mv))
314 ds_put_cstr (s, ", ");
315 mv_get_range (mv, &low, &high);
316 syntax_gen_num_range (s, low, high, format);
318 ds_put_cstr (s, ").\n");
320 if (var_has_value_labels (var))
322 const struct val_labs *vls = var_get_value_labels (var);
323 const struct val_lab **labels = val_labs_sorted (vls);
324 size_t n_labels = val_labs_count (vls);
327 syntax_gen_pspp (s, "VALUE LABELS %ss", name);
328 for (i = 0; i < n_labels; i++)
330 const struct val_lab *vl = labels[i];
331 ds_put_cstr (s, "\n ");
332 syntax_gen_value (s, &vl->value, width, format);
333 ds_put_byte (s, ' ');
334 syntax_gen_string (s, ss_cstr (val_lab_get_escaped_label (vl)));
337 ds_put_cstr (s, ".\n");
339 if (var_has_label (var))
340 syntax_gen_pspp (s, "VARIABLE LABELS %ss %sq.\n",
341 name, var_get_label (var));
342 if (measure != var_default_measure (type))
343 syntax_gen_pspp (s, "VARIABLE LEVEL %ss (%ss).\n",
345 (measure == MEASURE_NOMINAL ? "NOMINAL"
346 : measure == MEASURE_ORDINAL ? "ORDINAL"
348 if (alignment != var_default_alignment (type))
349 syntax_gen_pspp (s, "VARIABLE ALIGNMENT %ss (%ss).\n",
351 (alignment == ALIGN_LEFT ? "LEFT"
352 : alignment == ALIGN_CENTRE ? "CENTER"
354 if (var_get_display_width (var) != var_default_display_width (width))
355 syntax_gen_pspp (s, "VARIABLE WIDTH %ss (%d).\n",
356 name, var_get_display_width (var));
360 /* Generates and returns PSPP syntax to execute the import
361 operation described by IA. The caller must free the syntax
364 generate_syntax (const struct import_assistant *ia)
366 struct string s = DS_EMPTY_INITIALIZER;
375 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (
376 ia->intro.n_cases_button)))
377 ds_put_format (&s, " /IMPORTCASES=FIRST %d\n",
378 gtk_spin_button_get_value_as_int (
379 GTK_SPIN_BUTTON (ia->intro.n_cases_spin)));
380 else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (
381 ia->intro.percent_button)))
382 ds_put_format (&s, " /IMPORTCASES=PERCENT %d\n",
383 gtk_spin_button_get_value_as_int (
384 GTK_SPIN_BUTTON (ia->intro.percent_spin)));
386 ds_put_cstr (&s, " /IMPORTCASES=ALL\n");
388 " /ARRANGEMENT=DELIMITED\n"
390 if (ia->first_line.skip_lines > 0)
391 ds_put_format (&s, " /FIRSTCASE=%d\n", ia->first_line.skip_lines + 1);
392 ds_put_cstr (&s, " /DELIMITERS=\"");
393 if (ds_find_byte (&ia->separators.separators, '\t') != SIZE_MAX)
394 ds_put_cstr (&s, "\\t");
395 if (ds_find_byte (&ia->separators.separators, '\\') != SIZE_MAX)
396 ds_put_cstr (&s, "\\\\");
397 for (i = 0; i < ds_length (&ia->separators.separators); i++)
399 char c = ds_at (&ia->separators.separators, i);
401 ds_put_cstr (&s, "\"\"");
402 else if (c != '\t' && c != '\\')
405 ds_put_cstr (&s, "\"\n");
406 if (!ds_is_empty (&ia->separators.quotes))
407 syntax_gen_pspp (&s, " /QUALIFIER=%sq\n", ds_cstr (&ia->separators.quotes));
408 if (!ds_is_empty (&ia->separators.quotes) && ia->separators.escape)
409 ds_put_cstr (&s, " /ESCAPE\n");
410 ds_put_cstr (&s, " /VARIABLES=\n");
412 var_cnt = dict_get_var_cnt (ia->formats.dict);
413 for (i = 0; i < var_cnt; i++)
415 struct variable *var = dict_get_var (ia->formats.dict, i);
416 char format_string[FMT_STRING_LEN_MAX + 1];
417 fmt_to_string (var_get_print_format (var), format_string);
418 ds_put_format (&s, " %s %s%s\n",
419 var_get_name (var), format_string,
420 i == var_cnt - 1 ? "." : "");
423 apply_dict (ia->formats.dict, &s);
428 /* Choosing a file and reading it. */
430 static char *choose_file (GtkWindow *parent_window);
432 /* Obtains the file to import from the user and initializes IA's
433 file substructure. PARENT_WINDOW must be the window to use
434 as the file chooser window's parent.
436 Returns true if successful, false if the file name could not
437 be obtained or the file could not be read. */
439 init_file (struct import_assistant *ia, GtkWindow *parent_window)
441 struct file *file = &ia->file;
442 enum { MAX_PREVIEW_LINES = 1000 }; /* Max number of lines to read. */
443 enum { MAX_LINE_LEN = 16384 }; /* Max length of an acceptable line. */
446 file->file_name = choose_file (parent_window);
447 if (file->file_name == NULL)
450 stream = fopen (file->file_name, "r");
453 msg (ME, _("Could not open `%s': %s"),
454 file->file_name, strerror (errno));
458 file->lines = xnmalloc (MAX_PREVIEW_LINES, sizeof *file->lines);
459 for (; file->line_cnt < MAX_PREVIEW_LINES; file->line_cnt++)
461 struct string *line = &file->lines[file->line_cnt];
463 ds_init_empty (line);
464 if (!ds_read_line (line, stream, MAX_LINE_LEN))
468 else if (ferror (stream))
469 msg (ME, _("Error reading `%s': %s"),
470 file->file_name, strerror (errno));
472 msg (ME, _("Failed to read `%s', because it contains a line "
473 "over %d bytes long and therefore appears not to be "
475 file->file_name, MAX_LINE_LEN);
480 ds_chomp_byte (line, '\n');
481 ds_chomp_byte (line, '\r');
484 if (file->line_cnt == 0)
486 msg (ME, _("`%s' is empty."), file->file_name);
492 /* Estimate the number of lines in the file. */
493 if (file->line_cnt < MAX_PREVIEW_LINES)
494 file->total_lines = file->line_cnt;
498 off_t position = ftello (stream);
499 if (fstat (fileno (stream), &s) == 0 && position > 0)
500 file->total_lines = (double) file->line_cnt / position * s.st_size;
502 file->total_lines = 0;
508 /* Frees IA's file substructure. */
510 destroy_file (struct import_assistant *ia)
512 struct file *f = &ia->file;
515 for (i = 0; i < f->line_cnt; i++)
516 ds_destroy (&f->lines[i]);
518 g_free (f->file_name);
521 /* Obtains the file to read from the user and returns the name of
522 the file as a string that must be freed with g_free if
523 successful, otherwise a null pointer. PARENT_WINDOW must be
524 the window to use as the file chooser window's parent. */
526 choose_file (GtkWindow *parent_window)
531 dialog = gtk_file_chooser_dialog_new (_("Import Delimited Text Data"),
533 GTK_FILE_CHOOSER_ACTION_OPEN,
534 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
535 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
538 switch (gtk_dialog_run (GTK_DIALOG (dialog)))
540 case GTK_RESPONSE_ACCEPT:
541 file_name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
547 gtk_widget_destroy (dialog);
554 static void close_assistant (struct import_assistant *, int response);
555 static void on_prepare (GtkAssistant *assistant, GtkWidget *page,
556 struct import_assistant *);
557 static void on_cancel (GtkAssistant *assistant, struct import_assistant *);
558 static void on_close (GtkAssistant *assistant, struct import_assistant *);
559 static void on_paste (GtkButton *button, struct import_assistant *);
560 static void on_reset (GtkButton *button, struct import_assistant *);
561 static void close_assistant (struct import_assistant *, int response);
563 /* Initializes IA's asst substructure. PARENT_WINDOW must be the
564 window to use as the assistant window's parent. */
566 init_assistant (struct import_assistant *ia, GtkWindow *parent_window)
568 struct assistant *a = &ia->asst;
570 a->builder = builder_new ("text-data-import.ui");
571 a->assistant = GTK_ASSISTANT (gtk_assistant_new ());
572 g_signal_connect (a->assistant, "prepare", G_CALLBACK (on_prepare), ia);
573 g_signal_connect (a->assistant, "cancel", G_CALLBACK (on_cancel), ia);
574 g_signal_connect (a->assistant, "close", G_CALLBACK (on_close), ia);
575 a->paste_button = gtk_button_new_from_stock (GTK_STOCK_PASTE);
576 gtk_assistant_add_action_widget (a->assistant, a->paste_button);
577 g_signal_connect (a->paste_button, "clicked", G_CALLBACK (on_paste), ia);
578 a->reset_button = gtk_button_new_from_stock ("pspp-stock-reset");
579 gtk_assistant_add_action_widget (a->assistant, a->reset_button);
580 g_signal_connect (a->reset_button, "clicked", G_CALLBACK (on_reset), ia);
581 gtk_window_set_title (GTK_WINDOW (a->assistant),
582 _("Importing Delimited Text Data"));
583 gtk_window_set_transient_for (GTK_WINDOW (a->assistant), parent_window);
584 gtk_window_set_icon_name (GTK_WINDOW (a->assistant), "psppicon");
586 a->prop_renderer = gtk_cell_renderer_text_new ();
587 g_object_ref_sink (a->prop_renderer);
588 a->fixed_renderer = gtk_cell_renderer_text_new ();
589 g_object_ref_sink (a->fixed_renderer);
590 g_object_set (G_OBJECT (a->fixed_renderer),
591 "family", "Monospace",
595 /* Frees IA's asst substructure. */
597 destroy_assistant (struct import_assistant *ia)
599 struct assistant *a = &ia->asst;
601 g_object_unref (a->prop_renderer);
602 g_object_unref (a->fixed_renderer);
603 g_object_unref (a->builder);
606 /* Appends a page of the given TYPE, with PAGE as its content, to
607 the GtkAssistant encapsulated by IA. Returns the GtkWidget
608 that represents the page. */
610 add_page_to_assistant (struct import_assistant *ia,
611 GtkWidget *page, GtkAssistantPageType type)
617 title = gtk_window_get_title (GTK_WINDOW (page));
618 title_copy = xstrdup (title ? title : "");
620 content = gtk_bin_get_child (GTK_BIN (page));
622 g_object_ref (content);
623 gtk_container_remove (GTK_CONTAINER (page), content);
625 gtk_widget_destroy (page);
627 gtk_assistant_append_page (ia->asst.assistant, content);
628 gtk_assistant_set_page_type (ia->asst.assistant, content, type);
629 gtk_assistant_set_page_title (ia->asst.assistant, content, title_copy);
630 gtk_assistant_set_page_complete (ia->asst.assistant, content, true);
637 /* Called just before PAGE is displayed as the current page of
638 ASSISTANT, this updates IA content according to the new
641 on_prepare (GtkAssistant *assistant, GtkWidget *page,
642 struct import_assistant *ia)
645 if (gtk_assistant_get_page_type (assistant, page)
646 == GTK_ASSISTANT_PAGE_CONFIRM)
647 gtk_widget_grab_focus (assistant->apply);
649 gtk_widget_grab_focus (assistant->forward);
651 if (page == ia->separators.page)
652 prepare_separators_page (ia);
653 else if (page == ia->formats.page)
654 prepare_formats_page (ia);
656 gtk_widget_show (ia->asst.reset_button);
657 if (page == ia->formats.page)
658 gtk_widget_show (ia->asst.paste_button);
660 gtk_widget_hide (ia->asst.paste_button);
663 /* Called when the Cancel button in the assistant is clicked. */
665 on_cancel (GtkAssistant *assistant, struct import_assistant *ia)
667 close_assistant (ia, GTK_RESPONSE_CANCEL);
670 /* Called when the Apply button on the last page of the assistant
673 on_close (GtkAssistant *assistant, struct import_assistant *ia)
675 close_assistant (ia, GTK_RESPONSE_APPLY);
678 /* Called when the Paste button on the last page of the assistant
681 on_paste (GtkButton *button, struct import_assistant *ia)
683 close_assistant (ia, PSPPIRE_RESPONSE_PASTE);
686 /* Called when the Reset button is clicked. */
688 on_reset (GtkButton *button, struct import_assistant *ia)
690 gint page_num = gtk_assistant_get_current_page (ia->asst.assistant);
691 GtkWidget *page = gtk_assistant_get_nth_page (ia->asst.assistant, page_num);
693 if (page == ia->intro.page)
694 reset_intro_page (ia);
695 else if (page == ia->first_line.page)
696 reset_first_line_page (ia);
697 else if (page == ia->separators.page)
698 reset_separators_page (ia);
699 else if (page == ia->formats.page)
700 reset_formats_page (ia);
703 /* Causes the assistant to close, returning RESPONSE for
704 interpretation by text_data_import_assistant. */
706 close_assistant (struct import_assistant *ia, int response)
708 ia->asst.response = response;
709 g_main_loop_quit (ia->asst.main_loop);
710 gtk_widget_hide (GTK_WIDGET (ia->asst.assistant));
713 /* The "intro" page of the assistant. */
715 static void on_intro_amount_changed (struct import_assistant *);
717 /* Initializes IA's intro substructure. */
719 init_intro_page (struct import_assistant *ia)
721 GtkBuilder *builder = ia->asst.builder;
722 struct intro_page *p = &ia->intro;
724 GtkWidget *hbox_n_cases ;
725 GtkWidget *hbox_percent ;
729 p->n_cases_spin = gtk_spin_button_new_with_range (0, INT_MAX, 100);
731 hbox_n_cases = widget_scanf (_("Only the first %4d cases"), &p->n_cases_spin);
733 table = get_widget_assert (builder, "button-table");
735 gtk_table_attach_defaults (GTK_TABLE (table), hbox_n_cases,
739 p->percent_spin = gtk_spin_button_new_with_range (0, 100, 10);
741 hbox_percent = widget_scanf (_("Only the first %3d %% of file (approximately)"), &p->percent_spin);
743 gtk_table_attach_defaults (GTK_TABLE (table), hbox_percent,
747 p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Intro"),
748 GTK_ASSISTANT_PAGE_INTRO);
750 p->all_cases_button = get_widget_assert (builder, "import-all-cases");
752 p->n_cases_button = get_widget_assert (builder, "import-n-cases");
754 p->percent_button = get_widget_assert (builder, "import-percent");
756 g_signal_connect_swapped (p->all_cases_button, "toggled",
757 G_CALLBACK (on_intro_amount_changed), ia);
758 g_signal_connect_swapped (p->n_cases_button, "toggled",
759 G_CALLBACK (on_intro_amount_changed), ia);
760 g_signal_connect_swapped (p->percent_button, "toggled",
761 G_CALLBACK (on_intro_amount_changed), ia);
763 on_intro_amount_changed (ia);
766 ds_put_cstr (&s, _("This assistant will guide you through the process of "
767 "importing data into PSPP from a text file with one line "
768 "per case, in which fields are separated by tabs, "
769 "commas, or other delimiters.\n\n"));
770 if (ia->file.total_is_exact)
772 &s, ngettext ("The selected file contains %zu line of text. ",
773 "The selected file contains %zu lines of text. ",
776 else if (ia->file.total_lines > 0)
780 "The selected file contains approximately %lu line of text. ",
781 "The selected file contains approximately %lu lines of text. ",
782 ia->file.total_lines),
783 ia->file.total_lines);
786 "Only the first %zu line of the file will be shown for "
787 "preview purposes in the following screens. ",
788 "Only the first %zu lines of the file will be shown for "
789 "preview purposes in the following screens. ",
793 ds_put_cstr (&s, _("You may choose below how much of the file should "
794 "actually be imported."));
795 gtk_label_set_text (GTK_LABEL (get_widget_assert (builder, "intro-label")),
800 /* Resets IA's intro page to its initial state. */
802 reset_intro_page (struct import_assistant *ia)
804 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->intro.all_cases_button),
808 /* Called when one of the radio buttons is clicked. */
810 on_intro_amount_changed (struct import_assistant *ia)
812 struct intro_page *p = &ia->intro;
814 gtk_widget_set_sensitive (p->n_cases_spin,
815 gtk_toggle_button_get_active (
816 GTK_TOGGLE_BUTTON (p->n_cases_button)));
818 gtk_widget_set_sensitive (p->percent_spin,
819 gtk_toggle_button_get_active (
820 GTK_TOGGLE_BUTTON (p->percent_button)));
823 /* The "first line" page of the assistant. */
825 static GtkTreeView *create_lines_tree_view (GtkContainer *parent_window,
826 struct import_assistant *);
827 static void on_first_line_change (GtkTreeSelection *,
828 struct import_assistant *);
829 static void on_variable_names_cb_toggle (GtkToggleButton *,
830 struct import_assistant *);
831 static void set_first_line (struct import_assistant *);
832 static void get_first_line (struct import_assistant *);
834 /* Initializes IA's first_line substructure. */
836 init_first_line_page (struct import_assistant *ia)
838 struct first_line_page *p = &ia->first_line;
839 GtkBuilder *builder = ia->asst.builder;
841 p->page = add_page_to_assistant (ia, get_widget_assert (builder, "FirstLine"),
842 GTK_ASSISTANT_PAGE_CONTENT);
843 gtk_widget_destroy (get_widget_assert (builder, "first-line"));
844 p->tree_view = create_lines_tree_view (
845 GTK_CONTAINER (get_widget_assert (builder, "first-line-scroller")), ia);
846 p->variable_names_cb = get_widget_assert (builder, "variable-names");
847 gtk_tree_selection_set_mode (
848 gtk_tree_view_get_selection (GTK_TREE_VIEW (p->tree_view)),
849 GTK_SELECTION_BROWSE);
851 g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (p->tree_view)),
852 "changed", G_CALLBACK (on_first_line_change), ia);
853 g_signal_connect (p->variable_names_cb, "toggled",
854 G_CALLBACK (on_variable_names_cb_toggle), ia);
857 /* Resets the first_line page to its initial content. */
859 reset_first_line_page (struct import_assistant *ia)
861 ia->first_line.skip_lines = 0;
862 ia->first_line.variable_names = false;
866 /* Creates and returns a tree view that contains each of the
867 lines in IA's file as a row. */
869 create_lines_tree_view (GtkContainer *parent, struct import_assistant *ia)
871 GtkTreeView *tree_view;
872 GtkTreeViewColumn *column;
873 size_t max_line_length;
874 gint content_width, header_width;
876 const gchar *title = _("Text");
878 make_tree_view (ia, 0, &tree_view);
880 column = gtk_tree_view_column_new_with_attributes
882 title, ia->asst.fixed_renderer,
883 "text", TEXT_IMPORT_MODEL_COLUMN_LINE,
886 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
889 for (i = 0; i < ia->file.line_cnt; i++)
891 size_t w = ds_length (&ia->file.lines[i]);
892 max_line_length = MAX (max_line_length, w);
895 content_width = get_monospace_width (tree_view, ia->asst.fixed_renderer,
897 header_width = get_string_width (tree_view, ia->asst.prop_renderer, title);
898 gtk_tree_view_column_set_fixed_width (column, MAX (content_width,
900 gtk_tree_view_append_column (tree_view, column);
902 gtk_tree_view_set_fixed_height_mode (tree_view, true);
904 gtk_container_add (parent, GTK_WIDGET (tree_view));
905 gtk_widget_show (GTK_WIDGET (tree_view));
910 /* Called when the line selected in the first_line tree view
913 on_first_line_change (GtkTreeSelection *selection UNUSED,
914 struct import_assistant *ia)
919 /* Called when the checkbox that indicates whether variable
920 names are in the row above the first line is toggled. */
922 on_variable_names_cb_toggle (GtkToggleButton *variable_names_cb UNUSED,
923 struct import_assistant *ia)
928 /* Sets the widgets to match IA's first_line substructure. */
930 set_first_line (struct import_assistant *ia)
934 path = gtk_tree_path_new_from_indices (ia->first_line.skip_lines, -1);
935 gtk_tree_view_set_cursor (GTK_TREE_VIEW (ia->first_line.tree_view),
937 gtk_tree_path_free (path);
939 gtk_toggle_button_set_active (
940 GTK_TOGGLE_BUTTON (ia->first_line.variable_names_cb),
941 ia->first_line.variable_names);
942 gtk_widget_set_sensitive (ia->first_line.variable_names_cb,
943 ia->first_line.skip_lines > 0);
946 /* Sets IA's first_line substructure to match the widgets. */
948 get_first_line (struct import_assistant *ia)
950 GtkTreeSelection *selection;
954 selection = gtk_tree_view_get_selection (ia->first_line.tree_view);
955 if (gtk_tree_selection_get_selected (selection, &model, &iter))
957 GtkTreePath *path = gtk_tree_model_get_path (model, &iter);
958 int row = gtk_tree_path_get_indices (path)[0];
959 gtk_tree_path_free (path);
961 ia->first_line.skip_lines = row;
962 ia->first_line.variable_names =
963 (ia->first_line.skip_lines > 0
964 && gtk_toggle_button_get_active (
965 GTK_TOGGLE_BUTTON (ia->first_line.variable_names_cb)));
967 gtk_widget_set_sensitive (ia->first_line.variable_names_cb,
968 ia->first_line.skip_lines > 0);
971 /* The "separators" page of the assistant. */
973 static void revise_fields_preview (struct import_assistant *ia);
974 static void choose_likely_separators (struct import_assistant *ia);
975 static void find_commonest_chars (unsigned long int histogram[UCHAR_MAX + 1],
976 const char *targets, const char *def,
977 struct string *result);
978 static void clear_fields (struct import_assistant *ia);
979 static void revise_fields_preview (struct import_assistant *);
980 static void set_separators (struct import_assistant *);
981 static void get_separators (struct import_assistant *);
982 static void on_separators_custom_entry_notify (GObject *UNUSED,
984 struct import_assistant *);
985 static void on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
986 struct import_assistant *);
987 static void on_quote_combo_change (GtkComboBox *combo,
988 struct import_assistant *);
989 static void on_quote_cb_toggle (GtkToggleButton *quote_cb,
990 struct import_assistant *);
991 static void on_separator_toggle (GtkToggleButton *, struct import_assistant *);
992 static void render_input_cell (GtkTreeViewColumn *tree_column,
993 GtkCellRenderer *cell,
994 GtkTreeModel *model, GtkTreeIter *iter,
996 static gboolean on_query_input_tooltip (GtkWidget *widget, gint wx, gint wy,
997 gboolean keyboard_mode UNUSED,
999 struct import_assistant *);
1001 /* A common field separator and its identifying name. */
1004 const char *name; /* Name (for use with get_widget_assert). */
1005 int c; /* Separator character. */
1008 /* All the separators in the dialog box. */
1009 static const struct separator separators[] =
1021 #define SEPARATOR_CNT (sizeof separators / sizeof *separators)
1024 set_quote_list (GtkComboBoxEntry *cb)
1026 GtkListStore *list = gtk_list_store_new (1, G_TYPE_STRING);
1029 const gchar *seperator[3] = {"'\"", "\'", "\""};
1031 for (i = 0; i < 3; i++)
1033 const gchar *s = seperator[i];
1035 /* Add a new row to the model */
1036 gtk_list_store_append (list, &iter);
1037 gtk_list_store_set (list, &iter,
1043 gtk_combo_box_set_model (GTK_COMBO_BOX (cb), GTK_TREE_MODEL (list));
1045 gtk_combo_box_entry_set_text_column (cb, 0);
1048 /* Initializes IA's separators substructure. */
1050 init_separators_page (struct import_assistant *ia)
1052 GtkBuilder *builder = ia->asst.builder;
1053 struct separators_page *p = &ia->separators;
1056 choose_likely_separators (ia);
1058 p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Separators"),
1059 GTK_ASSISTANT_PAGE_CONTENT);
1060 p->custom_cb = get_widget_assert (builder, "custom-cb");
1061 p->custom_entry = get_widget_assert (builder, "custom-entry");
1062 p->quote_combo = get_widget_assert (builder, "quote-combo");
1063 p->quote_entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (p->quote_combo)));
1064 p->quote_cb = get_widget_assert (builder, "quote-cb");
1065 p->escape_cb = get_widget_assert (builder, "escape");
1067 set_separators (ia);
1068 set_quote_list (GTK_COMBO_BOX_ENTRY (p->quote_combo));
1069 p->fields_tree_view = GTK_TREE_VIEW (get_widget_assert (builder, "fields"));
1070 g_signal_connect (p->quote_combo, "changed",
1071 G_CALLBACK (on_quote_combo_change), ia);
1072 g_signal_connect (p->quote_cb, "toggled",
1073 G_CALLBACK (on_quote_cb_toggle), ia);
1074 g_signal_connect (p->custom_entry, "notify::text",
1075 G_CALLBACK (on_separators_custom_entry_notify), ia);
1076 g_signal_connect (p->custom_cb, "toggled",
1077 G_CALLBACK (on_separators_custom_cb_toggle), ia);
1078 for (i = 0; i < SEPARATOR_CNT; i++)
1079 g_signal_connect (get_widget_assert (builder, separators[i].name),
1080 "toggled", G_CALLBACK (on_separator_toggle), ia);
1081 g_signal_connect (p->escape_cb, "toggled",
1082 G_CALLBACK (on_separator_toggle), ia);
1085 /* Frees IA's separators substructure. */
1087 destroy_separators_page (struct import_assistant *ia)
1089 struct separators_page *s = &ia->separators;
1091 ds_destroy (&s->separators);
1092 ds_destroy (&s->quotes);
1096 /* Called just before the separators page becomes visible in the
1099 prepare_separators_page (struct import_assistant *ia)
1101 revise_fields_preview (ia);
1104 /* Called when the Reset button is clicked on the separators
1105 page, resets the separators to the defaults. */
1107 reset_separators_page (struct import_assistant *ia)
1109 choose_likely_separators (ia);
1110 set_separators (ia);
1113 /* Frees and clears the column data in IA's separators
1116 clear_fields (struct import_assistant *ia)
1118 struct separators_page *s = &ia->separators;
1120 if (s->column_cnt > 0)
1125 for (row = 0; row < ia->file.line_cnt; row++)
1127 const struct string *line = &ia->file.lines[row];
1128 const char *line_start = ds_data (line);
1129 const char *line_end = ds_end (line);
1131 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1133 char *s = ss_data (col->contents[row]);
1134 if (!(s >= line_start && s <= line_end))
1135 ss_dealloc (&col->contents[row]);
1139 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1142 free (col->contents);
1151 /* Breaks the file data in IA into columns based on the
1152 separators set in IA's separators substructure. */
1154 split_fields (struct import_assistant *ia)
1156 struct separators_page *s = &ia->separators;
1157 size_t columns_allocated;
1163 /* Is space in the set of separators? */
1164 space_sep = ss_find_byte (ds_ss (&s->separators), ' ') != SIZE_MAX;
1166 /* Split all the lines, not just those from
1167 ia->first_line.skip_lines on, so that we split the line that
1168 contains variables names if ia->first_line.variable_names is
1170 columns_allocated = 0;
1171 for (row = 0; row < ia->file.line_cnt; row++)
1173 struct string *line = &ia->file.lines[row];
1174 struct substring text = ds_ss (line);
1177 for (column_idx = 0; ; column_idx++)
1179 struct substring field;
1180 struct column *column;
1183 ss_ltrim (&text, ss_cstr (" "));
1184 if (ss_is_empty (text))
1186 if (column_idx != 0)
1190 else if (!ds_is_empty (&s->quotes)
1191 && ds_find_byte (&s->quotes, text.string[0]) != SIZE_MAX)
1193 int quote = ss_get_byte (&text);
1195 ss_get_until (&text, quote, &field);
1202 while ((c = ss_get_byte (&text)) != EOF)
1204 ds_put_byte (&s, c);
1205 else if (ss_match_byte (&text, quote))
1206 ds_put_byte (&s, quote);
1213 ss_get_bytes (&text, ss_cspan (text, ds_ss (&s->separators)),
1216 if (column_idx >= s->column_cnt)
1218 struct column *column;
1220 if (s->column_cnt >= columns_allocated)
1221 s->columns = x2nrealloc (s->columns, &columns_allocated,
1222 sizeof *s->columns);
1223 column = &s->columns[s->column_cnt++];
1224 column->name = NULL;
1226 column->contents = xcalloc (ia->file.line_cnt,
1227 sizeof *column->contents);
1229 column = &s->columns[column_idx];
1230 column->contents[row] = field;
1231 if (ss_length (field) > column->width)
1232 column->width = ss_length (field);
1235 ss_ltrim (&text, ss_cstr (" "));
1236 if (ss_is_empty (text))
1238 if (ss_find_byte (ds_ss (&s->separators), ss_first (text))
1240 ss_advance (&text, 1);
1245 /* Chooses a name for each column on the separators page */
1247 choose_column_names (struct import_assistant *ia)
1249 const struct first_line_page *f = &ia->first_line;
1250 struct separators_page *s = &ia->separators;
1251 struct dictionary *dict;
1252 unsigned long int generated_name_count = 0;
1256 dict = dict_create (get_default_encoding ());
1257 name_row = f->variable_names && f->skip_lines ? f->skip_lines : 0;
1258 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1262 hint = name_row ? ss_xstrdup (col->contents[name_row - 1]) : NULL;
1263 name = dict_make_unique_var_name (dict, hint, &generated_name_count);
1267 dict_create_var_assert (dict, name, 0);
1269 dict_destroy (dict);
1272 /* Picks the most likely separator and quote characters based on
1275 choose_likely_separators (struct import_assistant *ia)
1277 unsigned long int histogram[UCHAR_MAX + 1] = { 0 };
1280 /* Construct a histogram of all the characters used in the
1282 for (row = 0; row < ia->file.line_cnt; row++)
1284 struct substring line = ds_ss (&ia->file.lines[row]);
1285 size_t length = ss_length (line);
1287 for (i = 0; i < length; i++)
1288 histogram[(unsigned char) line.string[i]]++;
1291 find_commonest_chars (histogram, "\"'", "", &ia->separators.quotes);
1292 find_commonest_chars (histogram, ",;:/|!\t-", ",",
1293 &ia->separators.separators);
1294 ia->separators.escape = true;
1297 /* Chooses the most common character among those in TARGETS,
1298 based on the frequency data in HISTOGRAM, and stores it in
1299 RESULT. If there is a tie for the most common character among
1300 those in TARGETS, the earliest character is chosen. If none
1301 of the TARGETS appear at all, then DEF is used as a
1304 find_commonest_chars (unsigned long int histogram[UCHAR_MAX + 1],
1305 const char *targets, const char *def,
1306 struct string *result)
1308 unsigned char max = 0;
1309 unsigned long int max_count = 0;
1311 for (; *targets != '\0'; targets++)
1313 unsigned char c = *targets;
1314 unsigned long int count = histogram[c];
1315 if (count > max_count)
1324 ds_put_byte (result, max);
1327 ds_assign_cstr (result, def);
1330 /* Revises the contents of the fields tree view based on the
1331 currently chosen set of separators. */
1333 revise_fields_preview (struct import_assistant *ia)
1337 push_watch_cursor (ia);
1339 w = GTK_WIDGET (ia->separators.fields_tree_view);
1340 gtk_widget_destroy (w);
1341 get_separators (ia);
1343 choose_column_names (ia);
1344 ia->separators.fields_tree_view = create_data_tree_view (
1346 GTK_CONTAINER (get_widget_assert (ia->asst.builder, "fields-scroller")),
1349 pop_watch_cursor (ia);
1352 /* Sets the widgets to match IA's separators substructure. */
1354 set_separators (struct import_assistant *ia)
1356 struct separators_page *s = &ia->separators;
1358 struct string custom;
1363 ds_init_empty (&custom);
1365 for (i = 0; i < ds_length (&s->separators); i++)
1367 unsigned char c = ds_at (&s->separators, i);
1370 for (j = 0; j < SEPARATOR_CNT; j++)
1372 const struct separator *s = &separators[j];
1380 ds_put_byte (&custom, c);
1384 for (i = 0; i < SEPARATOR_CNT; i++)
1386 const struct separator *s = &separators[i];
1387 GtkWidget *button = get_widget_assert (ia->asst.builder, s->name);
1388 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
1389 (seps & (1u << i)) != 0);
1391 any_custom = !ds_is_empty (&custom);
1392 gtk_entry_set_text (GTK_ENTRY (s->custom_entry), ds_cstr (&custom));
1393 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->custom_cb),
1395 gtk_widget_set_sensitive (s->custom_entry, any_custom);
1396 ds_destroy (&custom);
1398 any_quotes = !ds_is_empty (&s->quotes);
1400 gtk_entry_set_text (s->quote_entry,
1401 any_quotes ? ds_cstr (&s->quotes) : "\"");
1402 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->quote_cb),
1404 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->escape_cb),
1406 gtk_widget_set_sensitive (s->quote_combo, any_quotes);
1407 gtk_widget_set_sensitive (s->escape_cb, any_quotes);
1410 /* Sets IA's separators substructure to match the widgets. */
1412 get_separators (struct import_assistant *ia)
1414 struct separators_page *s = &ia->separators;
1417 ds_clear (&s->separators);
1418 for (i = 0; i < SEPARATOR_CNT; i++)
1420 const struct separator *sep = &separators[i];
1421 GtkWidget *button = get_widget_assert (ia->asst.builder, sep->name);
1422 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
1423 ds_put_byte (&s->separators, sep->c);
1426 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->custom_cb)))
1427 ds_put_cstr (&s->separators,
1428 gtk_entry_get_text (GTK_ENTRY (s->custom_entry)));
1430 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->quote_cb)))
1432 gchar *text = gtk_combo_box_get_active_text (
1433 GTK_COMBO_BOX (s->quote_combo));
1434 ds_assign_cstr (&s->quotes, text);
1438 ds_clear (&s->quotes);
1439 s->escape = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->escape_cb));
1442 /* Called when the user changes the entry field for custom
1445 on_separators_custom_entry_notify (GObject *gobject UNUSED,
1446 GParamSpec *arg1 UNUSED,
1447 struct import_assistant *ia)
1449 revise_fields_preview (ia);
1452 /* Called when the user toggles the checkbox that enables custom
1455 on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
1456 struct import_assistant *ia)
1458 bool is_active = gtk_toggle_button_get_active (custom_cb);
1459 gtk_widget_set_sensitive (ia->separators.custom_entry, is_active);
1460 revise_fields_preview (ia);
1463 /* Called when the user changes the selection in the combo box
1464 that selects a quote character. */
1466 on_quote_combo_change (GtkComboBox *combo, struct import_assistant *ia)
1468 revise_fields_preview (ia);
1471 /* Called when the user toggles the checkbox that enables
1474 on_quote_cb_toggle (GtkToggleButton *quote_cb, struct import_assistant *ia)
1476 bool is_active = gtk_toggle_button_get_active (quote_cb);
1477 gtk_widget_set_sensitive (ia->separators.quote_combo, is_active);
1478 gtk_widget_set_sensitive (ia->separators.escape_cb, is_active);
1479 revise_fields_preview (ia);
1482 /* Called when the user toggles one of the separators
1485 on_separator_toggle (GtkToggleButton *toggle UNUSED,
1486 struct import_assistant *ia)
1488 revise_fields_preview (ia);
1491 /* Called to render one of the cells in the fields preview tree
1494 render_input_cell (GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1495 GtkTreeModel *model, GtkTreeIter *iter,
1498 struct import_assistant *ia = ia_;
1499 struct substring field;
1503 column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1505 row = text_import_model_iter_to_row (iter) + ia->first_line.skip_lines;
1506 field = ia->separators.columns[column].contents[row];
1507 if (field.string != NULL)
1509 GValue text = {0, };
1510 g_value_init (&text, G_TYPE_STRING);
1511 g_value_take_string (&text, ss_xstrdup (field));
1512 g_object_set_property (G_OBJECT (cell), "text", &text);
1513 g_value_unset (&text);
1514 g_object_set (cell, "background-set", FALSE, (void *) NULL);
1519 "background", "red",
1520 "background-set", TRUE,
1524 /* Called to render a tooltip on one of the cells in the fields
1525 preview tree view. */
1527 on_query_input_tooltip (GtkWidget *widget, gint wx, gint wy,
1528 gboolean keyboard_mode UNUSED,
1529 GtkTooltip *tooltip, struct import_assistant *ia)
1533 if (!get_tooltip_location (widget, wx, wy, ia, &row, &column))
1536 if (ia->separators.columns[column].contents[row].string != NULL)
1539 gtk_tooltip_set_text (tooltip,
1540 _("This input line has too few separators "
1541 "to fill in this field."));
1545 /* The "formats" page of the assistant. */
1547 static void on_variable_change (PsppireDict *dict, int idx,
1548 struct import_assistant *);
1549 static void clear_modified_vars (struct import_assistant *);
1551 /* Initializes IA's formats substructure. */
1553 init_formats_page (struct import_assistant *ia)
1555 GtkBuilder *builder = ia->asst.builder;
1556 struct formats_page *p = &ia->formats;
1558 p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Formats"),
1559 GTK_ASSISTANT_PAGE_CONFIRM);
1560 p->data_tree_view = GTK_TREE_VIEW (get_widget_assert (builder, "data"));
1561 p->modified_vars = NULL;
1562 p->modified_var_cnt = 0;
1566 /* Frees IA's formats substructure. */
1568 destroy_formats_page (struct import_assistant *ia)
1570 struct formats_page *p = &ia->formats;
1572 if (p->psppire_dict != NULL)
1574 /* This destroys p->dict also. */
1575 g_object_unref (p->psppire_dict);
1577 clear_modified_vars (ia);
1580 /* Called just before the formats page of the assistant is
1583 prepare_formats_page (struct import_assistant *ia)
1585 struct dictionary *dict;
1586 PsppireDict *psppire_dict;
1587 PsppireVarStore *var_store;
1588 GtkBin *vars_scroller;
1589 GtkWidget *old_var_sheet;
1590 PsppireVarSheet *var_sheet;
1591 struct separators_page *s = &ia->separators;
1592 struct formats_page *p = &ia->formats;
1593 struct fmt_guesser *fg;
1594 unsigned long int number = 0;
1597 push_watch_cursor (ia);
1599 dict = dict_create (get_default_encoding ());
1600 fg = fmt_guesser_create ();
1601 for (column_idx = 0; column_idx < s->column_cnt; column_idx++)
1603 struct variable *modified_var;
1605 modified_var = (column_idx < p->modified_var_cnt
1606 ? p->modified_vars[column_idx] : NULL);
1607 if (modified_var == NULL)
1609 struct column *column = &s->columns[column_idx];
1610 struct variable *var;
1611 struct fmt_spec format;
1615 /* Choose variable name. */
1616 name = dict_make_unique_var_name (dict, column->name, &number);
1618 /* Choose variable format. */
1619 fmt_guesser_clear (fg);
1620 for (row = ia->first_line.skip_lines; row < ia->file.line_cnt; row++)
1621 fmt_guesser_add (fg, column->contents[row]);
1622 fmt_guesser_guess (fg, &format);
1623 fmt_fix_input (&format);
1625 /* Create variable. */
1626 var = dict_create_var_assert (dict, name, fmt_var_width (&format));
1627 var_set_both_formats (var, &format);
1635 name = dict_make_unique_var_name (dict, var_get_name (modified_var),
1637 dict_clone_var_as_assert (dict, modified_var, name);
1641 fmt_guesser_destroy (fg);
1643 psppire_dict = psppire_dict_new_from_dict (dict);
1644 g_signal_connect (psppire_dict, "variable_changed",
1645 G_CALLBACK (on_variable_change), ia);
1646 ia->formats.dict = dict;
1647 ia->formats.psppire_dict = psppire_dict;
1649 /* XXX: PsppireVarStore doesn't hold a reference to
1650 psppire_dict for now, but it should. After it does, we
1651 should g_object_ref the psppire_dict here, since we also
1652 hold a reference via ia->formats.dict. */
1653 var_store = psppire_var_store_new (psppire_dict);
1654 g_object_set (var_store,
1655 "format-type", PSPPIRE_VAR_STORE_INPUT_FORMATS,
1657 var_sheet = PSPPIRE_VAR_SHEET (psppire_var_sheet_new ());
1658 g_object_set (var_sheet,
1660 "may-create-vars", FALSE,
1663 vars_scroller = GTK_BIN (get_widget_assert (ia->asst.builder, "vars-scroller"));
1664 old_var_sheet = gtk_bin_get_child (vars_scroller);
1665 if (old_var_sheet != NULL)
1666 gtk_widget_destroy (old_var_sheet);
1667 gtk_container_add (GTK_CONTAINER (vars_scroller), GTK_WIDGET (var_sheet));
1668 gtk_widget_show (GTK_WIDGET (var_sheet));
1670 gtk_widget_destroy (GTK_WIDGET (ia->formats.data_tree_view));
1671 ia->formats.data_tree_view = create_data_tree_view (
1673 GTK_CONTAINER (get_widget_assert (ia->asst.builder, "data-scroller")),
1676 pop_watch_cursor (ia);
1679 /* Clears the set of user-modified variables from IA's formats
1680 substructure. This discards user modifications to variable
1681 formats, thereby causing formats to revert to their
1684 clear_modified_vars (struct import_assistant *ia)
1686 struct formats_page *p = &ia->formats;
1689 for (i = 0; i < p->modified_var_cnt; i++)
1690 var_destroy (p->modified_vars[i]);
1691 free (p->modified_vars);
1692 p->modified_vars = NULL;
1693 p->modified_var_cnt = 0;
1696 /* Resets the formats page to its defaults, discarding user
1699 reset_formats_page (struct import_assistant *ia)
1701 clear_modified_vars (ia);
1702 prepare_formats_page (ia);
1705 /* Called when the user changes one of the variables in the
1708 on_variable_change (PsppireDict *dict, int dict_idx,
1709 struct import_assistant *ia)
1711 struct formats_page *p = &ia->formats;
1712 GtkTreeView *tv = ia->formats.data_tree_view;
1713 gint column_idx = dict_idx + 1;
1715 push_watch_cursor (ia);
1717 /* Remove previous column and replace with new column. */
1718 gtk_tree_view_remove_column (tv, gtk_tree_view_get_column (tv, column_idx));
1719 gtk_tree_view_insert_column (tv, make_data_column (ia, tv, false, dict_idx),
1722 /* Save a copy of the modified variable in modified_vars, so
1723 that its attributes will be preserved if we back up to the
1724 previous page with the Prev button and then come back
1726 if (dict_idx >= p->modified_var_cnt)
1729 p->modified_vars = xnrealloc (p->modified_vars, dict_idx + 1,
1730 sizeof *p->modified_vars);
1731 for (i = 0; i <= dict_idx; i++)
1732 p->modified_vars[i] = NULL;
1733 p->modified_var_cnt = dict_idx + 1;
1735 if (p->modified_vars[dict_idx])
1736 var_destroy (p->modified_vars[dict_idx]);
1737 p->modified_vars[dict_idx]
1738 = var_clone (psppire_dict_get_variable (dict, dict_idx));
1740 pop_watch_cursor (ia);
1743 /* Parses the contents of the field at (ROW,COLUMN) according to
1744 its variable format. If OUTPUTP is non-null, then *OUTPUTP
1745 receives the formatted output for that field (which must be
1746 freed with free). If TOOLTIPP is non-null, then *TOOLTIPP
1747 receives a message suitable for use in a tooltip, if one is
1748 needed, or a null pointer otherwise. Returns true if a
1749 tooltip message is needed, otherwise false. */
1751 parse_field (struct import_assistant *ia,
1752 size_t row, size_t column,
1753 char **outputp, char **tooltipp)
1755 struct substring field;
1757 struct variable *var;
1758 const struct fmt_spec *in;
1759 struct fmt_spec out;
1763 field = ia->separators.columns[column].contents[row];
1764 var = dict_get_var (ia->formats.dict, column);
1765 value_init (&val, var_get_width (var));
1766 in = var_get_print_format (var);
1767 out = fmt_for_output_from_input (in);
1769 if (field.string != NULL)
1773 error = data_in (field, C_ENCODING, in->type, &val, var_get_width (var),
1774 dict_get_encoding (ia->formats.dict));
1777 tooltip = xasprintf (_("Cannot parse field content `%.*s' as "
1779 (int) field.length, field.string,
1780 fmt_name (in->type), error);
1786 tooltip = xstrdup (_("This input line has too few separators "
1787 "to fill in this field."));
1788 value_set_missing (&val, var_get_width (var));
1790 if (outputp != NULL)
1792 *outputp = data_out (&val, dict_get_encoding (ia->formats.dict), &out);
1794 value_destroy (&val, var_get_width (var));
1796 ok = tooltip == NULL;
1797 if (tooltipp != NULL)
1798 *tooltipp = tooltip;
1804 /* Called to render one of the cells in the data preview tree
1807 render_output_cell (GtkTreeViewColumn *tree_column,
1808 GtkCellRenderer *cell,
1809 GtkTreeModel *model,
1813 struct import_assistant *ia = ia_;
1815 GValue gvalue = { 0, };
1818 ok = parse_field (ia,
1819 (text_import_model_iter_to_row (iter)
1820 + ia->first_line.skip_lines),
1821 GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1825 g_value_init (&gvalue, G_TYPE_STRING);
1826 g_value_take_string (&gvalue, output);
1827 g_object_set_property (G_OBJECT (cell), "text", &gvalue);
1828 g_value_unset (&gvalue);
1831 g_object_set (cell, "background-set", FALSE, (void *) NULL);
1834 "background", "red",
1835 "background-set", TRUE,
1839 /* Called to render a tooltip for one of the cells in the data
1840 preview tree view. */
1842 on_query_output_tooltip (GtkWidget *widget, gint wx, gint wy,
1843 gboolean keyboard_mode UNUSED,
1844 GtkTooltip *tooltip, struct import_assistant *ia)
1849 if (!get_tooltip_location (widget, wx, wy, ia, &row, &column))
1852 if (parse_field (ia, row, column, NULL, &text))
1855 gtk_tooltip_set_text (tooltip, text);
1860 /* Utility functions used by multiple pages of the assistant. */
1863 get_tooltip_location (GtkWidget *widget, gint wx, gint wy,
1864 const struct import_assistant *ia,
1865 size_t *row, size_t *column)
1867 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
1871 GtkTreeViewColumn *tree_column;
1872 GtkTreeModel *tree_model;
1875 /* Check that WIDGET is really visible on the screen before we
1876 do anything else. This is a bug fix for a sticky situation:
1877 when text_data_import_assistant() returns, it frees the data
1878 necessary to compose the tool tip message, but there may be
1879 a tool tip under preparation at that point (even if there is
1880 no visible tool tip) that will call back into us a little
1881 bit later. Perhaps the correct solution to this problem is
1882 to make the data related to the tool tips part of a GObject
1883 that only gets destroyed when all references are released,
1884 but this solution appears to be effective too. */
1885 if (!gtk_widget_get_mapped (widget))
1888 gtk_tree_view_convert_widget_to_bin_window_coords (tree_view,
1890 if (!gtk_tree_view_get_path_at_pos (tree_view, bx, by,
1891 &path, &tree_column, NULL, NULL))
1894 *column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1897 tree_model = gtk_tree_view_get_model (tree_view);
1898 ok = gtk_tree_model_get_iter (tree_model, &iter, path);
1899 gtk_tree_path_free (path);
1903 *row = text_import_model_iter_to_row (&iter) + ia->first_line.skip_lines;
1908 make_tree_view (const struct import_assistant *ia,
1910 GtkTreeView **tree_view)
1912 GtkTreeModel *model;
1914 *tree_view = GTK_TREE_VIEW (gtk_tree_view_new ());
1915 model = GTK_TREE_MODEL (text_import_model_new (
1916 ia->file.lines + first_line,
1917 ia->file.line_cnt - first_line, first_line));
1918 gtk_tree_view_set_model (*tree_view, model);
1920 add_line_number_column (ia, *tree_view);
1924 add_line_number_column (const struct import_assistant *ia,
1925 GtkTreeView *treeview)
1927 GtkTreeViewColumn *column;
1929 column = gtk_tree_view_column_new_with_attributes (
1930 _("Line"), ia->asst.prop_renderer,
1931 "text", TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER,
1933 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
1934 gtk_tree_view_column_set_fixed_width (
1935 column, get_monospace_width (treeview, ia->asst.prop_renderer, 5));
1936 gtk_tree_view_append_column (treeview, column);
1940 get_monospace_width (GtkTreeView *treeview, GtkCellRenderer *renderer,
1947 ds_put_byte_multiple (&s, '0', char_cnt);
1948 ds_put_byte (&s, ' ');
1949 width = get_string_width (treeview, renderer, ds_cstr (&s));
1956 get_string_width (GtkTreeView *treeview, GtkCellRenderer *renderer,
1960 g_object_set (G_OBJECT (renderer), "text", string, (void *) NULL);
1961 gtk_cell_renderer_get_size (renderer, GTK_WIDGET (treeview),
1962 NULL, NULL, NULL, &width, NULL);
1966 static GtkTreeViewColumn *
1967 make_data_column (struct import_assistant *ia, GtkTreeView *tree_view,
1968 bool input, gint dict_idx)
1970 struct variable *var = NULL;
1971 struct column *column = NULL;
1973 gint content_width, header_width;
1974 GtkTreeViewColumn *tree_column;
1978 column = &ia->separators.columns[dict_idx];
1980 var = dict_get_var (ia->formats.dict, dict_idx);
1982 name = escape_underscores (input ? column->name : var_get_name (var));
1983 char_cnt = input ? column->width : var_get_print_format (var)->w;
1984 content_width = get_monospace_width (tree_view, ia->asst.fixed_renderer,
1986 header_width = get_string_width (tree_view, ia->asst.prop_renderer,
1989 tree_column = gtk_tree_view_column_new ();
1990 g_object_set_data (G_OBJECT (tree_column), "column-number",
1991 GINT_TO_POINTER (dict_idx));
1992 gtk_tree_view_column_set_title (tree_column, name);
1993 gtk_tree_view_column_pack_start (tree_column, ia->asst.fixed_renderer,
1995 gtk_tree_view_column_set_cell_data_func (
1996 tree_column, ia->asst.fixed_renderer,
1997 input ? render_input_cell : render_output_cell, ia, NULL);
1998 gtk_tree_view_column_set_sizing (tree_column, GTK_TREE_VIEW_COLUMN_FIXED);
1999 gtk_tree_view_column_set_fixed_width (tree_column, MAX (content_width,
2007 static GtkTreeView *
2008 create_data_tree_view (bool input, GtkContainer *parent,
2009 struct import_assistant *ia)
2011 GtkTreeView *tree_view;
2014 make_tree_view (ia, ia->first_line.skip_lines, &tree_view);
2015 gtk_tree_selection_set_mode (gtk_tree_view_get_selection (tree_view),
2016 GTK_SELECTION_NONE);
2018 for (i = 0; i < ia->separators.column_cnt; i++)
2019 gtk_tree_view_append_column (tree_view,
2020 make_data_column (ia, tree_view, input, i));
2022 g_object_set (G_OBJECT (tree_view), "has-tooltip", TRUE, (void *) NULL);
2023 g_signal_connect (tree_view, "query-tooltip",
2024 G_CALLBACK (input ? on_query_input_tooltip
2025 : on_query_output_tooltip), ia);
2026 gtk_tree_view_set_fixed_height_mode (tree_view, true);
2028 gtk_container_add (parent, GTK_WIDGET (tree_view));
2029 gtk_widget_show (GTK_WIDGET (tree_view));
2035 escape_underscores (const char *in)
2037 char *out = xmalloc (2 * strlen (in) + 1);
2041 for (; *in != '\0'; in++)
2052 /* TextImportModel, a GtkTreeModel implementation used by some
2053 pages of the assistant. */
2055 #define G_TYPE_TEXT_IMPORT_MODEL (text_import_model_get_type ())
2056 #define TEXT_IMPORT_MODEL(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), G_TYPE_TEXT_IMPORT_MODEL, TextImportModel))
2057 #define TEXT_IMPORT_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), G_TYPE_TEXT_IMPORT_MODEL, TextImportModelClass))
2058 #define IS_TEXT_IMPORT_MODEL(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), G_TYPE_TEXT_IMPORT_MODEL))
2059 #define IS_TEXT_IMPORT_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), G_TYPE_TEXT_IMPORT_MODEL))
2060 #define TEXT_IMPORT_MODEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), G_TYPE_TEXT_IMPORT_MODEL, TextImportModelClass))
2062 /* Random number used in 'stamp' member of GtkTreeIter. */
2063 #define TREE_MODEL_STAMP 0x7efd67d3
2065 struct TextImportModel
2068 struct string *lines;
2073 struct TextImportModelClass
2075 GObjectClass parent_class;
2078 GType text_import_model_get_type (void);
2079 static void text_import_model_tree_model_init (gpointer iface, gpointer data);
2082 text_import_model_get_type (void)
2084 static GType object_type = 0;
2088 static const GTypeInfo object_info = {
2089 sizeof (TextImportModelClass),
2090 (GBaseInitFunc) NULL,
2091 (GBaseFinalizeFunc) NULL,
2092 NULL, /* class_init */
2093 NULL, /* class_finalize */
2094 NULL, /* class_data */
2095 sizeof (TextImportModel),
2096 0, /* n_preallocs */
2097 NULL, /* instance_init */
2100 static const GInterfaceInfo tree_model_info = {
2101 text_import_model_tree_model_init,
2106 object_type = g_type_register_static (G_TYPE_OBJECT,
2110 g_type_add_interface_static (object_type, GTK_TYPE_TREE_MODEL,
2120 /* Creates and returns a new TextImportModel that contains the
2121 LINE_CNT lines in LINES. The lines before FIRST_LINE in LINES
2122 are not part of the model, but they are included in the line
2123 numbers in the TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER column.
2125 The caller retains responsibility for freeing LINES and must
2126 ensure that its lifetime and that of the strings that it
2127 contains exceeds that of the TextImportModel. */
2129 text_import_model_new (struct string *lines, size_t line_cnt,
2132 TextImportModel *new_text_import_model
2133 = g_object_new (G_TYPE_TEXT_IMPORT_MODEL, NULL);
2134 new_text_import_model->lines = lines;
2135 new_text_import_model->line_cnt = line_cnt;
2136 new_text_import_model->first_line = first_line;
2137 return new_text_import_model;
2142 tree_model_iter_has_child (GtkTreeModel *tree_model,
2149 tree_model_iter_parent (GtkTreeModel *tree_model,
2156 static GtkTreeModelFlags
2157 tree_model_get_flags (GtkTreeModel *model)
2159 g_return_val_if_fail (IS_TEXT_IMPORT_MODEL (model), (GtkTreeModelFlags) 0);
2161 return GTK_TREE_MODEL_LIST_ONLY | GTK_TREE_MODEL_ITERS_PERSIST;
2166 tree_model_n_columns (GtkTreeModel *model)
2172 tree_model_column_type (GtkTreeModel *model, gint index)
2174 return (index == TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER ? G_TYPE_INT
2175 : index == TEXT_IMPORT_MODEL_COLUMN_LINE ? G_TYPE_STRING
2180 init_iter (TextImportModel *list, gint idx, GtkTreeIter *iter)
2182 if (idx < 0 || idx >= list->line_cnt)
2185 iter->user_data = GINT_TO_POINTER (-1);
2190 iter->stamp = TREE_MODEL_STAMP;
2191 iter->user_data = GINT_TO_POINTER (idx);
2197 tree_model_get_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreePath *path)
2199 gint *indices, depth;
2201 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2203 g_return_val_if_fail (path, FALSE);
2205 indices = gtk_tree_path_get_indices (path);
2206 depth = gtk_tree_path_get_depth (path);
2208 g_return_val_if_fail (depth == 1, FALSE);
2210 return init_iter (list, indices[0], iter);
2215 tree_model_iter_next (GtkTreeModel *model, GtkTreeIter *iter)
2217 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2220 assert (iter->stamp == TREE_MODEL_STAMP);
2222 idx = GPOINTER_TO_INT (iter->user_data);
2223 return init_iter (list, idx == -1 ? -1 : idx + 1, iter);
2226 static GtkTreePath *
2227 tree_model_get_path (GtkTreeModel *model, GtkTreeIter *iter)
2231 g_return_val_if_fail (iter->stamp == TREE_MODEL_STAMP, FALSE);
2233 path = gtk_tree_path_new ();
2234 gtk_tree_path_append_index (path, GPOINTER_TO_INT (iter->user_data));
2240 tree_model_get_value (GtkTreeModel *model, GtkTreeIter *iter,
2241 gint column, GValue *value)
2243 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2246 g_return_if_fail (iter->stamp == TREE_MODEL_STAMP);
2248 idx = GPOINTER_TO_INT (iter->user_data);
2249 assert (idx >= 0 && idx < list->line_cnt);
2253 g_value_init (value, G_TYPE_INT);
2254 g_value_set_int (value, idx + list->first_line + 1);
2258 g_value_init (value, G_TYPE_STRING);
2259 g_value_set_static_string (value, ds_cstr (&list->lines[idx]));
2264 tree_model_iter_children (GtkTreeModel *tree_model,
2266 GtkTreeIter *parent)
2272 tree_model_n_children (GtkTreeModel *model, GtkTreeIter *iter)
2274 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2276 return iter == NULL ? list->line_cnt : 0;
2280 tree_model_nth_child (GtkTreeModel *model, GtkTreeIter *iter,
2281 GtkTreeIter *parent, gint n)
2283 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2284 g_return_val_if_fail (IS_TEXT_IMPORT_MODEL (model), FALSE);
2288 return init_iter (list, n, iter);
2292 text_import_model_tree_model_init (gpointer iface_, gpointer data UNUSED)
2294 GtkTreeModelIface *iface = (GtkTreeModelIface *) iface_;
2296 iface->get_flags = tree_model_get_flags;
2297 iface->get_n_columns = tree_model_n_columns;
2298 iface->get_column_type = tree_model_column_type;
2299 iface->get_iter = tree_model_get_iter;
2300 iface->iter_next = tree_model_iter_next;
2301 iface->get_path = tree_model_get_path;
2302 iface->get_value = tree_model_get_value;
2304 iface->iter_children = tree_model_iter_children;
2305 iface->iter_has_child = tree_model_iter_has_child;
2306 iface->iter_n_children = tree_model_n_children;
2307 iface->iter_nth_child = tree_model_nth_child;
2308 iface->iter_parent = tree_model_iter_parent;
2312 text_import_model_iter_to_row (const GtkTreeIter *iter)
2314 assert (iter->stamp == TREE_MODEL_STAMP);
2315 return GPOINTER_TO_INT (iter->user_data);
2318 /* Increments the "watch cursor" level, setting the cursor for
2319 the assistant window to a watch face to indicate to the user
2320 that the ongoing operation may take some time. */
2322 push_watch_cursor (struct import_assistant *ia)
2324 if (++ia->asst.watch_cursor == 1)
2326 GtkWidget *widget = GTK_WIDGET (ia->asst.assistant);
2327 GdkDisplay *display = gtk_widget_get_display (widget);
2328 GdkCursor *cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
2329 gdk_window_set_cursor (widget->window, cursor);
2330 gdk_cursor_unref (cursor);
2331 gdk_display_flush (display);
2335 /* Decrements the "watch cursor" level. If the level reaches
2336 zero, the cursor is reset to its default shape. */
2338 pop_watch_cursor (struct import_assistant *ia)
2340 if (--ia->asst.watch_cursor == 0)
2342 GtkWidget *widget = GTK_WIDGET (ia->asst.assistant);
2343 gdk_window_set_cursor (widget->window, NULL);