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"
23 #include <gtk-contrib/psppire-sheet.h>
29 #include "data/data-in.h"
30 #include "data/data-out.h"
31 #include "data/format-guesser.h"
32 #include "data/value-labels.h"
33 #include "language/data-io/data-parser.h"
34 #include "language/lexer/lexer.h"
35 #include "libpspp/assertion.h"
36 #include "libpspp/i18n.h"
37 #include "libpspp/line-reader.h"
38 #include "libpspp/message.h"
39 #include "ui/gui/checkbox-treeview.h"
40 #include "ui/gui/dialog-common.h"
41 #include "ui/gui/executor.h"
42 #include "ui/gui/helper.h"
43 #include "ui/gui/builder-wrapper.h"
44 #include "ui/gui/psppire-data-window.h"
45 #include "ui/gui/psppire-dialog.h"
46 #include "ui/gui/psppire-encoding-selector.h"
47 #include "ui/gui/psppire-empty-list-store.h"
48 #include "ui/gui/psppire-var-sheet.h"
49 #include "ui/gui/psppire-var-store.h"
50 #include "ui/gui/psppire-scanf.h"
51 #include "ui/syntax-gen.h"
54 #include "gl/intprops.h"
55 #include "gl/xalloc.h"
58 #define _(msgid) gettext (msgid)
59 #define N_(msgid) msgid
61 struct import_assistant;
63 /* The file to be imported. */
66 char *file_name; /* File name. */
67 gchar *encoding; /* Encoding. */
68 unsigned long int total_lines; /* Number of lines in file. */
69 bool total_is_exact; /* Is total_lines exact (or an estimate)? */
71 /* The first several lines of the file. */
75 static bool init_file (struct import_assistant *, GtkWindow *parent);
76 static void destroy_file (struct import_assistant *);
78 /* The main body of the GTK+ assistant and related data. */
82 GtkAssistant *assistant;
84 GtkWidget *paste_button;
85 GtkWidget *reset_button;
89 GtkCellRenderer *prop_renderer;
90 GtkCellRenderer *fixed_renderer;
92 static void init_assistant (struct import_assistant *, GtkWindow *);
93 static void destroy_assistant (struct import_assistant *);
94 static GtkWidget *add_page_to_assistant (struct import_assistant *,
96 GtkAssistantPageType);
98 /* The introduction page of the assistant. */
102 GtkWidget *all_cases_button;
103 GtkWidget *n_cases_button;
104 GtkWidget *n_cases_spin;
105 GtkWidget *percent_button;
106 GtkWidget *percent_spin;
108 static void init_intro_page (struct import_assistant *);
109 static void reset_intro_page (struct import_assistant *);
111 /* Page where the user chooses the first line of data. */
112 struct first_line_page
114 int skip_lines; /* Number of initial lines to skip? */
115 bool variable_names; /* Variable names above first line of data? */
118 GtkTreeView *tree_view;
119 GtkWidget *variable_names_cb;
121 static void init_first_line_page (struct import_assistant *);
122 static void reset_first_line_page (struct import_assistant *);
124 /* Page where the user chooses field separators. */
125 struct separators_page
127 /* How to break lines into columns. */
128 struct string separators; /* Field separators. */
129 struct string quotes; /* Quote characters. */
130 bool escape; /* Doubled quotes yield a quote mark? */
132 /* The columns produced thereby. */
133 struct column *columns; /* Information about each column. */
134 size_t column_cnt; /* Number of columns. */
137 GtkWidget *custom_cb;
138 GtkWidget *custom_entry;
140 GtkWidget *quote_combo;
141 GtkEntry *quote_entry;
142 GtkWidget *escape_cb;
143 GtkTreeView *fields_tree_view;
145 /* The columns that the separators divide the data into. */
148 /* Variable name for this column. This is the variable name
149 used on the separators page; it can be overridden by the
150 user on the formats page. */
153 /* Maximum length of any row in this column. */
156 /* Contents of this column: contents[row] is the contents for
159 A null substring indicates a missing column for that row
160 (because the line contains an insufficient number of
163 contents[] elements may be substrings of the lines[]
164 strings that represent the whole lines of the file, to
165 save memory. Other elements are dynamically allocated
166 with ss_alloc_substring. */
167 struct substring *contents;
169 static void init_separators_page (struct import_assistant *);
170 static void destroy_separators_page (struct import_assistant *);
171 static void prepare_separators_page (struct import_assistant *);
172 static void reset_separators_page (struct import_assistant *);
174 /* Page where the user verifies and adjusts input formats. */
177 struct dictionary *dict;
180 GtkTreeView *data_tree_view;
181 PsppireDict *psppire_dict;
182 struct variable **modified_vars;
183 size_t modified_var_cnt;
185 static void init_formats_page (struct import_assistant *);
186 static void destroy_formats_page (struct import_assistant *);
187 static void prepare_formats_page (struct import_assistant *);
188 static void reset_formats_page (struct import_assistant *);
190 struct import_assistant
193 struct assistant asst;
194 struct intro_page intro;
195 struct first_line_page first_line;
196 struct separators_page separators;
197 struct formats_page formats;
200 static void apply_dict (const struct dictionary *, struct string *);
201 static char *generate_syntax (const struct import_assistant *);
203 static gboolean get_tooltip_location (GtkWidget *widget, gint wx, gint wy,
204 const struct import_assistant *,
205 size_t *row, size_t *column);
206 static void make_tree_view (const struct import_assistant *ia,
208 GtkTreeView **tree_view);
209 static void add_line_number_column (const struct import_assistant *,
211 static gint get_monospace_width (GtkTreeView *, GtkCellRenderer *,
213 static gint get_string_width (GtkTreeView *, GtkCellRenderer *,
215 static GtkTreeViewColumn *make_data_column (struct import_assistant *,
216 GtkTreeView *, bool input,
218 static GtkTreeView *create_data_tree_view (bool input, GtkContainer *parent,
219 struct import_assistant *);
220 static void push_watch_cursor (struct import_assistant *);
221 static void pop_watch_cursor (struct import_assistant *);
223 /* Pops up the Text Data Import assistant. */
225 text_data_import_assistant (PsppireDataWindow *dw)
227 GtkWindow *parent_window = GTK_WINDOW (dw);
228 struct import_assistant *ia;
230 ia = xzalloc (sizeof *ia);
231 if (!init_file (ia, parent_window))
237 init_assistant (ia, parent_window);
238 init_intro_page (ia);
239 init_first_line_page (ia);
240 init_separators_page (ia);
241 init_formats_page (ia);
243 gtk_widget_show_all (GTK_WIDGET (ia->asst.assistant));
245 ia->asst.main_loop = g_main_loop_new (NULL, false);
246 g_main_loop_run (ia->asst.main_loop);
247 g_main_loop_unref (ia->asst.main_loop);
249 switch (ia->asst.response)
251 case GTK_RESPONSE_APPLY:
252 free (execute_syntax_string (dw, generate_syntax (ia)));
254 case PSPPIRE_RESPONSE_PASTE:
255 free (paste_syntax_to_window (generate_syntax (ia)));
261 destroy_formats_page (ia);
262 destroy_separators_page (ia);
263 destroy_assistant (ia);
268 /* Emits PSPP syntax to S that applies the dictionary attributes
269 (such as missing values and value labels) of the variables in
272 apply_dict (const struct dictionary *dict, struct string *s)
274 size_t var_cnt = dict_get_var_cnt (dict);
277 for (i = 0; i < var_cnt; i++)
279 struct variable *var = dict_get_var (dict, i);
280 const char *name = var_get_name (var);
281 enum val_type type = var_get_type (var);
282 int width = var_get_width (var);
283 enum measure measure = var_get_measure (var);
284 enum alignment alignment = var_get_alignment (var);
285 const struct fmt_spec *format = var_get_print_format (var);
287 if (var_has_missing_values (var))
289 const struct missing_values *mv = var_get_missing_values (var);
292 syntax_gen_pspp (s, "MISSING VALUES %ss (", name);
293 for (j = 0; j < mv_n_values (mv); j++)
296 ds_put_cstr (s, ", ");
297 syntax_gen_value (s, mv_get_value (mv, j), width, format);
300 if (mv_has_range (mv))
303 if (mv_has_value (mv))
304 ds_put_cstr (s, ", ");
305 mv_get_range (mv, &low, &high);
306 syntax_gen_num_range (s, low, high, format);
308 ds_put_cstr (s, ").\n");
310 if (var_has_value_labels (var))
312 const struct val_labs *vls = var_get_value_labels (var);
313 const struct val_lab **labels = val_labs_sorted (vls);
314 size_t n_labels = val_labs_count (vls);
317 syntax_gen_pspp (s, "VALUE LABELS %ss", name);
318 for (i = 0; i < n_labels; i++)
320 const struct val_lab *vl = labels[i];
321 ds_put_cstr (s, "\n ");
322 syntax_gen_value (s, &vl->value, width, format);
323 ds_put_byte (s, ' ');
324 syntax_gen_string (s, ss_cstr (val_lab_get_escaped_label (vl)));
327 ds_put_cstr (s, ".\n");
329 if (var_has_label (var))
330 syntax_gen_pspp (s, "VARIABLE LABELS %ss %sq.\n",
331 name, var_get_label (var));
332 if (measure != var_default_measure (type))
333 syntax_gen_pspp (s, "VARIABLE LEVEL %ss (%ss).\n",
335 (measure == MEASURE_NOMINAL ? "NOMINAL"
336 : measure == MEASURE_ORDINAL ? "ORDINAL"
338 if (alignment != var_default_alignment (type))
339 syntax_gen_pspp (s, "VARIABLE ALIGNMENT %ss (%ss).\n",
341 (alignment == ALIGN_LEFT ? "LEFT"
342 : alignment == ALIGN_CENTRE ? "CENTER"
344 if (var_get_display_width (var) != var_default_display_width (width))
345 syntax_gen_pspp (s, "VARIABLE WIDTH %ss (%d).\n",
346 name, var_get_display_width (var));
350 /* Generates and returns PSPP syntax to execute the import
351 operation described by IA. The caller must free the syntax
354 generate_syntax (const struct import_assistant *ia)
356 struct string s = DS_EMPTY_INITIALIZER;
365 if (ia->file.encoding && strcmp (ia->file.encoding, "Auto"))
366 syntax_gen_pspp (&s, " /ENCODING=%sq\n", ia->file.encoding);
367 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (
368 ia->intro.n_cases_button)))
369 ds_put_format (&s, " /IMPORTCASES=FIRST %d\n",
370 gtk_spin_button_get_value_as_int (
371 GTK_SPIN_BUTTON (ia->intro.n_cases_spin)));
372 else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (
373 ia->intro.percent_button)))
374 ds_put_format (&s, " /IMPORTCASES=PERCENT %d\n",
375 gtk_spin_button_get_value_as_int (
376 GTK_SPIN_BUTTON (ia->intro.percent_spin)));
378 ds_put_cstr (&s, " /IMPORTCASES=ALL\n");
380 " /ARRANGEMENT=DELIMITED\n"
382 if (ia->first_line.skip_lines > 0)
383 ds_put_format (&s, " /FIRSTCASE=%d\n", ia->first_line.skip_lines + 1);
384 ds_put_cstr (&s, " /DELIMITERS=\"");
385 if (ds_find_byte (&ia->separators.separators, '\t') != SIZE_MAX)
386 ds_put_cstr (&s, "\\t");
387 if (ds_find_byte (&ia->separators.separators, '\\') != SIZE_MAX)
388 ds_put_cstr (&s, "\\\\");
389 for (i = 0; i < ds_length (&ia->separators.separators); i++)
391 char c = ds_at (&ia->separators.separators, i);
393 ds_put_cstr (&s, "\"\"");
394 else if (c != '\t' && c != '\\')
397 ds_put_cstr (&s, "\"\n");
398 if (!ds_is_empty (&ia->separators.quotes))
399 syntax_gen_pspp (&s, " /QUALIFIER=%sq\n", ds_cstr (&ia->separators.quotes));
400 if (!ds_is_empty (&ia->separators.quotes) && ia->separators.escape)
401 ds_put_cstr (&s, " /ESCAPE\n");
402 ds_put_cstr (&s, " /VARIABLES=\n");
404 var_cnt = dict_get_var_cnt (ia->formats.dict);
405 for (i = 0; i < var_cnt; i++)
407 struct variable *var = dict_get_var (ia->formats.dict, i);
408 char format_string[FMT_STRING_LEN_MAX + 1];
409 fmt_to_string (var_get_print_format (var), format_string);
410 ds_put_format (&s, " %s %s%s\n",
411 var_get_name (var), format_string,
412 i == var_cnt - 1 ? "." : "");
415 apply_dict (ia->formats.dict, &s);
420 /* Choosing a file and reading it. */
422 static char *choose_file (GtkWindow *parent_window, gchar **encodingp);
424 /* Obtains the file to import from the user and initializes IA's
425 file substructure. PARENT_WINDOW must be the window to use
426 as the file chooser window's parent.
428 Returns true if successful, false if the file name could not
429 be obtained or the file could not be read. */
431 init_file (struct import_assistant *ia, GtkWindow *parent_window)
433 struct file *file = &ia->file;
434 enum { MAX_PREVIEW_LINES = 1000 }; /* Max number of lines to read. */
435 enum { MAX_LINE_LEN = 16384 }; /* Max length of an acceptable line. */
436 struct line_reader *reader;
439 file->file_name = choose_file (parent_window, &file->encoding);
440 if (file->file_name == NULL)
443 reader = line_reader_for_file (file->encoding, file->file_name, O_RDONLY);
446 msg (ME, _("Could not open `%s': %s"),
447 file->file_name, strerror (errno));
451 ds_init_empty (&input);
452 file->lines = xnmalloc (MAX_PREVIEW_LINES, sizeof *file->lines);
453 for (; file->line_cnt < MAX_PREVIEW_LINES; file->line_cnt++)
456 if (!line_reader_read (reader, &input, MAX_LINE_LEN + 1)
457 || ds_length (&input) > MAX_LINE_LEN)
459 if (line_reader_eof (reader))
461 else if (line_reader_error (reader))
462 msg (ME, _("Error reading `%s': %s"),
463 file->file_name, strerror (line_reader_error (reader)));
465 msg (ME, _("Failed to read `%s', because it contains a line "
466 "over %d bytes long and therefore appears not to be "
468 file->file_name, MAX_LINE_LEN);
469 line_reader_close (reader);
475 ds_init_cstr (&file->lines[file->line_cnt],
476 recode_string ("UTF-8", line_reader_get_encoding (reader),
477 ds_cstr (&input), ds_length (&input)));
481 if (file->line_cnt == 0)
483 msg (ME, _("`%s' is empty."), file->file_name);
484 line_reader_close (reader);
489 /* Estimate the number of lines in the file. */
490 if (file->line_cnt < MAX_PREVIEW_LINES)
491 file->total_lines = file->line_cnt;
495 off_t position = line_reader_tell (reader);
496 if (fstat (line_reader_fileno (reader), &s) == 0 && position > 0)
497 file->total_lines = (double) file->line_cnt / position * s.st_size;
499 file->total_lines = 0;
502 line_reader_close (reader);
507 /* Frees IA's file substructure. */
509 destroy_file (struct import_assistant *ia)
511 struct file *f = &ia->file;
514 for (i = 0; i < f->line_cnt; i++)
515 ds_destroy (&f->lines[i]);
517 g_free (f->file_name);
518 g_free (f->encoding);
521 /* Obtains the file to read from the user. If successful, returns the name of
522 the file and stores the user's chosen encoding for the file into *ENCODINGP.
523 The caller must free each of these strings with g_free().
525 On failure, stores a null pointer and stores NULL in *ENCODINGP.
527 PARENT_WINDOW must be the window to use as the file chooser window's
530 choose_file (GtkWindow *parent_window, gchar **encodingp)
533 GtkFileFilter *filter = NULL;
535 GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Import Delimited Text Data"),
537 GTK_FILE_CHOOSER_ACTION_OPEN,
538 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
539 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
542 g_object_set (dialog, "local-only", FALSE, NULL);
544 filter = gtk_file_filter_new ();
545 gtk_file_filter_set_name (filter, _("Text files"));
546 gtk_file_filter_add_mime_type (filter, "text/*");
547 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
549 filter = gtk_file_filter_new ();
550 gtk_file_filter_set_name (filter, _("Text (*.txt) Files"));
551 gtk_file_filter_add_pattern (filter, "*.txt");
552 gtk_file_filter_add_pattern (filter, "*.TXT");
553 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
555 filter = gtk_file_filter_new ();
556 gtk_file_filter_set_name (filter, _("Plain Text (ASCII) Files"));
557 gtk_file_filter_add_mime_type (filter, "text/plain");
558 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
560 filter = gtk_file_filter_new ();
561 gtk_file_filter_set_name (filter, _("Comma Separated Value Files"));
562 gtk_file_filter_add_mime_type (filter, "text/csv");
563 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
565 /* I've never encountered one of these, but it's listed here:
566 http://www.iana.org/assignments/media-types/text/tab-separated-values */
567 filter = gtk_file_filter_new ();
568 gtk_file_filter_set_name (filter, _("Tab Separated Value Files"));
569 gtk_file_filter_add_mime_type (filter, "text/tab-separated-values");
570 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
572 gtk_file_chooser_set_extra_widget (
573 GTK_FILE_CHOOSER (dialog), psppire_encoding_selector_new ("Auto", true));
575 filter = gtk_file_filter_new ();
576 gtk_file_filter_set_name (filter, _("All Files"));
577 gtk_file_filter_add_pattern (filter, "*");
578 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
580 switch (gtk_dialog_run (GTK_DIALOG (dialog)))
582 case GTK_RESPONSE_ACCEPT:
583 file_name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
584 *encodingp = psppire_encoding_selector_get_encoding (
585 gtk_file_chooser_get_extra_widget (GTK_FILE_CHOOSER (dialog)));
592 gtk_widget_destroy (dialog);
599 static void close_assistant (struct import_assistant *, int response);
600 static void on_prepare (GtkAssistant *assistant, GtkWidget *page,
601 struct import_assistant *);
602 static void on_cancel (GtkAssistant *assistant, struct import_assistant *);
603 static void on_close (GtkAssistant *assistant, struct import_assistant *);
604 static void on_paste (GtkButton *button, struct import_assistant *);
605 static void on_reset (GtkButton *button, struct import_assistant *);
606 static void close_assistant (struct import_assistant *, int response);
608 /* Initializes IA's asst substructure. PARENT_WINDOW must be the
609 window to use as the assistant window's parent. */
611 init_assistant (struct import_assistant *ia, GtkWindow *parent_window)
613 struct assistant *a = &ia->asst;
615 a->builder = builder_new ("text-data-import.ui");
616 a->assistant = GTK_ASSISTANT (gtk_assistant_new ());
617 g_signal_connect (a->assistant, "prepare", G_CALLBACK (on_prepare), ia);
618 g_signal_connect (a->assistant, "cancel", G_CALLBACK (on_cancel), ia);
619 g_signal_connect (a->assistant, "close", G_CALLBACK (on_close), ia);
620 a->paste_button = gtk_button_new_from_stock (GTK_STOCK_PASTE);
621 gtk_assistant_add_action_widget (a->assistant, a->paste_button);
622 g_signal_connect (a->paste_button, "clicked", G_CALLBACK (on_paste), ia);
623 a->reset_button = gtk_button_new_from_stock ("pspp-stock-reset");
624 gtk_assistant_add_action_widget (a->assistant, a->reset_button);
625 g_signal_connect (a->reset_button, "clicked", G_CALLBACK (on_reset), ia);
626 gtk_window_set_title (GTK_WINDOW (a->assistant),
627 _("Importing Delimited Text Data"));
628 gtk_window_set_transient_for (GTK_WINDOW (a->assistant), parent_window);
629 gtk_window_set_icon_name (GTK_WINDOW (a->assistant), "pspp");
631 a->prop_renderer = gtk_cell_renderer_text_new ();
632 g_object_ref_sink (a->prop_renderer);
633 a->fixed_renderer = gtk_cell_renderer_text_new ();
634 g_object_ref_sink (a->fixed_renderer);
635 g_object_set (G_OBJECT (a->fixed_renderer),
636 "family", "Monospace",
640 /* Frees IA's asst substructure. */
642 destroy_assistant (struct import_assistant *ia)
644 struct assistant *a = &ia->asst;
646 g_object_unref (a->prop_renderer);
647 g_object_unref (a->fixed_renderer);
648 g_object_unref (a->builder);
651 /* Appends a page of the given TYPE, with PAGE as its content, to
652 the GtkAssistant encapsulated by IA. Returns the GtkWidget
653 that represents the page. */
655 add_page_to_assistant (struct import_assistant *ia,
656 GtkWidget *page, GtkAssistantPageType type)
662 title = gtk_window_get_title (GTK_WINDOW (page));
663 title_copy = xstrdup (title ? title : "");
665 content = gtk_bin_get_child (GTK_BIN (page));
667 g_object_ref (content);
668 gtk_container_remove (GTK_CONTAINER (page), content);
670 gtk_widget_destroy (page);
672 gtk_assistant_append_page (ia->asst.assistant, content);
673 gtk_assistant_set_page_type (ia->asst.assistant, content, type);
674 gtk_assistant_set_page_title (ia->asst.assistant, content, title_copy);
675 gtk_assistant_set_page_complete (ia->asst.assistant, content, true);
682 /* Called just before PAGE is displayed as the current page of
683 ASSISTANT, this updates IA content according to the new
686 on_prepare (GtkAssistant *assistant, GtkWidget *page,
687 struct import_assistant *ia)
690 if (gtk_assistant_get_page_type (assistant, page)
691 == GTK_ASSISTANT_PAGE_CONFIRM)
692 gtk_widget_grab_focus (assistant->apply);
694 gtk_widget_grab_focus (assistant->forward);
696 if (page == ia->separators.page)
697 prepare_separators_page (ia);
698 else if (page == ia->formats.page)
699 prepare_formats_page (ia);
701 gtk_widget_show (ia->asst.reset_button);
702 if (page == ia->formats.page)
703 gtk_widget_show (ia->asst.paste_button);
705 gtk_widget_hide (ia->asst.paste_button);
708 /* Called when the Cancel button in the assistant is clicked. */
710 on_cancel (GtkAssistant *assistant, struct import_assistant *ia)
712 close_assistant (ia, GTK_RESPONSE_CANCEL);
715 /* Called when the Apply button on the last page of the assistant
718 on_close (GtkAssistant *assistant, struct import_assistant *ia)
720 close_assistant (ia, GTK_RESPONSE_APPLY);
723 /* Called when the Paste button on the last page of the assistant
726 on_paste (GtkButton *button, struct import_assistant *ia)
728 close_assistant (ia, PSPPIRE_RESPONSE_PASTE);
731 /* Called when the Reset button is clicked. */
733 on_reset (GtkButton *button, struct import_assistant *ia)
735 gint page_num = gtk_assistant_get_current_page (ia->asst.assistant);
736 GtkWidget *page = gtk_assistant_get_nth_page (ia->asst.assistant, page_num);
738 if (page == ia->intro.page)
739 reset_intro_page (ia);
740 else if (page == ia->first_line.page)
741 reset_first_line_page (ia);
742 else if (page == ia->separators.page)
743 reset_separators_page (ia);
744 else if (page == ia->formats.page)
745 reset_formats_page (ia);
748 /* Causes the assistant to close, returning RESPONSE for
749 interpretation by text_data_import_assistant. */
751 close_assistant (struct import_assistant *ia, int response)
753 ia->asst.response = response;
754 g_main_loop_quit (ia->asst.main_loop);
755 gtk_widget_hide (GTK_WIDGET (ia->asst.assistant));
758 /* The "intro" page of the assistant. */
760 static void on_intro_amount_changed (struct import_assistant *);
762 /* Initializes IA's intro substructure. */
764 init_intro_page (struct import_assistant *ia)
766 GtkBuilder *builder = ia->asst.builder;
767 struct intro_page *p = &ia->intro;
769 GtkWidget *hbox_n_cases ;
770 GtkWidget *hbox_percent ;
774 p->n_cases_spin = gtk_spin_button_new_with_range (0, INT_MAX, 100);
776 hbox_n_cases = psppire_scanf_new (_("Only the first %4d cases"), &p->n_cases_spin);
778 table = get_widget_assert (builder, "button-table");
780 gtk_table_attach_defaults (GTK_TABLE (table), hbox_n_cases,
784 p->percent_spin = gtk_spin_button_new_with_range (0, 100, 10);
786 hbox_percent = psppire_scanf_new (_("Only the first %3d %% of file (approximately)"), &p->percent_spin);
788 gtk_table_attach_defaults (GTK_TABLE (table), hbox_percent,
792 p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Intro"),
793 GTK_ASSISTANT_PAGE_INTRO);
795 p->all_cases_button = get_widget_assert (builder, "import-all-cases");
797 p->n_cases_button = get_widget_assert (builder, "import-n-cases");
799 p->percent_button = get_widget_assert (builder, "import-percent");
801 g_signal_connect_swapped (p->all_cases_button, "toggled",
802 G_CALLBACK (on_intro_amount_changed), ia);
803 g_signal_connect_swapped (p->n_cases_button, "toggled",
804 G_CALLBACK (on_intro_amount_changed), ia);
805 g_signal_connect_swapped (p->percent_button, "toggled",
806 G_CALLBACK (on_intro_amount_changed), ia);
808 on_intro_amount_changed (ia);
811 ds_put_cstr (&s, _("This assistant will guide you through the process of "
812 "importing data into PSPP from a text file with one line "
813 "per case, in which fields are separated by tabs, "
814 "commas, or other delimiters.\n\n"));
815 if (ia->file.total_is_exact)
817 &s, ngettext ("The selected file contains %zu line of text. ",
818 "The selected file contains %zu lines of text. ",
821 else if (ia->file.total_lines > 0)
825 "The selected file contains approximately %lu line of text. ",
826 "The selected file contains approximately %lu lines of text. ",
827 ia->file.total_lines),
828 ia->file.total_lines);
831 "Only the first %zu line of the file will be shown for "
832 "preview purposes in the following screens. ",
833 "Only the first %zu lines of the file will be shown for "
834 "preview purposes in the following screens. ",
838 ds_put_cstr (&s, _("You may choose below how much of the file should "
839 "actually be imported."));
840 gtk_label_set_text (GTK_LABEL (get_widget_assert (builder, "intro-label")),
845 /* Resets IA's intro page to its initial state. */
847 reset_intro_page (struct import_assistant *ia)
849 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->intro.all_cases_button),
853 /* Called when one of the radio buttons is clicked. */
855 on_intro_amount_changed (struct import_assistant *ia)
857 struct intro_page *p = &ia->intro;
859 gtk_widget_set_sensitive (p->n_cases_spin,
860 gtk_toggle_button_get_active (
861 GTK_TOGGLE_BUTTON (p->n_cases_button)));
863 gtk_widget_set_sensitive (p->percent_spin,
864 gtk_toggle_button_get_active (
865 GTK_TOGGLE_BUTTON (p->percent_button)));
868 /* The "first line" page of the assistant. */
870 static GtkTreeView *create_lines_tree_view (GtkContainer *parent_window,
871 struct import_assistant *);
872 static void on_first_line_change (GtkTreeSelection *,
873 struct import_assistant *);
874 static void on_variable_names_cb_toggle (GtkToggleButton *,
875 struct import_assistant *);
876 static void set_first_line (struct import_assistant *);
877 static void get_first_line (struct import_assistant *);
879 /* Initializes IA's first_line substructure. */
881 init_first_line_page (struct import_assistant *ia)
883 struct first_line_page *p = &ia->first_line;
884 GtkBuilder *builder = ia->asst.builder;
886 p->page = add_page_to_assistant (ia, get_widget_assert (builder, "FirstLine"),
887 GTK_ASSISTANT_PAGE_CONTENT);
888 gtk_widget_destroy (get_widget_assert (builder, "first-line"));
889 p->tree_view = create_lines_tree_view (
890 GTK_CONTAINER (get_widget_assert (builder, "first-line-scroller")), ia);
891 p->variable_names_cb = get_widget_assert (builder, "variable-names");
892 gtk_tree_selection_set_mode (
893 gtk_tree_view_get_selection (GTK_TREE_VIEW (p->tree_view)),
894 GTK_SELECTION_BROWSE);
896 g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (p->tree_view)),
897 "changed", G_CALLBACK (on_first_line_change), ia);
898 g_signal_connect (p->variable_names_cb, "toggled",
899 G_CALLBACK (on_variable_names_cb_toggle), ia);
902 /* Resets the first_line page to its initial content. */
904 reset_first_line_page (struct import_assistant *ia)
906 ia->first_line.skip_lines = 0;
907 ia->first_line.variable_names = false;
912 render_line (GtkTreeViewColumn *tree_column,
913 GtkCellRenderer *cell,
914 GtkTreeModel *tree_model,
918 gint row = empty_list_store_iter_to_row (iter);
919 struct string *lines;
921 lines = g_object_get_data (G_OBJECT (tree_model), "lines");
922 g_return_if_fail (lines != NULL);
924 g_object_set (cell, "text", ds_cstr (&lines[row]), NULL);
928 /* Creates and returns a tree view that contains each of the
929 lines in IA's file as a row. */
931 create_lines_tree_view (GtkContainer *parent, struct import_assistant *ia)
933 GtkTreeView *tree_view;
934 GtkTreeViewColumn *column;
935 size_t max_line_length;
936 gint content_width, header_width;
938 const gchar *title = _("Text");
940 make_tree_view (ia, 0, &tree_view);
942 column = gtk_tree_view_column_new_with_attributes (
943 title, ia->asst.fixed_renderer, (void *) NULL);
944 gtk_tree_view_column_set_cell_data_func (column, ia->asst.fixed_renderer,
945 render_line, NULL, NULL);
946 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
949 for (i = 0; i < ia->file.line_cnt; i++)
951 size_t w = ds_length (&ia->file.lines[i]);
952 max_line_length = MAX (max_line_length, w);
955 content_width = get_monospace_width (tree_view, ia->asst.fixed_renderer,
957 header_width = get_string_width (tree_view, ia->asst.prop_renderer, title);
958 gtk_tree_view_column_set_fixed_width (column, MAX (content_width,
960 gtk_tree_view_append_column (tree_view, column);
962 gtk_tree_view_set_fixed_height_mode (tree_view, true);
964 gtk_container_add (parent, GTK_WIDGET (tree_view));
965 gtk_widget_show (GTK_WIDGET (tree_view));
970 /* Called when the line selected in the first_line tree view
973 on_first_line_change (GtkTreeSelection *selection UNUSED,
974 struct import_assistant *ia)
979 /* Called when the checkbox that indicates whether variable
980 names are in the row above the first line is toggled. */
982 on_variable_names_cb_toggle (GtkToggleButton *variable_names_cb UNUSED,
983 struct import_assistant *ia)
988 /* Sets the widgets to match IA's first_line substructure. */
990 set_first_line (struct import_assistant *ia)
994 path = gtk_tree_path_new_from_indices (ia->first_line.skip_lines, -1);
995 gtk_tree_view_set_cursor (GTK_TREE_VIEW (ia->first_line.tree_view),
997 gtk_tree_path_free (path);
999 gtk_toggle_button_set_active (
1000 GTK_TOGGLE_BUTTON (ia->first_line.variable_names_cb),
1001 ia->first_line.variable_names);
1002 gtk_widget_set_sensitive (ia->first_line.variable_names_cb,
1003 ia->first_line.skip_lines > 0);
1006 /* Sets IA's first_line substructure to match the widgets. */
1008 get_first_line (struct import_assistant *ia)
1010 GtkTreeSelection *selection;
1012 GtkTreeModel *model;
1014 selection = gtk_tree_view_get_selection (ia->first_line.tree_view);
1015 if (gtk_tree_selection_get_selected (selection, &model, &iter))
1017 GtkTreePath *path = gtk_tree_model_get_path (model, &iter);
1018 int row = gtk_tree_path_get_indices (path)[0];
1019 gtk_tree_path_free (path);
1021 ia->first_line.skip_lines = row;
1022 ia->first_line.variable_names =
1023 (ia->first_line.skip_lines > 0
1024 && gtk_toggle_button_get_active (
1025 GTK_TOGGLE_BUTTON (ia->first_line.variable_names_cb)));
1027 gtk_widget_set_sensitive (ia->first_line.variable_names_cb,
1028 ia->first_line.skip_lines > 0);
1031 /* The "separators" page of the assistant. */
1033 static void revise_fields_preview (struct import_assistant *ia);
1034 static void choose_likely_separators (struct import_assistant *ia);
1035 static void find_commonest_chars (unsigned long int histogram[UCHAR_MAX + 1],
1036 const char *targets, const char *def,
1037 struct string *result);
1038 static void clear_fields (struct import_assistant *ia);
1039 static void revise_fields_preview (struct import_assistant *);
1040 static void set_separators (struct import_assistant *);
1041 static void get_separators (struct import_assistant *);
1042 static void on_separators_custom_entry_notify (GObject *UNUSED,
1044 struct import_assistant *);
1045 static void on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
1046 struct import_assistant *);
1047 static void on_quote_combo_change (GtkComboBox *combo,
1048 struct import_assistant *);
1049 static void on_quote_cb_toggle (GtkToggleButton *quote_cb,
1050 struct import_assistant *);
1051 static void on_separator_toggle (GtkToggleButton *, struct import_assistant *);
1052 static void render_input_cell (GtkTreeViewColumn *tree_column,
1053 GtkCellRenderer *cell,
1054 GtkTreeModel *model, GtkTreeIter *iter,
1056 static gboolean on_query_input_tooltip (GtkWidget *widget, gint wx, gint wy,
1057 gboolean keyboard_mode UNUSED,
1058 GtkTooltip *tooltip,
1059 struct import_assistant *);
1061 /* A common field separator and its identifying name. */
1064 const char *name; /* Name (for use with get_widget_assert). */
1065 int c; /* Separator character. */
1068 /* All the separators in the dialog box. */
1069 static const struct separator separators[] =
1081 #define SEPARATOR_CNT (sizeof separators / sizeof *separators)
1084 set_quote_list (GtkComboBoxEntry *cb)
1086 GtkListStore *list = gtk_list_store_new (1, G_TYPE_STRING);
1089 const gchar *seperator[3] = {"'\"", "\'", "\""};
1091 for (i = 0; i < 3; i++)
1093 const gchar *s = seperator[i];
1095 /* Add a new row to the model */
1096 gtk_list_store_append (list, &iter);
1097 gtk_list_store_set (list, &iter,
1103 gtk_combo_box_set_model (GTK_COMBO_BOX (cb), GTK_TREE_MODEL (list));
1104 g_object_unref (list);
1106 gtk_combo_box_entry_set_text_column (cb, 0);
1109 /* Initializes IA's separators substructure. */
1111 init_separators_page (struct import_assistant *ia)
1113 GtkBuilder *builder = ia->asst.builder;
1114 struct separators_page *p = &ia->separators;
1117 choose_likely_separators (ia);
1119 p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Separators"),
1120 GTK_ASSISTANT_PAGE_CONTENT);
1121 p->custom_cb = get_widget_assert (builder, "custom-cb");
1122 p->custom_entry = get_widget_assert (builder, "custom-entry");
1123 p->quote_combo = get_widget_assert (builder, "quote-combo");
1124 p->quote_entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (p->quote_combo)));
1125 p->quote_cb = get_widget_assert (builder, "quote-cb");
1126 p->escape_cb = get_widget_assert (builder, "escape");
1128 set_separators (ia);
1129 set_quote_list (GTK_COMBO_BOX_ENTRY (p->quote_combo));
1130 p->fields_tree_view = GTK_TREE_VIEW (get_widget_assert (builder, "fields"));
1131 g_signal_connect (p->quote_combo, "changed",
1132 G_CALLBACK (on_quote_combo_change), ia);
1133 g_signal_connect (p->quote_cb, "toggled",
1134 G_CALLBACK (on_quote_cb_toggle), ia);
1135 g_signal_connect (p->custom_entry, "notify::text",
1136 G_CALLBACK (on_separators_custom_entry_notify), ia);
1137 g_signal_connect (p->custom_cb, "toggled",
1138 G_CALLBACK (on_separators_custom_cb_toggle), ia);
1139 for (i = 0; i < SEPARATOR_CNT; i++)
1140 g_signal_connect (get_widget_assert (builder, separators[i].name),
1141 "toggled", G_CALLBACK (on_separator_toggle), ia);
1142 g_signal_connect (p->escape_cb, "toggled",
1143 G_CALLBACK (on_separator_toggle), ia);
1146 /* Frees IA's separators substructure. */
1148 destroy_separators_page (struct import_assistant *ia)
1150 struct separators_page *s = &ia->separators;
1152 ds_destroy (&s->separators);
1153 ds_destroy (&s->quotes);
1157 /* Called just before the separators page becomes visible in the
1160 prepare_separators_page (struct import_assistant *ia)
1162 revise_fields_preview (ia);
1165 /* Called when the Reset button is clicked on the separators
1166 page, resets the separators to the defaults. */
1168 reset_separators_page (struct import_assistant *ia)
1170 choose_likely_separators (ia);
1171 set_separators (ia);
1174 /* Frees and clears the column data in IA's separators
1177 clear_fields (struct import_assistant *ia)
1179 struct separators_page *s = &ia->separators;
1181 if (s->column_cnt > 0)
1186 for (row = 0; row < ia->file.line_cnt; row++)
1188 const struct string *line = &ia->file.lines[row];
1189 const char *line_start = ds_data (line);
1190 const char *line_end = ds_end (line);
1192 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1194 char *s = ss_data (col->contents[row]);
1195 if (!(s >= line_start && s <= line_end))
1196 ss_dealloc (&col->contents[row]);
1200 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1203 free (col->contents);
1212 /* Breaks the file data in IA into columns based on the
1213 separators set in IA's separators substructure. */
1215 split_fields (struct import_assistant *ia)
1217 struct separators_page *s = &ia->separators;
1218 size_t columns_allocated;
1224 /* Is space in the set of separators? */
1225 space_sep = ss_find_byte (ds_ss (&s->separators), ' ') != SIZE_MAX;
1227 /* Split all the lines, not just those from
1228 ia->first_line.skip_lines on, so that we split the line that
1229 contains variables names if ia->first_line.variable_names is
1231 columns_allocated = 0;
1232 for (row = 0; row < ia->file.line_cnt; row++)
1234 struct string *line = &ia->file.lines[row];
1235 struct substring text = ds_ss (line);
1238 for (column_idx = 0; ; column_idx++)
1240 struct substring field;
1241 struct column *column;
1244 ss_ltrim (&text, ss_cstr (" "));
1245 if (ss_is_empty (text))
1247 if (column_idx != 0)
1251 else if (!ds_is_empty (&s->quotes)
1252 && ds_find_byte (&s->quotes, text.string[0]) != SIZE_MAX)
1254 int quote = ss_get_byte (&text);
1256 ss_get_until (&text, quote, &field);
1263 while ((c = ss_get_byte (&text)) != EOF)
1265 ds_put_byte (&s, c);
1266 else if (ss_match_byte (&text, quote))
1267 ds_put_byte (&s, quote);
1274 ss_get_bytes (&text, ss_cspan (text, ds_ss (&s->separators)),
1277 if (column_idx >= s->column_cnt)
1279 struct column *column;
1281 if (s->column_cnt >= columns_allocated)
1282 s->columns = x2nrealloc (s->columns, &columns_allocated,
1283 sizeof *s->columns);
1284 column = &s->columns[s->column_cnt++];
1285 column->name = NULL;
1287 column->contents = xcalloc (ia->file.line_cnt,
1288 sizeof *column->contents);
1290 column = &s->columns[column_idx];
1291 column->contents[row] = field;
1292 if (ss_length (field) > column->width)
1293 column->width = ss_length (field);
1296 ss_ltrim (&text, ss_cstr (" "));
1297 if (ss_is_empty (text))
1299 if (ss_find_byte (ds_ss (&s->separators), ss_first (text))
1301 ss_advance (&text, 1);
1306 /* Chooses a name for each column on the separators page */
1308 choose_column_names (struct import_assistant *ia)
1310 const struct first_line_page *f = &ia->first_line;
1311 struct separators_page *s = &ia->separators;
1312 struct dictionary *dict;
1313 unsigned long int generated_name_count = 0;
1317 dict = dict_create (get_default_encoding ());
1318 name_row = f->variable_names && f->skip_lines ? f->skip_lines : 0;
1319 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1323 hint = name_row ? ss_xstrdup (col->contents[name_row - 1]) : NULL;
1324 name = dict_make_unique_var_name (dict, hint, &generated_name_count);
1328 dict_create_var_assert (dict, name, 0);
1330 dict_destroy (dict);
1333 /* Picks the most likely separator and quote characters based on
1336 choose_likely_separators (struct import_assistant *ia)
1338 unsigned long int histogram[UCHAR_MAX + 1] = { 0 };
1341 /* Construct a histogram of all the characters used in the
1343 for (row = 0; row < ia->file.line_cnt; row++)
1345 struct substring line = ds_ss (&ia->file.lines[row]);
1346 size_t length = ss_length (line);
1348 for (i = 0; i < length; i++)
1349 histogram[(unsigned char) line.string[i]]++;
1352 find_commonest_chars (histogram, "\"'", "", &ia->separators.quotes);
1353 find_commonest_chars (histogram, ",;:/|!\t-", ",",
1354 &ia->separators.separators);
1355 ia->separators.escape = true;
1358 /* Chooses the most common character among those in TARGETS,
1359 based on the frequency data in HISTOGRAM, and stores it in
1360 RESULT. If there is a tie for the most common character among
1361 those in TARGETS, the earliest character is chosen. If none
1362 of the TARGETS appear at all, then DEF is used as a
1365 find_commonest_chars (unsigned long int histogram[UCHAR_MAX + 1],
1366 const char *targets, const char *def,
1367 struct string *result)
1369 unsigned char max = 0;
1370 unsigned long int max_count = 0;
1372 for (; *targets != '\0'; targets++)
1374 unsigned char c = *targets;
1375 unsigned long int count = histogram[c];
1376 if (count > max_count)
1385 ds_put_byte (result, max);
1388 ds_assign_cstr (result, def);
1391 /* Revises the contents of the fields tree view based on the
1392 currently chosen set of separators. */
1394 revise_fields_preview (struct import_assistant *ia)
1398 push_watch_cursor (ia);
1400 w = GTK_WIDGET (ia->separators.fields_tree_view);
1401 gtk_widget_destroy (w);
1402 get_separators (ia);
1404 choose_column_names (ia);
1405 ia->separators.fields_tree_view = create_data_tree_view (
1407 GTK_CONTAINER (get_widget_assert (ia->asst.builder, "fields-scroller")),
1410 pop_watch_cursor (ia);
1413 /* Sets the widgets to match IA's separators substructure. */
1415 set_separators (struct import_assistant *ia)
1417 struct separators_page *s = &ia->separators;
1419 struct string custom;
1424 ds_init_empty (&custom);
1426 for (i = 0; i < ds_length (&s->separators); i++)
1428 unsigned char c = ds_at (&s->separators, i);
1431 for (j = 0; j < SEPARATOR_CNT; j++)
1433 const struct separator *s = &separators[j];
1441 ds_put_byte (&custom, c);
1445 for (i = 0; i < SEPARATOR_CNT; i++)
1447 const struct separator *s = &separators[i];
1448 GtkWidget *button = get_widget_assert (ia->asst.builder, s->name);
1449 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
1450 (seps & (1u << i)) != 0);
1452 any_custom = !ds_is_empty (&custom);
1453 gtk_entry_set_text (GTK_ENTRY (s->custom_entry), ds_cstr (&custom));
1454 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->custom_cb),
1456 gtk_widget_set_sensitive (s->custom_entry, any_custom);
1457 ds_destroy (&custom);
1459 any_quotes = !ds_is_empty (&s->quotes);
1461 gtk_entry_set_text (s->quote_entry,
1462 any_quotes ? ds_cstr (&s->quotes) : "\"");
1463 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->quote_cb),
1465 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->escape_cb),
1467 gtk_widget_set_sensitive (s->quote_combo, any_quotes);
1468 gtk_widget_set_sensitive (s->escape_cb, any_quotes);
1471 /* Sets IA's separators substructure to match the widgets. */
1473 get_separators (struct import_assistant *ia)
1475 struct separators_page *s = &ia->separators;
1478 ds_clear (&s->separators);
1479 for (i = 0; i < SEPARATOR_CNT; i++)
1481 const struct separator *sep = &separators[i];
1482 GtkWidget *button = get_widget_assert (ia->asst.builder, sep->name);
1483 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
1484 ds_put_byte (&s->separators, sep->c);
1487 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->custom_cb)))
1488 ds_put_cstr (&s->separators,
1489 gtk_entry_get_text (GTK_ENTRY (s->custom_entry)));
1491 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->quote_cb)))
1493 gchar *text = gtk_combo_box_get_active_text (
1494 GTK_COMBO_BOX (s->quote_combo));
1495 ds_assign_cstr (&s->quotes, text);
1499 ds_clear (&s->quotes);
1500 s->escape = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->escape_cb));
1503 /* Called when the user changes the entry field for custom
1506 on_separators_custom_entry_notify (GObject *gobject UNUSED,
1507 GParamSpec *arg1 UNUSED,
1508 struct import_assistant *ia)
1510 revise_fields_preview (ia);
1513 /* Called when the user toggles the checkbox that enables custom
1516 on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
1517 struct import_assistant *ia)
1519 bool is_active = gtk_toggle_button_get_active (custom_cb);
1520 gtk_widget_set_sensitive (ia->separators.custom_entry, is_active);
1521 revise_fields_preview (ia);
1524 /* Called when the user changes the selection in the combo box
1525 that selects a quote character. */
1527 on_quote_combo_change (GtkComboBox *combo, struct import_assistant *ia)
1529 revise_fields_preview (ia);
1532 /* Called when the user toggles the checkbox that enables
1535 on_quote_cb_toggle (GtkToggleButton *quote_cb, struct import_assistant *ia)
1537 bool is_active = gtk_toggle_button_get_active (quote_cb);
1538 gtk_widget_set_sensitive (ia->separators.quote_combo, is_active);
1539 gtk_widget_set_sensitive (ia->separators.escape_cb, is_active);
1540 revise_fields_preview (ia);
1543 /* Called when the user toggles one of the separators
1546 on_separator_toggle (GtkToggleButton *toggle UNUSED,
1547 struct import_assistant *ia)
1549 revise_fields_preview (ia);
1552 /* Called to render one of the cells in the fields preview tree
1555 render_input_cell (GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1556 GtkTreeModel *model, GtkTreeIter *iter,
1559 struct import_assistant *ia = ia_;
1560 struct substring field;
1564 column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1566 row = empty_list_store_iter_to_row (iter) + ia->first_line.skip_lines;
1567 field = ia->separators.columns[column].contents[row];
1568 if (field.string != NULL)
1570 GValue text = {0, };
1571 g_value_init (&text, G_TYPE_STRING);
1572 g_value_take_string (&text, ss_xstrdup (field));
1573 g_object_set_property (G_OBJECT (cell), "text", &text);
1574 g_value_unset (&text);
1575 g_object_set (cell, "background-set", FALSE, (void *) NULL);
1580 "background", "red",
1581 "background-set", TRUE,
1585 /* Called to render a tooltip on one of the cells in the fields
1586 preview tree view. */
1588 on_query_input_tooltip (GtkWidget *widget, gint wx, gint wy,
1589 gboolean keyboard_mode UNUSED,
1590 GtkTooltip *tooltip, struct import_assistant *ia)
1594 if (!get_tooltip_location (widget, wx, wy, ia, &row, &column))
1597 if (ia->separators.columns[column].contents[row].string != NULL)
1600 gtk_tooltip_set_text (tooltip,
1601 _("This input line has too few separators "
1602 "to fill in this field."));
1606 /* The "formats" page of the assistant. */
1608 static void on_variable_change (PsppireDict *dict, int idx,
1609 struct import_assistant *);
1610 static void clear_modified_vars (struct import_assistant *);
1612 /* Initializes IA's formats substructure. */
1614 init_formats_page (struct import_assistant *ia)
1616 GtkBuilder *builder = ia->asst.builder;
1617 struct formats_page *p = &ia->formats;
1619 p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Formats"),
1620 GTK_ASSISTANT_PAGE_CONFIRM);
1621 p->data_tree_view = GTK_TREE_VIEW (get_widget_assert (builder, "data"));
1622 p->modified_vars = NULL;
1623 p->modified_var_cnt = 0;
1627 /* Frees IA's formats substructure. */
1629 destroy_formats_page (struct import_assistant *ia)
1631 struct formats_page *p = &ia->formats;
1633 if (p->psppire_dict != NULL)
1635 dict_destroy (p->psppire_dict->dict);
1636 g_object_unref (p->psppire_dict);
1638 clear_modified_vars (ia);
1641 /* Called just before the formats page of the assistant is
1644 prepare_formats_page (struct import_assistant *ia)
1646 struct dictionary *dict;
1647 PsppireDict *psppire_dict;
1648 PsppireVarStore *var_store;
1649 GtkBin *vars_scroller;
1650 GtkWidget *old_var_sheet;
1651 PsppireVarSheet *var_sheet;
1652 struct separators_page *s = &ia->separators;
1653 struct formats_page *p = &ia->formats;
1654 struct fmt_guesser *fg;
1655 unsigned long int number = 0;
1658 push_watch_cursor (ia);
1660 dict = dict_create (get_default_encoding ());
1661 fg = fmt_guesser_create ();
1662 for (column_idx = 0; column_idx < s->column_cnt; column_idx++)
1664 struct variable *modified_var;
1666 modified_var = (column_idx < p->modified_var_cnt
1667 ? p->modified_vars[column_idx] : NULL);
1668 if (modified_var == NULL)
1670 struct column *column = &s->columns[column_idx];
1671 struct variable *var;
1672 struct fmt_spec format;
1676 /* Choose variable name. */
1677 name = dict_make_unique_var_name (dict, column->name, &number);
1679 /* Choose variable format. */
1680 fmt_guesser_clear (fg);
1681 for (row = ia->first_line.skip_lines; row < ia->file.line_cnt; row++)
1682 fmt_guesser_add (fg, column->contents[row]);
1683 fmt_guesser_guess (fg, &format);
1684 fmt_fix_input (&format);
1686 /* Create variable. */
1687 var = dict_create_var_assert (dict, name, fmt_var_width (&format));
1688 var_set_both_formats (var, &format);
1696 name = dict_make_unique_var_name (dict, var_get_name (modified_var),
1698 dict_clone_var_as_assert (dict, modified_var, name);
1702 fmt_guesser_destroy (fg);
1704 psppire_dict = psppire_dict_new_from_dict (dict);
1705 g_signal_connect (psppire_dict, "variable_changed",
1706 G_CALLBACK (on_variable_change), ia);
1707 ia->formats.dict = dict;
1708 ia->formats.psppire_dict = psppire_dict;
1710 /* XXX: PsppireVarStore doesn't hold a reference to
1711 psppire_dict for now, but it should. After it does, we
1712 should g_object_ref the psppire_dict here, since we also
1713 hold a reference via ia->formats.dict. */
1714 var_store = psppire_var_store_new (psppire_dict);
1715 g_object_set (var_store,
1716 "format-type", PSPPIRE_VAR_STORE_INPUT_FORMATS,
1718 var_sheet = PSPPIRE_VAR_SHEET (psppire_var_sheet_new ());
1719 g_object_set (var_sheet,
1721 "may-create-vars", FALSE,
1724 vars_scroller = GTK_BIN (get_widget_assert (ia->asst.builder, "vars-scroller"));
1725 old_var_sheet = gtk_bin_get_child (vars_scroller);
1726 if (old_var_sheet != NULL)
1727 gtk_widget_destroy (old_var_sheet);
1728 gtk_container_add (GTK_CONTAINER (vars_scroller), GTK_WIDGET (var_sheet));
1729 gtk_widget_show (GTK_WIDGET (var_sheet));
1731 gtk_widget_destroy (GTK_WIDGET (ia->formats.data_tree_view));
1732 ia->formats.data_tree_view = create_data_tree_view (
1734 GTK_CONTAINER (get_widget_assert (ia->asst.builder, "data-scroller")),
1737 pop_watch_cursor (ia);
1740 /* Clears the set of user-modified variables from IA's formats
1741 substructure. This discards user modifications to variable
1742 formats, thereby causing formats to revert to their
1745 clear_modified_vars (struct import_assistant *ia)
1747 struct formats_page *p = &ia->formats;
1750 for (i = 0; i < p->modified_var_cnt; i++)
1751 var_destroy (p->modified_vars[i]);
1752 free (p->modified_vars);
1753 p->modified_vars = NULL;
1754 p->modified_var_cnt = 0;
1757 /* Resets the formats page to its defaults, discarding user
1760 reset_formats_page (struct import_assistant *ia)
1762 clear_modified_vars (ia);
1763 prepare_formats_page (ia);
1766 /* Called when the user changes one of the variables in the
1769 on_variable_change (PsppireDict *dict, int dict_idx,
1770 struct import_assistant *ia)
1772 struct formats_page *p = &ia->formats;
1773 GtkTreeView *tv = ia->formats.data_tree_view;
1774 gint column_idx = dict_idx + 1;
1776 push_watch_cursor (ia);
1778 /* Remove previous column and replace with new column. */
1779 gtk_tree_view_remove_column (tv, gtk_tree_view_get_column (tv, column_idx));
1780 gtk_tree_view_insert_column (tv, make_data_column (ia, tv, false, dict_idx),
1783 /* Save a copy of the modified variable in modified_vars, so
1784 that its attributes will be preserved if we back up to the
1785 previous page with the Prev button and then come back
1787 if (dict_idx >= p->modified_var_cnt)
1790 p->modified_vars = xnrealloc (p->modified_vars, dict_idx + 1,
1791 sizeof *p->modified_vars);
1792 for (i = 0; i <= dict_idx; i++)
1793 p->modified_vars[i] = NULL;
1794 p->modified_var_cnt = dict_idx + 1;
1796 if (p->modified_vars[dict_idx])
1797 var_destroy (p->modified_vars[dict_idx]);
1798 p->modified_vars[dict_idx]
1799 = var_clone (psppire_dict_get_variable (dict, dict_idx));
1801 pop_watch_cursor (ia);
1804 /* Parses the contents of the field at (ROW,COLUMN) according to
1805 its variable format. If OUTPUTP is non-null, then *OUTPUTP
1806 receives the formatted output for that field (which must be
1807 freed with free). If TOOLTIPP is non-null, then *TOOLTIPP
1808 receives a message suitable for use in a tooltip, if one is
1809 needed, or a null pointer otherwise. Returns true if a
1810 tooltip message is needed, otherwise false. */
1812 parse_field (struct import_assistant *ia,
1813 size_t row, size_t column,
1814 char **outputp, char **tooltipp)
1816 struct substring field;
1818 struct variable *var;
1819 const struct fmt_spec *in;
1820 struct fmt_spec out;
1824 field = ia->separators.columns[column].contents[row];
1825 var = dict_get_var (ia->formats.dict, column);
1826 value_init (&val, var_get_width (var));
1827 in = var_get_print_format (var);
1828 out = fmt_for_output_from_input (in);
1830 if (field.string != NULL)
1834 error = data_in (field, "UTF-8", in->type, &val, var_get_width (var),
1835 dict_get_encoding (ia->formats.dict));
1838 tooltip = xasprintf (_("Cannot parse field content `%.*s' as "
1840 (int) field.length, field.string,
1841 fmt_name (in->type), error);
1847 tooltip = xstrdup (_("This input line has too few separators "
1848 "to fill in this field."));
1849 value_set_missing (&val, var_get_width (var));
1851 if (outputp != NULL)
1853 *outputp = data_out (&val, dict_get_encoding (ia->formats.dict), &out);
1855 value_destroy (&val, var_get_width (var));
1857 ok = tooltip == NULL;
1858 if (tooltipp != NULL)
1859 *tooltipp = tooltip;
1865 /* Called to render one of the cells in the data preview tree
1868 render_output_cell (GtkTreeViewColumn *tree_column,
1869 GtkCellRenderer *cell,
1870 GtkTreeModel *model,
1874 struct import_assistant *ia = ia_;
1876 GValue gvalue = { 0, };
1879 ok = parse_field (ia,
1880 (empty_list_store_iter_to_row (iter)
1881 + ia->first_line.skip_lines),
1882 GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1886 g_value_init (&gvalue, G_TYPE_STRING);
1887 g_value_take_string (&gvalue, output);
1888 g_object_set_property (G_OBJECT (cell), "text", &gvalue);
1889 g_value_unset (&gvalue);
1892 g_object_set (cell, "background-set", FALSE, (void *) NULL);
1895 "background", "red",
1896 "background-set", TRUE,
1900 /* Called to render a tooltip for one of the cells in the data
1901 preview tree view. */
1903 on_query_output_tooltip (GtkWidget *widget, gint wx, gint wy,
1904 gboolean keyboard_mode UNUSED,
1905 GtkTooltip *tooltip, struct import_assistant *ia)
1910 if (!get_tooltip_location (widget, wx, wy, ia, &row, &column))
1913 if (parse_field (ia, row, column, NULL, &text))
1916 gtk_tooltip_set_text (tooltip, text);
1921 /* Utility functions used by multiple pages of the assistant. */
1924 get_tooltip_location (GtkWidget *widget, gint wx, gint wy,
1925 const struct import_assistant *ia,
1926 size_t *row, size_t *column)
1928 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
1932 GtkTreeViewColumn *tree_column;
1933 GtkTreeModel *tree_model;
1936 /* Check that WIDGET is really visible on the screen before we
1937 do anything else. This is a bug fix for a sticky situation:
1938 when text_data_import_assistant() returns, it frees the data
1939 necessary to compose the tool tip message, but there may be
1940 a tool tip under preparation at that point (even if there is
1941 no visible tool tip) that will call back into us a little
1942 bit later. Perhaps the correct solution to this problem is
1943 to make the data related to the tool tips part of a GObject
1944 that only gets destroyed when all references are released,
1945 but this solution appears to be effective too. */
1946 if (!gtk_widget_get_mapped (widget))
1949 gtk_tree_view_convert_widget_to_bin_window_coords (tree_view,
1951 if (!gtk_tree_view_get_path_at_pos (tree_view, bx, by,
1952 &path, &tree_column, NULL, NULL))
1955 *column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1958 tree_model = gtk_tree_view_get_model (tree_view);
1959 ok = gtk_tree_model_get_iter (tree_model, &iter, path);
1960 gtk_tree_path_free (path);
1964 *row = empty_list_store_iter_to_row (&iter) + ia->first_line.skip_lines;
1969 make_tree_view (const struct import_assistant *ia,
1971 GtkTreeView **tree_view)
1973 GtkTreeModel *model;
1975 *tree_view = GTK_TREE_VIEW (gtk_tree_view_new ());
1976 model = GTK_TREE_MODEL (psppire_empty_list_store_new (
1977 ia->file.line_cnt - first_line));
1978 g_object_set_data (G_OBJECT (model), "lines", ia->file.lines + first_line);
1979 g_object_set_data (G_OBJECT (model), "first-line",
1980 GINT_TO_POINTER (first_line));
1981 gtk_tree_view_set_model (*tree_view, model);
1982 g_object_unref (model);
1984 add_line_number_column (ia, *tree_view);
1988 render_line_number (GtkTreeViewColumn *tree_column,
1989 GtkCellRenderer *cell,
1990 GtkTreeModel *tree_model,
1994 gint row = empty_list_store_iter_to_row (iter);
1995 char s[INT_BUFSIZE_BOUND (int)];
1998 first_line = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_model),
2000 sprintf (s, "%d", first_line + row);
2001 g_object_set (cell, "text", s, NULL);
2005 add_line_number_column (const struct import_assistant *ia,
2006 GtkTreeView *treeview)
2008 GtkTreeViewColumn *column;
2010 column = gtk_tree_view_column_new_with_attributes (
2011 _("Line"), ia->asst.prop_renderer, (void *) NULL);
2012 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
2013 gtk_tree_view_column_set_fixed_width (
2014 column, get_monospace_width (treeview, ia->asst.prop_renderer, 5));
2015 gtk_tree_view_column_set_resizable (column, TRUE);
2016 gtk_tree_view_column_set_cell_data_func (column, ia->asst.prop_renderer,
2017 render_line_number, NULL, NULL);
2018 gtk_tree_view_append_column (treeview, column);
2022 get_monospace_width (GtkTreeView *treeview, GtkCellRenderer *renderer,
2029 ds_put_byte_multiple (&s, '0', char_cnt);
2030 ds_put_byte (&s, ' ');
2031 width = get_string_width (treeview, renderer, ds_cstr (&s));
2038 get_string_width (GtkTreeView *treeview, GtkCellRenderer *renderer,
2042 g_object_set (G_OBJECT (renderer), "text", string, (void *) NULL);
2043 gtk_cell_renderer_get_size (renderer, GTK_WIDGET (treeview),
2044 NULL, NULL, NULL, &width, NULL);
2048 static GtkTreeViewColumn *
2049 make_data_column (struct import_assistant *ia, GtkTreeView *tree_view,
2050 bool input, gint dict_idx)
2052 struct variable *var = NULL;
2053 struct column *column = NULL;
2055 gint content_width, header_width;
2056 GtkTreeViewColumn *tree_column;
2060 column = &ia->separators.columns[dict_idx];
2062 var = dict_get_var (ia->formats.dict, dict_idx);
2064 name = escape_underscores (input ? column->name : var_get_name (var));
2065 char_cnt = input ? column->width : var_get_print_format (var)->w;
2066 content_width = get_monospace_width (tree_view, ia->asst.fixed_renderer,
2068 header_width = get_string_width (tree_view, ia->asst.prop_renderer,
2071 tree_column = gtk_tree_view_column_new ();
2072 g_object_set_data (G_OBJECT (tree_column), "column-number",
2073 GINT_TO_POINTER (dict_idx));
2074 gtk_tree_view_column_set_title (tree_column, name);
2075 gtk_tree_view_column_pack_start (tree_column, ia->asst.fixed_renderer,
2077 gtk_tree_view_column_set_cell_data_func (
2078 tree_column, ia->asst.fixed_renderer,
2079 input ? render_input_cell : render_output_cell, ia, NULL);
2080 gtk_tree_view_column_set_sizing (tree_column, GTK_TREE_VIEW_COLUMN_FIXED);
2081 gtk_tree_view_column_set_fixed_width (tree_column, MAX (content_width,
2089 static GtkTreeView *
2090 create_data_tree_view (bool input, GtkContainer *parent,
2091 struct import_assistant *ia)
2093 GtkTreeView *tree_view;
2096 make_tree_view (ia, ia->first_line.skip_lines, &tree_view);
2097 gtk_tree_selection_set_mode (gtk_tree_view_get_selection (tree_view),
2098 GTK_SELECTION_NONE);
2100 for (i = 0; i < ia->separators.column_cnt; i++)
2101 gtk_tree_view_append_column (tree_view,
2102 make_data_column (ia, tree_view, input, i));
2104 g_object_set (G_OBJECT (tree_view), "has-tooltip", TRUE, (void *) NULL);
2105 g_signal_connect (tree_view, "query-tooltip",
2106 G_CALLBACK (input ? on_query_input_tooltip
2107 : on_query_output_tooltip), ia);
2108 gtk_tree_view_set_fixed_height_mode (tree_view, true);
2110 gtk_container_add (parent, GTK_WIDGET (tree_view));
2111 gtk_widget_show (GTK_WIDGET (tree_view));
2116 /* Increments the "watch cursor" level, setting the cursor for
2117 the assistant window to a watch face to indicate to the user
2118 that the ongoing operation may take some time. */
2120 push_watch_cursor (struct import_assistant *ia)
2122 if (++ia->asst.watch_cursor == 1)
2124 GtkWidget *widget = GTK_WIDGET (ia->asst.assistant);
2125 GdkDisplay *display = gtk_widget_get_display (widget);
2126 GdkCursor *cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
2127 gdk_window_set_cursor (widget->window, cursor);
2128 gdk_cursor_unref (cursor);
2129 gdk_display_flush (display);
2133 /* Decrements the "watch cursor" level. If the level reaches
2134 zero, the cursor is reset to its default shape. */
2136 pop_watch_cursor (struct import_assistant *ia)
2138 if (--ia->asst.watch_cursor == 0)
2140 GtkWidget *widget = GTK_WIDGET (ia->asst.assistant);
2141 gdk_window_set_cursor (widget->window, NULL);