1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2008, 2009, 2010, 2011, 2012 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/dialog-common.h"
39 #include "ui/gui/executor.h"
40 #include "ui/gui/helper.h"
41 #include "ui/gui/builder-wrapper.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/psppire-scanf.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)
530 GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Import Delimited Text Data"),
532 GTK_FILE_CHOOSER_ACTION_OPEN,
533 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
534 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
537 g_object_set (dialog, "local-only", FALSE, NULL);
539 switch (gtk_dialog_run (GTK_DIALOG (dialog)))
541 case GTK_RESPONSE_ACCEPT:
542 file_name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
548 gtk_widget_destroy (dialog);
555 static void close_assistant (struct import_assistant *, int response);
556 static void on_prepare (GtkAssistant *assistant, GtkWidget *page,
557 struct import_assistant *);
558 static void on_cancel (GtkAssistant *assistant, struct import_assistant *);
559 static void on_close (GtkAssistant *assistant, struct import_assistant *);
560 static void on_paste (GtkButton *button, struct import_assistant *);
561 static void on_reset (GtkButton *button, struct import_assistant *);
562 static void close_assistant (struct import_assistant *, int response);
564 /* Initializes IA's asst substructure. PARENT_WINDOW must be the
565 window to use as the assistant window's parent. */
567 init_assistant (struct import_assistant *ia, GtkWindow *parent_window)
569 struct assistant *a = &ia->asst;
571 a->builder = builder_new ("text-data-import.ui");
572 a->assistant = GTK_ASSISTANT (gtk_assistant_new ());
573 g_signal_connect (a->assistant, "prepare", G_CALLBACK (on_prepare), ia);
574 g_signal_connect (a->assistant, "cancel", G_CALLBACK (on_cancel), ia);
575 g_signal_connect (a->assistant, "close", G_CALLBACK (on_close), ia);
576 a->paste_button = gtk_button_new_from_stock (GTK_STOCK_PASTE);
577 gtk_assistant_add_action_widget (a->assistant, a->paste_button);
578 g_signal_connect (a->paste_button, "clicked", G_CALLBACK (on_paste), ia);
579 a->reset_button = gtk_button_new_from_stock ("pspp-stock-reset");
580 gtk_assistant_add_action_widget (a->assistant, a->reset_button);
581 g_signal_connect (a->reset_button, "clicked", G_CALLBACK (on_reset), ia);
582 gtk_window_set_title (GTK_WINDOW (a->assistant),
583 _("Importing Delimited Text Data"));
584 gtk_window_set_transient_for (GTK_WINDOW (a->assistant), parent_window);
585 gtk_window_set_icon_name (GTK_WINDOW (a->assistant), "pspp");
587 a->prop_renderer = gtk_cell_renderer_text_new ();
588 g_object_ref_sink (a->prop_renderer);
589 a->fixed_renderer = gtk_cell_renderer_text_new ();
590 g_object_ref_sink (a->fixed_renderer);
591 g_object_set (G_OBJECT (a->fixed_renderer),
592 "family", "Monospace",
596 /* Frees IA's asst substructure. */
598 destroy_assistant (struct import_assistant *ia)
600 struct assistant *a = &ia->asst;
602 g_object_unref (a->prop_renderer);
603 g_object_unref (a->fixed_renderer);
604 g_object_unref (a->builder);
607 /* Appends a page of the given TYPE, with PAGE as its content, to
608 the GtkAssistant encapsulated by IA. Returns the GtkWidget
609 that represents the page. */
611 add_page_to_assistant (struct import_assistant *ia,
612 GtkWidget *page, GtkAssistantPageType type)
618 title = gtk_window_get_title (GTK_WINDOW (page));
619 title_copy = xstrdup (title ? title : "");
621 content = gtk_bin_get_child (GTK_BIN (page));
623 g_object_ref (content);
624 gtk_container_remove (GTK_CONTAINER (page), content);
626 gtk_widget_destroy (page);
628 gtk_assistant_append_page (ia->asst.assistant, content);
629 gtk_assistant_set_page_type (ia->asst.assistant, content, type);
630 gtk_assistant_set_page_title (ia->asst.assistant, content, title_copy);
631 gtk_assistant_set_page_complete (ia->asst.assistant, content, true);
638 /* Called just before PAGE is displayed as the current page of
639 ASSISTANT, this updates IA content according to the new
642 on_prepare (GtkAssistant *assistant, GtkWidget *page,
643 struct import_assistant *ia)
646 if (gtk_assistant_get_page_type (assistant, page)
647 == GTK_ASSISTANT_PAGE_CONFIRM)
648 gtk_widget_grab_focus (assistant->apply);
650 gtk_widget_grab_focus (assistant->forward);
652 if (page == ia->separators.page)
653 prepare_separators_page (ia);
654 else if (page == ia->formats.page)
655 prepare_formats_page (ia);
657 gtk_widget_show (ia->asst.reset_button);
658 if (page == ia->formats.page)
659 gtk_widget_show (ia->asst.paste_button);
661 gtk_widget_hide (ia->asst.paste_button);
664 /* Called when the Cancel button in the assistant is clicked. */
666 on_cancel (GtkAssistant *assistant, struct import_assistant *ia)
668 close_assistant (ia, GTK_RESPONSE_CANCEL);
671 /* Called when the Apply button on the last page of the assistant
674 on_close (GtkAssistant *assistant, struct import_assistant *ia)
676 close_assistant (ia, GTK_RESPONSE_APPLY);
679 /* Called when the Paste button on the last page of the assistant
682 on_paste (GtkButton *button, struct import_assistant *ia)
684 close_assistant (ia, PSPPIRE_RESPONSE_PASTE);
687 /* Called when the Reset button is clicked. */
689 on_reset (GtkButton *button, struct import_assistant *ia)
691 gint page_num = gtk_assistant_get_current_page (ia->asst.assistant);
692 GtkWidget *page = gtk_assistant_get_nth_page (ia->asst.assistant, page_num);
694 if (page == ia->intro.page)
695 reset_intro_page (ia);
696 else if (page == ia->first_line.page)
697 reset_first_line_page (ia);
698 else if (page == ia->separators.page)
699 reset_separators_page (ia);
700 else if (page == ia->formats.page)
701 reset_formats_page (ia);
704 /* Causes the assistant to close, returning RESPONSE for
705 interpretation by text_data_import_assistant. */
707 close_assistant (struct import_assistant *ia, int response)
709 ia->asst.response = response;
710 g_main_loop_quit (ia->asst.main_loop);
711 gtk_widget_hide (GTK_WIDGET (ia->asst.assistant));
714 /* The "intro" page of the assistant. */
716 static void on_intro_amount_changed (struct import_assistant *);
718 /* Initializes IA's intro substructure. */
720 init_intro_page (struct import_assistant *ia)
722 GtkBuilder *builder = ia->asst.builder;
723 struct intro_page *p = &ia->intro;
725 GtkWidget *hbox_n_cases ;
726 GtkWidget *hbox_percent ;
730 p->n_cases_spin = gtk_spin_button_new_with_range (0, INT_MAX, 100);
732 hbox_n_cases = psppire_scanf_new (_("Only the first %4d cases"), &p->n_cases_spin);
734 table = get_widget_assert (builder, "button-table");
736 gtk_table_attach_defaults (GTK_TABLE (table), hbox_n_cases,
740 p->percent_spin = gtk_spin_button_new_with_range (0, 100, 10);
742 hbox_percent = psppire_scanf_new (_("Only the first %3d %% of file (approximately)"), &p->percent_spin);
744 gtk_table_attach_defaults (GTK_TABLE (table), hbox_percent,
748 p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Intro"),
749 GTK_ASSISTANT_PAGE_INTRO);
751 p->all_cases_button = get_widget_assert (builder, "import-all-cases");
753 p->n_cases_button = get_widget_assert (builder, "import-n-cases");
755 p->percent_button = get_widget_assert (builder, "import-percent");
757 g_signal_connect_swapped (p->all_cases_button, "toggled",
758 G_CALLBACK (on_intro_amount_changed), ia);
759 g_signal_connect_swapped (p->n_cases_button, "toggled",
760 G_CALLBACK (on_intro_amount_changed), ia);
761 g_signal_connect_swapped (p->percent_button, "toggled",
762 G_CALLBACK (on_intro_amount_changed), ia);
764 on_intro_amount_changed (ia);
767 ds_put_cstr (&s, _("This assistant will guide you through the process of "
768 "importing data into PSPP from a text file with one line "
769 "per case, in which fields are separated by tabs, "
770 "commas, or other delimiters.\n\n"));
771 if (ia->file.total_is_exact)
773 &s, ngettext ("The selected file contains %zu line of text. ",
774 "The selected file contains %zu lines of text. ",
777 else if (ia->file.total_lines > 0)
781 "The selected file contains approximately %lu line of text. ",
782 "The selected file contains approximately %lu lines of text. ",
783 ia->file.total_lines),
784 ia->file.total_lines);
787 "Only the first %zu line of the file will be shown for "
788 "preview purposes in the following screens. ",
789 "Only the first %zu lines of the file will be shown for "
790 "preview purposes in the following screens. ",
794 ds_put_cstr (&s, _("You may choose below how much of the file should "
795 "actually be imported."));
796 gtk_label_set_text (GTK_LABEL (get_widget_assert (builder, "intro-label")),
801 /* Resets IA's intro page to its initial state. */
803 reset_intro_page (struct import_assistant *ia)
805 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->intro.all_cases_button),
809 /* Called when one of the radio buttons is clicked. */
811 on_intro_amount_changed (struct import_assistant *ia)
813 struct intro_page *p = &ia->intro;
815 gtk_widget_set_sensitive (p->n_cases_spin,
816 gtk_toggle_button_get_active (
817 GTK_TOGGLE_BUTTON (p->n_cases_button)));
819 gtk_widget_set_sensitive (p->percent_spin,
820 gtk_toggle_button_get_active (
821 GTK_TOGGLE_BUTTON (p->percent_button)));
824 /* The "first line" page of the assistant. */
826 static GtkTreeView *create_lines_tree_view (GtkContainer *parent_window,
827 struct import_assistant *);
828 static void on_first_line_change (GtkTreeSelection *,
829 struct import_assistant *);
830 static void on_variable_names_cb_toggle (GtkToggleButton *,
831 struct import_assistant *);
832 static void set_first_line (struct import_assistant *);
833 static void get_first_line (struct import_assistant *);
835 /* Initializes IA's first_line substructure. */
837 init_first_line_page (struct import_assistant *ia)
839 struct first_line_page *p = &ia->first_line;
840 GtkBuilder *builder = ia->asst.builder;
842 p->page = add_page_to_assistant (ia, get_widget_assert (builder, "FirstLine"),
843 GTK_ASSISTANT_PAGE_CONTENT);
844 gtk_widget_destroy (get_widget_assert (builder, "first-line"));
845 p->tree_view = create_lines_tree_view (
846 GTK_CONTAINER (get_widget_assert (builder, "first-line-scroller")), ia);
847 p->variable_names_cb = get_widget_assert (builder, "variable-names");
848 gtk_tree_selection_set_mode (
849 gtk_tree_view_get_selection (GTK_TREE_VIEW (p->tree_view)),
850 GTK_SELECTION_BROWSE);
852 g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (p->tree_view)),
853 "changed", G_CALLBACK (on_first_line_change), ia);
854 g_signal_connect (p->variable_names_cb, "toggled",
855 G_CALLBACK (on_variable_names_cb_toggle), ia);
858 /* Resets the first_line page to its initial content. */
860 reset_first_line_page (struct import_assistant *ia)
862 ia->first_line.skip_lines = 0;
863 ia->first_line.variable_names = false;
867 /* Creates and returns a tree view that contains each of the
868 lines in IA's file as a row. */
870 create_lines_tree_view (GtkContainer *parent, struct import_assistant *ia)
872 GtkTreeView *tree_view;
873 GtkTreeViewColumn *column;
874 size_t max_line_length;
875 gint content_width, header_width;
877 const gchar *title = _("Text");
879 make_tree_view (ia, 0, &tree_view);
881 column = gtk_tree_view_column_new_with_attributes
883 title, ia->asst.fixed_renderer,
884 "text", TEXT_IMPORT_MODEL_COLUMN_LINE,
887 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
890 for (i = 0; i < ia->file.line_cnt; i++)
892 size_t w = ds_length (&ia->file.lines[i]);
893 max_line_length = MAX (max_line_length, w);
896 content_width = get_monospace_width (tree_view, ia->asst.fixed_renderer,
898 header_width = get_string_width (tree_view, ia->asst.prop_renderer, title);
899 gtk_tree_view_column_set_fixed_width (column, MAX (content_width,
901 gtk_tree_view_append_column (tree_view, column);
903 gtk_tree_view_set_fixed_height_mode (tree_view, true);
905 gtk_container_add (parent, GTK_WIDGET (tree_view));
906 gtk_widget_show (GTK_WIDGET (tree_view));
911 /* Called when the line selected in the first_line tree view
914 on_first_line_change (GtkTreeSelection *selection UNUSED,
915 struct import_assistant *ia)
920 /* Called when the checkbox that indicates whether variable
921 names are in the row above the first line is toggled. */
923 on_variable_names_cb_toggle (GtkToggleButton *variable_names_cb UNUSED,
924 struct import_assistant *ia)
929 /* Sets the widgets to match IA's first_line substructure. */
931 set_first_line (struct import_assistant *ia)
935 path = gtk_tree_path_new_from_indices (ia->first_line.skip_lines, -1);
936 gtk_tree_view_set_cursor (GTK_TREE_VIEW (ia->first_line.tree_view),
938 gtk_tree_path_free (path);
940 gtk_toggle_button_set_active (
941 GTK_TOGGLE_BUTTON (ia->first_line.variable_names_cb),
942 ia->first_line.variable_names);
943 gtk_widget_set_sensitive (ia->first_line.variable_names_cb,
944 ia->first_line.skip_lines > 0);
947 /* Sets IA's first_line substructure to match the widgets. */
949 get_first_line (struct import_assistant *ia)
951 GtkTreeSelection *selection;
955 selection = gtk_tree_view_get_selection (ia->first_line.tree_view);
956 if (gtk_tree_selection_get_selected (selection, &model, &iter))
958 GtkTreePath *path = gtk_tree_model_get_path (model, &iter);
959 int row = gtk_tree_path_get_indices (path)[0];
960 gtk_tree_path_free (path);
962 ia->first_line.skip_lines = row;
963 ia->first_line.variable_names =
964 (ia->first_line.skip_lines > 0
965 && gtk_toggle_button_get_active (
966 GTK_TOGGLE_BUTTON (ia->first_line.variable_names_cb)));
968 gtk_widget_set_sensitive (ia->first_line.variable_names_cb,
969 ia->first_line.skip_lines > 0);
972 /* The "separators" page of the assistant. */
974 static void revise_fields_preview (struct import_assistant *ia);
975 static void choose_likely_separators (struct import_assistant *ia);
976 static void find_commonest_chars (unsigned long int histogram[UCHAR_MAX + 1],
977 const char *targets, const char *def,
978 struct string *result);
979 static void clear_fields (struct import_assistant *ia);
980 static void revise_fields_preview (struct import_assistant *);
981 static void set_separators (struct import_assistant *);
982 static void get_separators (struct import_assistant *);
983 static void on_separators_custom_entry_notify (GObject *UNUSED,
985 struct import_assistant *);
986 static void on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
987 struct import_assistant *);
988 static void on_quote_combo_change (GtkComboBox *combo,
989 struct import_assistant *);
990 static void on_quote_cb_toggle (GtkToggleButton *quote_cb,
991 struct import_assistant *);
992 static void on_separator_toggle (GtkToggleButton *, struct import_assistant *);
993 static void render_input_cell (GtkTreeViewColumn *tree_column,
994 GtkCellRenderer *cell,
995 GtkTreeModel *model, GtkTreeIter *iter,
997 static gboolean on_query_input_tooltip (GtkWidget *widget, gint wx, gint wy,
998 gboolean keyboard_mode UNUSED,
1000 struct import_assistant *);
1002 /* A common field separator and its identifying name. */
1005 const char *name; /* Name (for use with get_widget_assert). */
1006 int c; /* Separator character. */
1009 /* All the separators in the dialog box. */
1010 static const struct separator separators[] =
1017 {N_("hyphen"), '-'},
1019 {N_("semicolon"), ';'},
1022 #define SEPARATOR_CNT (sizeof separators / sizeof *separators)
1025 set_quote_list (GtkComboBoxEntry *cb)
1027 GtkListStore *list = gtk_list_store_new (1, G_TYPE_STRING);
1030 const gchar *seperator[3] = {"'\"", "\'", "\""};
1032 for (i = 0; i < 3; i++)
1034 const gchar *s = seperator[i];
1036 /* Add a new row to the model */
1037 gtk_list_store_append (list, &iter);
1038 gtk_list_store_set (list, &iter,
1044 gtk_combo_box_set_model (GTK_COMBO_BOX (cb), GTK_TREE_MODEL (list));
1046 gtk_combo_box_entry_set_text_column (cb, 0);
1049 /* Initializes IA's separators substructure. */
1051 init_separators_page (struct import_assistant *ia)
1053 GtkBuilder *builder = ia->asst.builder;
1054 struct separators_page *p = &ia->separators;
1057 choose_likely_separators (ia);
1059 p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Separators"),
1060 GTK_ASSISTANT_PAGE_CONTENT);
1061 p->custom_cb = get_widget_assert (builder, "custom-cb");
1062 p->custom_entry = get_widget_assert (builder, "custom-entry");
1063 p->quote_combo = get_widget_assert (builder, "quote-combo");
1064 p->quote_entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (p->quote_combo)));
1065 p->quote_cb = get_widget_assert (builder, "quote-cb");
1066 p->escape_cb = get_widget_assert (builder, "escape");
1068 set_separators (ia);
1069 set_quote_list (GTK_COMBO_BOX_ENTRY (p->quote_combo));
1070 p->fields_tree_view = GTK_TREE_VIEW (get_widget_assert (builder, "fields"));
1071 g_signal_connect (p->quote_combo, "changed",
1072 G_CALLBACK (on_quote_combo_change), ia);
1073 g_signal_connect (p->quote_cb, "toggled",
1074 G_CALLBACK (on_quote_cb_toggle), ia);
1075 g_signal_connect (p->custom_entry, "notify::text",
1076 G_CALLBACK (on_separators_custom_entry_notify), ia);
1077 g_signal_connect (p->custom_cb, "toggled",
1078 G_CALLBACK (on_separators_custom_cb_toggle), ia);
1079 for (i = 0; i < SEPARATOR_CNT; i++)
1080 g_signal_connect (get_widget_assert (builder, gettext(separators[i].name)),
1081 "toggled", G_CALLBACK (on_separator_toggle), ia);
1082 g_signal_connect (p->escape_cb, "toggled",
1083 G_CALLBACK (on_separator_toggle), ia);
1086 /* Frees IA's separators substructure. */
1088 destroy_separators_page (struct import_assistant *ia)
1090 struct separators_page *s = &ia->separators;
1092 ds_destroy (&s->separators);
1093 ds_destroy (&s->quotes);
1097 /* Called just before the separators page becomes visible in the
1100 prepare_separators_page (struct import_assistant *ia)
1102 revise_fields_preview (ia);
1105 /* Called when the Reset button is clicked on the separators
1106 page, resets the separators to the defaults. */
1108 reset_separators_page (struct import_assistant *ia)
1110 choose_likely_separators (ia);
1111 set_separators (ia);
1114 /* Frees and clears the column data in IA's separators
1117 clear_fields (struct import_assistant *ia)
1119 struct separators_page *s = &ia->separators;
1121 if (s->column_cnt > 0)
1126 for (row = 0; row < ia->file.line_cnt; row++)
1128 const struct string *line = &ia->file.lines[row];
1129 const char *line_start = ds_data (line);
1130 const char *line_end = ds_end (line);
1132 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1134 char *s = ss_data (col->contents[row]);
1135 if (!(s >= line_start && s <= line_end))
1136 ss_dealloc (&col->contents[row]);
1140 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1143 free (col->contents);
1152 /* Breaks the file data in IA into columns based on the
1153 separators set in IA's separators substructure. */
1155 split_fields (struct import_assistant *ia)
1157 struct separators_page *s = &ia->separators;
1158 size_t columns_allocated;
1164 /* Is space in the set of separators? */
1165 space_sep = ss_find_byte (ds_ss (&s->separators), ' ') != SIZE_MAX;
1167 /* Split all the lines, not just those from
1168 ia->first_line.skip_lines on, so that we split the line that
1169 contains variables names if ia->first_line.variable_names is
1171 columns_allocated = 0;
1172 for (row = 0; row < ia->file.line_cnt; row++)
1174 struct string *line = &ia->file.lines[row];
1175 struct substring text = ds_ss (line);
1178 for (column_idx = 0; ; column_idx++)
1180 struct substring field;
1181 struct column *column;
1184 ss_ltrim (&text, ss_cstr (" "));
1185 if (ss_is_empty (text))
1187 if (column_idx != 0)
1191 else if (!ds_is_empty (&s->quotes)
1192 && ds_find_byte (&s->quotes, text.string[0]) != SIZE_MAX)
1194 int quote = ss_get_byte (&text);
1196 ss_get_until (&text, quote, &field);
1203 while ((c = ss_get_byte (&text)) != EOF)
1205 ds_put_byte (&s, c);
1206 else if (ss_match_byte (&text, quote))
1207 ds_put_byte (&s, quote);
1214 ss_get_bytes (&text, ss_cspan (text, ds_ss (&s->separators)),
1217 if (column_idx >= s->column_cnt)
1219 struct column *column;
1221 if (s->column_cnt >= columns_allocated)
1222 s->columns = x2nrealloc (s->columns, &columns_allocated,
1223 sizeof *s->columns);
1224 column = &s->columns[s->column_cnt++];
1225 column->name = NULL;
1227 column->contents = xcalloc (ia->file.line_cnt,
1228 sizeof *column->contents);
1230 column = &s->columns[column_idx];
1231 column->contents[row] = field;
1232 if (ss_length (field) > column->width)
1233 column->width = ss_length (field);
1236 ss_ltrim (&text, ss_cstr (" "));
1237 if (ss_is_empty (text))
1239 if (ss_find_byte (ds_ss (&s->separators), ss_first (text))
1241 ss_advance (&text, 1);
1246 /* Chooses a name for each column on the separators page */
1248 choose_column_names (struct import_assistant *ia)
1250 const struct first_line_page *f = &ia->first_line;
1251 struct separators_page *s = &ia->separators;
1252 struct dictionary *dict;
1253 unsigned long int generated_name_count = 0;
1257 dict = dict_create (get_default_encoding ());
1258 name_row = f->variable_names && f->skip_lines ? f->skip_lines : 0;
1259 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1263 hint = name_row ? ss_xstrdup (col->contents[name_row - 1]) : NULL;
1264 name = dict_make_unique_var_name (dict, hint, &generated_name_count);
1268 dict_create_var_assert (dict, name, 0);
1270 dict_destroy (dict);
1273 /* Picks the most likely separator and quote characters based on
1276 choose_likely_separators (struct import_assistant *ia)
1278 unsigned long int histogram[UCHAR_MAX + 1] = { 0 };
1281 /* Construct a histogram of all the characters used in the
1283 for (row = 0; row < ia->file.line_cnt; row++)
1285 struct substring line = ds_ss (&ia->file.lines[row]);
1286 size_t length = ss_length (line);
1288 for (i = 0; i < length; i++)
1289 histogram[(unsigned char) line.string[i]]++;
1292 find_commonest_chars (histogram, "\"'", "", &ia->separators.quotes);
1293 find_commonest_chars (histogram, ",;:/|!\t-", ",",
1294 &ia->separators.separators);
1295 ia->separators.escape = true;
1298 /* Chooses the most common character among those in TARGETS,
1299 based on the frequency data in HISTOGRAM, and stores it in
1300 RESULT. If there is a tie for the most common character among
1301 those in TARGETS, the earliest character is chosen. If none
1302 of the TARGETS appear at all, then DEF is used as a
1305 find_commonest_chars (unsigned long int histogram[UCHAR_MAX + 1],
1306 const char *targets, const char *def,
1307 struct string *result)
1309 unsigned char max = 0;
1310 unsigned long int max_count = 0;
1312 for (; *targets != '\0'; targets++)
1314 unsigned char c = *targets;
1315 unsigned long int count = histogram[c];
1316 if (count > max_count)
1325 ds_put_byte (result, max);
1328 ds_assign_cstr (result, def);
1331 /* Revises the contents of the fields tree view based on the
1332 currently chosen set of separators. */
1334 revise_fields_preview (struct import_assistant *ia)
1338 push_watch_cursor (ia);
1340 w = GTK_WIDGET (ia->separators.fields_tree_view);
1341 gtk_widget_destroy (w);
1342 get_separators (ia);
1344 choose_column_names (ia);
1345 ia->separators.fields_tree_view = create_data_tree_view (
1347 GTK_CONTAINER (get_widget_assert (ia->asst.builder, "fields-scroller")),
1350 pop_watch_cursor (ia);
1353 /* Sets the widgets to match IA's separators substructure. */
1355 set_separators (struct import_assistant *ia)
1357 struct separators_page *s = &ia->separators;
1359 struct string custom;
1364 ds_init_empty (&custom);
1366 for (i = 0; i < ds_length (&s->separators); i++)
1368 unsigned char c = ds_at (&s->separators, i);
1371 for (j = 0; j < SEPARATOR_CNT; j++)
1373 const struct separator *s = &separators[j];
1381 ds_put_byte (&custom, c);
1385 for (i = 0; i < SEPARATOR_CNT; i++)
1387 const struct separator *s = &separators[i];
1388 GtkWidget *button = get_widget_assert (ia->asst.builder, s->name);
1389 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
1390 (seps & (1u << i)) != 0);
1392 any_custom = !ds_is_empty (&custom);
1393 gtk_entry_set_text (GTK_ENTRY (s->custom_entry), ds_cstr (&custom));
1394 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->custom_cb),
1396 gtk_widget_set_sensitive (s->custom_entry, any_custom);
1397 ds_destroy (&custom);
1399 any_quotes = !ds_is_empty (&s->quotes);
1401 gtk_entry_set_text (s->quote_entry,
1402 any_quotes ? ds_cstr (&s->quotes) : "\"");
1403 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->quote_cb),
1405 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->escape_cb),
1407 gtk_widget_set_sensitive (s->quote_combo, any_quotes);
1408 gtk_widget_set_sensitive (s->escape_cb, any_quotes);
1411 /* Sets IA's separators substructure to match the widgets. */
1413 get_separators (struct import_assistant *ia)
1415 struct separators_page *s = &ia->separators;
1418 ds_clear (&s->separators);
1419 for (i = 0; i < SEPARATOR_CNT; i++)
1421 const struct separator *sep = &separators[i];
1422 GtkWidget *button = get_widget_assert (ia->asst.builder, sep->name);
1423 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
1424 ds_put_byte (&s->separators, sep->c);
1427 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->custom_cb)))
1428 ds_put_cstr (&s->separators,
1429 gtk_entry_get_text (GTK_ENTRY (s->custom_entry)));
1431 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->quote_cb)))
1433 gchar *text = gtk_combo_box_get_active_text (
1434 GTK_COMBO_BOX (s->quote_combo));
1435 ds_assign_cstr (&s->quotes, text);
1439 ds_clear (&s->quotes);
1440 s->escape = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->escape_cb));
1443 /* Called when the user changes the entry field for custom
1446 on_separators_custom_entry_notify (GObject *gobject UNUSED,
1447 GParamSpec *arg1 UNUSED,
1448 struct import_assistant *ia)
1450 revise_fields_preview (ia);
1453 /* Called when the user toggles the checkbox that enables custom
1456 on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
1457 struct import_assistant *ia)
1459 bool is_active = gtk_toggle_button_get_active (custom_cb);
1460 gtk_widget_set_sensitive (ia->separators.custom_entry, is_active);
1461 revise_fields_preview (ia);
1464 /* Called when the user changes the selection in the combo box
1465 that selects a quote character. */
1467 on_quote_combo_change (GtkComboBox *combo, struct import_assistant *ia)
1469 revise_fields_preview (ia);
1472 /* Called when the user toggles the checkbox that enables
1475 on_quote_cb_toggle (GtkToggleButton *quote_cb, struct import_assistant *ia)
1477 bool is_active = gtk_toggle_button_get_active (quote_cb);
1478 gtk_widget_set_sensitive (ia->separators.quote_combo, is_active);
1479 gtk_widget_set_sensitive (ia->separators.escape_cb, is_active);
1480 revise_fields_preview (ia);
1483 /* Called when the user toggles one of the separators
1486 on_separator_toggle (GtkToggleButton *toggle UNUSED,
1487 struct import_assistant *ia)
1489 revise_fields_preview (ia);
1492 /* Called to render one of the cells in the fields preview tree
1495 render_input_cell (GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1496 GtkTreeModel *model, GtkTreeIter *iter,
1499 struct import_assistant *ia = ia_;
1500 struct substring field;
1504 column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1506 row = text_import_model_iter_to_row (iter) + ia->first_line.skip_lines;
1507 field = ia->separators.columns[column].contents[row];
1508 if (field.string != NULL)
1510 GValue text = {0, };
1511 g_value_init (&text, G_TYPE_STRING);
1512 g_value_take_string (&text, ss_xstrdup (field));
1513 g_object_set_property (G_OBJECT (cell), "text", &text);
1514 g_value_unset (&text);
1515 g_object_set (cell, "background-set", FALSE, (void *) NULL);
1520 "background", "red",
1521 "background-set", TRUE,
1525 /* Called to render a tooltip on one of the cells in the fields
1526 preview tree view. */
1528 on_query_input_tooltip (GtkWidget *widget, gint wx, gint wy,
1529 gboolean keyboard_mode UNUSED,
1530 GtkTooltip *tooltip, struct import_assistant *ia)
1534 if (!get_tooltip_location (widget, wx, wy, ia, &row, &column))
1537 if (ia->separators.columns[column].contents[row].string != NULL)
1540 gtk_tooltip_set_text (tooltip,
1541 _("This input line has too few separators "
1542 "to fill in this field."));
1546 /* The "formats" page of the assistant. */
1548 static void on_variable_change (PsppireDict *dict, int idx,
1549 struct import_assistant *);
1550 static void clear_modified_vars (struct import_assistant *);
1552 /* Initializes IA's formats substructure. */
1554 init_formats_page (struct import_assistant *ia)
1556 GtkBuilder *builder = ia->asst.builder;
1557 struct formats_page *p = &ia->formats;
1559 p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Formats"),
1560 GTK_ASSISTANT_PAGE_CONFIRM);
1561 p->data_tree_view = GTK_TREE_VIEW (get_widget_assert (builder, "data"));
1562 p->modified_vars = NULL;
1563 p->modified_var_cnt = 0;
1567 /* Frees IA's formats substructure. */
1569 destroy_formats_page (struct import_assistant *ia)
1571 struct formats_page *p = &ia->formats;
1573 if (p->psppire_dict != NULL)
1575 /* This destroys p->dict also. */
1576 g_object_unref (p->psppire_dict);
1578 clear_modified_vars (ia);
1581 /* Called just before the formats page of the assistant is
1584 prepare_formats_page (struct import_assistant *ia)
1586 struct dictionary *dict;
1587 PsppireDict *psppire_dict;
1588 PsppireVarStore *var_store;
1589 GtkBin *vars_scroller;
1590 GtkWidget *old_var_sheet;
1591 PsppireVarSheet *var_sheet;
1592 struct separators_page *s = &ia->separators;
1593 struct formats_page *p = &ia->formats;
1594 struct fmt_guesser *fg;
1595 unsigned long int number = 0;
1598 push_watch_cursor (ia);
1600 dict = dict_create (get_default_encoding ());
1601 fg = fmt_guesser_create ();
1602 for (column_idx = 0; column_idx < s->column_cnt; column_idx++)
1604 struct variable *modified_var;
1606 modified_var = (column_idx < p->modified_var_cnt
1607 ? p->modified_vars[column_idx] : NULL);
1608 if (modified_var == NULL)
1610 struct column *column = &s->columns[column_idx];
1611 struct variable *var;
1612 struct fmt_spec format;
1616 /* Choose variable name. */
1617 name = dict_make_unique_var_name (dict, column->name, &number);
1619 /* Choose variable format. */
1620 fmt_guesser_clear (fg);
1621 for (row = ia->first_line.skip_lines; row < ia->file.line_cnt; row++)
1622 fmt_guesser_add (fg, column->contents[row]);
1623 fmt_guesser_guess (fg, &format);
1624 fmt_fix_input (&format);
1626 /* Create variable. */
1627 var = dict_create_var_assert (dict, name, fmt_var_width (&format));
1628 var_set_both_formats (var, &format);
1636 name = dict_make_unique_var_name (dict, var_get_name (modified_var),
1638 dict_clone_var_as_assert (dict, modified_var, name);
1642 fmt_guesser_destroy (fg);
1644 psppire_dict = psppire_dict_new_from_dict (dict);
1645 g_signal_connect (psppire_dict, "variable_changed",
1646 G_CALLBACK (on_variable_change), ia);
1647 ia->formats.dict = dict;
1648 ia->formats.psppire_dict = psppire_dict;
1650 /* XXX: PsppireVarStore doesn't hold a reference to
1651 psppire_dict for now, but it should. After it does, we
1652 should g_object_ref the psppire_dict here, since we also
1653 hold a reference via ia->formats.dict. */
1654 var_store = psppire_var_store_new (psppire_dict);
1655 g_object_set (var_store,
1656 "format-type", PSPPIRE_VAR_STORE_INPUT_FORMATS,
1658 var_sheet = PSPPIRE_VAR_SHEET (psppire_var_sheet_new ());
1659 g_object_set (var_sheet,
1661 "may-create-vars", FALSE,
1664 vars_scroller = GTK_BIN (get_widget_assert (ia->asst.builder, "vars-scroller"));
1665 old_var_sheet = gtk_bin_get_child (vars_scroller);
1666 if (old_var_sheet != NULL)
1667 gtk_widget_destroy (old_var_sheet);
1668 gtk_container_add (GTK_CONTAINER (vars_scroller), GTK_WIDGET (var_sheet));
1669 gtk_widget_show (GTK_WIDGET (var_sheet));
1671 gtk_widget_destroy (GTK_WIDGET (ia->formats.data_tree_view));
1672 ia->formats.data_tree_view = create_data_tree_view (
1674 GTK_CONTAINER (get_widget_assert (ia->asst.builder, "data-scroller")),
1677 pop_watch_cursor (ia);
1680 /* Clears the set of user-modified variables from IA's formats
1681 substructure. This discards user modifications to variable
1682 formats, thereby causing formats to revert to their
1685 clear_modified_vars (struct import_assistant *ia)
1687 struct formats_page *p = &ia->formats;
1690 for (i = 0; i < p->modified_var_cnt; i++)
1691 var_destroy (p->modified_vars[i]);
1692 free (p->modified_vars);
1693 p->modified_vars = NULL;
1694 p->modified_var_cnt = 0;
1697 /* Resets the formats page to its defaults, discarding user
1700 reset_formats_page (struct import_assistant *ia)
1702 clear_modified_vars (ia);
1703 prepare_formats_page (ia);
1706 /* Called when the user changes one of the variables in the
1709 on_variable_change (PsppireDict *dict, int dict_idx,
1710 struct import_assistant *ia)
1712 struct formats_page *p = &ia->formats;
1713 GtkTreeView *tv = ia->formats.data_tree_view;
1714 gint column_idx = dict_idx + 1;
1716 push_watch_cursor (ia);
1718 /* Remove previous column and replace with new column. */
1719 gtk_tree_view_remove_column (tv, gtk_tree_view_get_column (tv, column_idx));
1720 gtk_tree_view_insert_column (tv, make_data_column (ia, tv, false, dict_idx),
1723 /* Save a copy of the modified variable in modified_vars, so
1724 that its attributes will be preserved if we back up to the
1725 previous page with the Prev button and then come back
1727 if (dict_idx >= p->modified_var_cnt)
1730 p->modified_vars = xnrealloc (p->modified_vars, dict_idx + 1,
1731 sizeof *p->modified_vars);
1732 for (i = 0; i <= dict_idx; i++)
1733 p->modified_vars[i] = NULL;
1734 p->modified_var_cnt = dict_idx + 1;
1736 if (p->modified_vars[dict_idx])
1737 var_destroy (p->modified_vars[dict_idx]);
1738 p->modified_vars[dict_idx]
1739 = var_clone (psppire_dict_get_variable (dict, dict_idx));
1741 pop_watch_cursor (ia);
1744 /* Parses the contents of the field at (ROW,COLUMN) according to
1745 its variable format. If OUTPUTP is non-null, then *OUTPUTP
1746 receives the formatted output for that field (which must be
1747 freed with free). If TOOLTIPP is non-null, then *TOOLTIPP
1748 receives a message suitable for use in a tooltip, if one is
1749 needed, or a null pointer otherwise. Returns true if a
1750 tooltip message is needed, otherwise false. */
1752 parse_field (struct import_assistant *ia,
1753 size_t row, size_t column,
1754 char **outputp, char **tooltipp)
1756 struct substring field;
1758 struct variable *var;
1759 const struct fmt_spec *in;
1760 struct fmt_spec out;
1764 field = ia->separators.columns[column].contents[row];
1765 var = dict_get_var (ia->formats.dict, column);
1766 value_init (&val, var_get_width (var));
1767 in = var_get_print_format (var);
1768 out = fmt_for_output_from_input (in);
1770 if (field.string != NULL)
1774 error = data_in (field, C_ENCODING, in->type, &val, var_get_width (var),
1775 dict_get_encoding (ia->formats.dict));
1778 tooltip = xasprintf (_("Cannot parse field content `%.*s' as "
1780 (int) field.length, field.string,
1781 fmt_name (in->type), error);
1787 tooltip = xstrdup (_("This input line has too few separators "
1788 "to fill in this field."));
1789 value_set_missing (&val, var_get_width (var));
1791 if (outputp != NULL)
1793 *outputp = data_out (&val, dict_get_encoding (ia->formats.dict), &out);
1795 value_destroy (&val, var_get_width (var));
1797 ok = tooltip == NULL;
1798 if (tooltipp != NULL)
1799 *tooltipp = tooltip;
1805 /* Called to render one of the cells in the data preview tree
1808 render_output_cell (GtkTreeViewColumn *tree_column,
1809 GtkCellRenderer *cell,
1810 GtkTreeModel *model,
1814 struct import_assistant *ia = ia_;
1816 GValue gvalue = { 0, };
1819 ok = parse_field (ia,
1820 (text_import_model_iter_to_row (iter)
1821 + ia->first_line.skip_lines),
1822 GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1826 g_value_init (&gvalue, G_TYPE_STRING);
1827 g_value_take_string (&gvalue, output);
1828 g_object_set_property (G_OBJECT (cell), "text", &gvalue);
1829 g_value_unset (&gvalue);
1832 g_object_set (cell, "background-set", FALSE, (void *) NULL);
1835 "background", "red",
1836 "background-set", TRUE,
1840 /* Called to render a tooltip for one of the cells in the data
1841 preview tree view. */
1843 on_query_output_tooltip (GtkWidget *widget, gint wx, gint wy,
1844 gboolean keyboard_mode UNUSED,
1845 GtkTooltip *tooltip, struct import_assistant *ia)
1850 if (!get_tooltip_location (widget, wx, wy, ia, &row, &column))
1853 if (parse_field (ia, row, column, NULL, &text))
1856 gtk_tooltip_set_text (tooltip, text);
1861 /* Utility functions used by multiple pages of the assistant. */
1864 get_tooltip_location (GtkWidget *widget, gint wx, gint wy,
1865 const struct import_assistant *ia,
1866 size_t *row, size_t *column)
1868 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
1872 GtkTreeViewColumn *tree_column;
1873 GtkTreeModel *tree_model;
1876 /* Check that WIDGET is really visible on the screen before we
1877 do anything else. This is a bug fix for a sticky situation:
1878 when text_data_import_assistant() returns, it frees the data
1879 necessary to compose the tool tip message, but there may be
1880 a tool tip under preparation at that point (even if there is
1881 no visible tool tip) that will call back into us a little
1882 bit later. Perhaps the correct solution to this problem is
1883 to make the data related to the tool tips part of a GObject
1884 that only gets destroyed when all references are released,
1885 but this solution appears to be effective too. */
1886 if (!gtk_widget_get_mapped (widget))
1889 gtk_tree_view_convert_widget_to_bin_window_coords (tree_view,
1891 if (!gtk_tree_view_get_path_at_pos (tree_view, bx, by,
1892 &path, &tree_column, NULL, NULL))
1895 *column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1898 tree_model = gtk_tree_view_get_model (tree_view);
1899 ok = gtk_tree_model_get_iter (tree_model, &iter, path);
1900 gtk_tree_path_free (path);
1904 *row = text_import_model_iter_to_row (&iter) + ia->first_line.skip_lines;
1909 make_tree_view (const struct import_assistant *ia,
1911 GtkTreeView **tree_view)
1913 GtkTreeModel *model;
1915 *tree_view = GTK_TREE_VIEW (gtk_tree_view_new ());
1916 model = GTK_TREE_MODEL (text_import_model_new (
1917 ia->file.lines + first_line,
1918 ia->file.line_cnt - first_line, first_line));
1919 gtk_tree_view_set_model (*tree_view, model);
1921 add_line_number_column (ia, *tree_view);
1925 add_line_number_column (const struct import_assistant *ia,
1926 GtkTreeView *treeview)
1928 GtkTreeViewColumn *column;
1930 column = gtk_tree_view_column_new_with_attributes (
1931 _("Line"), ia->asst.prop_renderer,
1932 "text", TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER,
1934 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
1935 gtk_tree_view_column_set_fixed_width (
1936 column, get_monospace_width (treeview, ia->asst.prop_renderer, 5));
1937 gtk_tree_view_append_column (treeview, column);
1941 get_monospace_width (GtkTreeView *treeview, GtkCellRenderer *renderer,
1948 ds_put_byte_multiple (&s, '0', char_cnt);
1949 ds_put_byte (&s, ' ');
1950 width = get_string_width (treeview, renderer, ds_cstr (&s));
1957 get_string_width (GtkTreeView *treeview, GtkCellRenderer *renderer,
1961 g_object_set (G_OBJECT (renderer), "text", string, (void *) NULL);
1962 gtk_cell_renderer_get_size (renderer, GTK_WIDGET (treeview),
1963 NULL, NULL, NULL, &width, NULL);
1967 static GtkTreeViewColumn *
1968 make_data_column (struct import_assistant *ia, GtkTreeView *tree_view,
1969 bool input, gint dict_idx)
1971 struct variable *var = NULL;
1972 struct column *column = NULL;
1974 gint content_width, header_width;
1975 GtkTreeViewColumn *tree_column;
1979 column = &ia->separators.columns[dict_idx];
1981 var = dict_get_var (ia->formats.dict, dict_idx);
1983 name = escape_underscores (input ? column->name : var_get_name (var));
1984 char_cnt = input ? column->width : var_get_print_format (var)->w;
1985 content_width = get_monospace_width (tree_view, ia->asst.fixed_renderer,
1987 header_width = get_string_width (tree_view, ia->asst.prop_renderer,
1990 tree_column = gtk_tree_view_column_new ();
1991 g_object_set_data (G_OBJECT (tree_column), "column-number",
1992 GINT_TO_POINTER (dict_idx));
1993 gtk_tree_view_column_set_title (tree_column, name);
1994 gtk_tree_view_column_pack_start (tree_column, ia->asst.fixed_renderer,
1996 gtk_tree_view_column_set_cell_data_func (
1997 tree_column, ia->asst.fixed_renderer,
1998 input ? render_input_cell : render_output_cell, ia, NULL);
1999 gtk_tree_view_column_set_sizing (tree_column, GTK_TREE_VIEW_COLUMN_FIXED);
2000 gtk_tree_view_column_set_fixed_width (tree_column, MAX (content_width,
2008 static GtkTreeView *
2009 create_data_tree_view (bool input, GtkContainer *parent,
2010 struct import_assistant *ia)
2012 GtkTreeView *tree_view;
2015 make_tree_view (ia, ia->first_line.skip_lines, &tree_view);
2016 gtk_tree_selection_set_mode (gtk_tree_view_get_selection (tree_view),
2017 GTK_SELECTION_NONE);
2019 for (i = 0; i < ia->separators.column_cnt; i++)
2020 gtk_tree_view_append_column (tree_view,
2021 make_data_column (ia, tree_view, input, i));
2023 g_object_set (G_OBJECT (tree_view), "has-tooltip", TRUE, (void *) NULL);
2024 g_signal_connect (tree_view, "query-tooltip",
2025 G_CALLBACK (input ? on_query_input_tooltip
2026 : on_query_output_tooltip), ia);
2027 gtk_tree_view_set_fixed_height_mode (tree_view, true);
2029 gtk_container_add (parent, GTK_WIDGET (tree_view));
2030 gtk_widget_show (GTK_WIDGET (tree_view));
2036 escape_underscores (const char *in)
2038 char *out = xmalloc (2 * strlen (in) + 1);
2042 for (; *in != '\0'; in++)
2053 /* TextImportModel, a GtkTreeModel implementation used by some
2054 pages of the assistant. */
2056 #define G_TYPE_TEXT_IMPORT_MODEL (text_import_model_get_type ())
2057 #define TEXT_IMPORT_MODEL(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), G_TYPE_TEXT_IMPORT_MODEL, TextImportModel))
2058 #define TEXT_IMPORT_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), G_TYPE_TEXT_IMPORT_MODEL, TextImportModelClass))
2059 #define IS_TEXT_IMPORT_MODEL(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), G_TYPE_TEXT_IMPORT_MODEL))
2060 #define IS_TEXT_IMPORT_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), G_TYPE_TEXT_IMPORT_MODEL))
2061 #define TEXT_IMPORT_MODEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), G_TYPE_TEXT_IMPORT_MODEL, TextImportModelClass))
2063 /* Random number used in 'stamp' member of GtkTreeIter. */
2064 #define TREE_MODEL_STAMP 0x7efd67d3
2066 struct TextImportModel
2069 struct string *lines;
2074 struct TextImportModelClass
2076 GObjectClass parent_class;
2079 GType text_import_model_get_type (void);
2080 static void text_import_model_tree_model_init (gpointer iface, gpointer data);
2083 text_import_model_get_type (void)
2085 static GType object_type = 0;
2089 static const GTypeInfo object_info = {
2090 sizeof (TextImportModelClass),
2091 (GBaseInitFunc) NULL,
2092 (GBaseFinalizeFunc) NULL,
2093 NULL, /* class_init */
2094 NULL, /* class_finalize */
2095 NULL, /* class_data */
2096 sizeof (TextImportModel),
2097 0, /* n_preallocs */
2098 NULL, /* instance_init */
2101 static const GInterfaceInfo tree_model_info = {
2102 text_import_model_tree_model_init,
2107 object_type = g_type_register_static (G_TYPE_OBJECT,
2111 g_type_add_interface_static (object_type, GTK_TYPE_TREE_MODEL,
2121 /* Creates and returns a new TextImportModel that contains the
2122 LINE_CNT lines in LINES. The lines before FIRST_LINE in LINES
2123 are not part of the model, but they are included in the line
2124 numbers in the TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER column.
2126 The caller retains responsibility for freeing LINES and must
2127 ensure that its lifetime and that of the strings that it
2128 contains exceeds that of the TextImportModel. */
2130 text_import_model_new (struct string *lines, size_t line_cnt,
2133 TextImportModel *new_text_import_model
2134 = g_object_new (G_TYPE_TEXT_IMPORT_MODEL, NULL);
2135 new_text_import_model->lines = lines;
2136 new_text_import_model->line_cnt = line_cnt;
2137 new_text_import_model->first_line = first_line;
2138 return new_text_import_model;
2143 tree_model_iter_has_child (GtkTreeModel *tree_model,
2150 tree_model_iter_parent (GtkTreeModel *tree_model,
2157 static GtkTreeModelFlags
2158 tree_model_get_flags (GtkTreeModel *model)
2160 g_return_val_if_fail (IS_TEXT_IMPORT_MODEL (model), (GtkTreeModelFlags) 0);
2162 return GTK_TREE_MODEL_LIST_ONLY | GTK_TREE_MODEL_ITERS_PERSIST;
2167 tree_model_n_columns (GtkTreeModel *model)
2173 tree_model_column_type (GtkTreeModel *model, gint index)
2175 return (index == TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER ? G_TYPE_INT
2176 : index == TEXT_IMPORT_MODEL_COLUMN_LINE ? G_TYPE_STRING
2181 init_iter (TextImportModel *list, gint idx, GtkTreeIter *iter)
2183 if (idx < 0 || idx >= list->line_cnt)
2186 iter->user_data = GINT_TO_POINTER (-1);
2191 iter->stamp = TREE_MODEL_STAMP;
2192 iter->user_data = GINT_TO_POINTER (idx);
2198 tree_model_get_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreePath *path)
2200 gint *indices, depth;
2202 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2204 g_return_val_if_fail (path, FALSE);
2206 indices = gtk_tree_path_get_indices (path);
2207 depth = gtk_tree_path_get_depth (path);
2209 g_return_val_if_fail (depth == 1, FALSE);
2211 return init_iter (list, indices[0], iter);
2216 tree_model_iter_next (GtkTreeModel *model, GtkTreeIter *iter)
2218 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2221 assert (iter->stamp == TREE_MODEL_STAMP);
2223 idx = GPOINTER_TO_INT (iter->user_data);
2224 return init_iter (list, idx == -1 ? -1 : idx + 1, iter);
2227 static GtkTreePath *
2228 tree_model_get_path (GtkTreeModel *model, GtkTreeIter *iter)
2232 g_return_val_if_fail (iter->stamp == TREE_MODEL_STAMP, FALSE);
2234 path = gtk_tree_path_new ();
2235 gtk_tree_path_append_index (path, GPOINTER_TO_INT (iter->user_data));
2241 tree_model_get_value (GtkTreeModel *model, GtkTreeIter *iter,
2242 gint column, GValue *value)
2244 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2247 g_return_if_fail (iter->stamp == TREE_MODEL_STAMP);
2249 idx = GPOINTER_TO_INT (iter->user_data);
2250 assert (idx >= 0 && idx < list->line_cnt);
2254 g_value_init (value, G_TYPE_INT);
2255 g_value_set_int (value, idx + list->first_line + 1);
2259 g_value_init (value, G_TYPE_STRING);
2260 g_value_set_static_string (value, ds_cstr (&list->lines[idx]));
2265 tree_model_iter_children (GtkTreeModel *tree_model,
2267 GtkTreeIter *parent)
2273 tree_model_n_children (GtkTreeModel *model, GtkTreeIter *iter)
2275 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2277 return iter == NULL ? list->line_cnt : 0;
2281 tree_model_nth_child (GtkTreeModel *model, GtkTreeIter *iter,
2282 GtkTreeIter *parent, gint n)
2284 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2285 g_return_val_if_fail (IS_TEXT_IMPORT_MODEL (model), FALSE);
2289 return init_iter (list, n, iter);
2293 text_import_model_tree_model_init (gpointer iface_, gpointer data UNUSED)
2295 GtkTreeModelIface *iface = (GtkTreeModelIface *) iface_;
2297 iface->get_flags = tree_model_get_flags;
2298 iface->get_n_columns = tree_model_n_columns;
2299 iface->get_column_type = tree_model_column_type;
2300 iface->get_iter = tree_model_get_iter;
2301 iface->iter_next = tree_model_iter_next;
2302 iface->get_path = tree_model_get_path;
2303 iface->get_value = tree_model_get_value;
2305 iface->iter_children = tree_model_iter_children;
2306 iface->iter_has_child = tree_model_iter_has_child;
2307 iface->iter_n_children = tree_model_n_children;
2308 iface->iter_nth_child = tree_model_nth_child;
2309 iface->iter_parent = tree_model_iter_parent;
2313 text_import_model_iter_to_row (const GtkTreeIter *iter)
2315 assert (iter->stamp == TREE_MODEL_STAMP);
2316 return GPOINTER_TO_INT (iter->user_data);
2319 /* Increments the "watch cursor" level, setting the cursor for
2320 the assistant window to a watch face to indicate to the user
2321 that the ongoing operation may take some time. */
2323 push_watch_cursor (struct import_assistant *ia)
2325 if (++ia->asst.watch_cursor == 1)
2327 GtkWidget *widget = GTK_WIDGET (ia->asst.assistant);
2328 GdkDisplay *display = gtk_widget_get_display (widget);
2329 GdkCursor *cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
2330 gdk_window_set_cursor (widget->window, cursor);
2331 gdk_cursor_unref (cursor);
2332 gdk_display_flush (display);
2336 /* Decrements the "watch cursor" level. If the level reaches
2337 zero, the cursor is reset to its default shape. */
2339 pop_watch_cursor (struct import_assistant *ia)
2341 if (--ia->asst.watch_cursor == 0)
2343 GtkWidget *widget = GTK_WIDGET (ia->asst.assistant);
2344 gdk_window_set_cursor (widget->window, NULL);