1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2008, 2009, 2010, 2011 Free Software Foundation
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19 #include "ui/gui/text-data-import-dialog.h"
22 #include <gtk-contrib/psppire-sheet.h>
28 #include "data/data-in.h"
29 #include "data/data-out.h"
30 #include "data/format-guesser.h"
31 #include "data/value-labels.h"
32 #include "language/data-io/data-parser.h"
33 #include "language/lexer/lexer.h"
34 #include "libpspp/assertion.h"
35 #include "libpspp/i18n.h"
36 #include "libpspp/message.h"
37 #include "ui/gui/checkbox-treeview.h"
38 #include "ui/gui/descriptives-dialog.h"
39 #include "ui/gui/dialog-common.h"
40 #include "ui/gui/executor.h"
41 #include "ui/gui/helper.h"
42 #include "ui/gui/psppire-data-window.h"
43 #include "ui/gui/psppire-dialog.h"
44 #include "ui/gui/psppire-var-sheet.h"
45 #include "ui/gui/psppire-var-store.h"
46 #include "ui/gui/widget-io.h"
47 #include "ui/syntax-gen.h"
50 #include "gl/xalloc.h"
53 #define _(msgid) gettext (msgid)
54 #define N_(msgid) msgid
57 /* TextImportModel, a GtkTreeModel used by the text data import
61 TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER, /* 1-based line number in file */
62 TEXT_IMPORT_MODEL_COLUMN_LINE, /* The line from the file. */
64 typedef struct TextImportModel TextImportModel;
65 typedef struct TextImportModelClass TextImportModelClass;
67 TextImportModel *text_import_model_new (struct string *lines, size_t line_cnt,
69 gint text_import_model_iter_to_row (const GtkTreeIter *);
71 struct import_assistant;
73 /* The file to be imported. */
76 char *file_name; /* File name. */
77 unsigned long int total_lines; /* Number of lines in file. */
78 bool total_is_exact; /* Is total_lines exact (or an estimate)? */
80 /* The first several lines of the file. */
84 static bool init_file (struct import_assistant *, GtkWindow *parent);
85 static void destroy_file (struct import_assistant *);
87 /* The main body of the GTK+ assistant and related data. */
91 GtkAssistant *assistant;
93 GtkWidget *paste_button;
94 GtkWidget *reset_button;
98 GtkCellRenderer *prop_renderer;
99 GtkCellRenderer *fixed_renderer;
101 static void init_assistant (struct import_assistant *, GtkWindow *);
102 static void destroy_assistant (struct import_assistant *);
103 static GtkWidget *add_page_to_assistant (struct import_assistant *,
105 GtkAssistantPageType);
107 /* The introduction page of the assistant. */
111 GtkWidget *all_cases_button;
112 GtkWidget *n_cases_button;
113 GtkWidget *n_cases_spin;
114 GtkWidget *percent_button;
115 GtkWidget *percent_spin;
117 static void init_intro_page (struct import_assistant *);
118 static void reset_intro_page (struct import_assistant *);
120 /* Page where the user chooses the first line of data. */
121 struct first_line_page
123 int skip_lines; /* Number of initial lines to skip? */
124 bool variable_names; /* Variable names above first line of data? */
127 GtkTreeView *tree_view;
128 GtkWidget *variable_names_cb;
130 static void init_first_line_page (struct import_assistant *);
131 static void reset_first_line_page (struct import_assistant *);
133 /* Page where the user chooses field separators. */
134 struct separators_page
136 /* How to break lines into columns. */
137 struct string separators; /* Field separators. */
138 struct string quotes; /* Quote characters. */
139 bool escape; /* Doubled quotes yield a quote mark? */
141 /* The columns produced thereby. */
142 struct column *columns; /* Information about each column. */
143 size_t column_cnt; /* Number of columns. */
146 GtkWidget *custom_cb;
147 GtkWidget *custom_entry;
149 GtkWidget *quote_combo;
150 GtkEntry *quote_entry;
151 GtkWidget *escape_cb;
152 GtkTreeView *fields_tree_view;
154 /* The columns that the separators divide the data into. */
157 /* Variable name for this column. This is the variable name
158 used on the separators page; it can be overridden by the
159 user on the formats page. */
162 /* Maximum length of any row in this column. */
165 /* Contents of this column: contents[row] is the contents for
168 A null substring indicates a missing column for that row
169 (because the line contains an insufficient number of
172 contents[] elements may be substrings of the lines[]
173 strings that represent the whole lines of the file, to
174 save memory. Other elements are dynamically allocated
175 with ss_alloc_substring. */
176 struct substring *contents;
178 static void init_separators_page (struct import_assistant *);
179 static void destroy_separators_page (struct import_assistant *);
180 static void prepare_separators_page (struct import_assistant *);
181 static void reset_separators_page (struct import_assistant *);
183 /* Page where the user verifies and adjusts input formats. */
186 struct dictionary *dict;
189 GtkTreeView *data_tree_view;
190 PsppireDict *psppire_dict;
191 struct variable **modified_vars;
192 size_t modified_var_cnt;
194 static void init_formats_page (struct import_assistant *);
195 static void destroy_formats_page (struct import_assistant *);
196 static void prepare_formats_page (struct import_assistant *);
197 static void reset_formats_page (struct import_assistant *);
199 struct import_assistant
202 struct assistant asst;
203 struct intro_page intro;
204 struct first_line_page first_line;
205 struct separators_page separators;
206 struct formats_page formats;
209 static void apply_dict (const struct dictionary *, struct string *);
210 static char *generate_syntax (const struct import_assistant *);
212 static gboolean get_tooltip_location (GtkWidget *widget, gint wx, gint wy,
213 const struct import_assistant *,
214 size_t *row, size_t *column);
215 static void make_tree_view (const struct import_assistant *ia,
217 GtkTreeView **tree_view);
218 static void add_line_number_column (const struct import_assistant *,
220 static gint get_monospace_width (GtkTreeView *, GtkCellRenderer *,
222 static gint get_string_width (GtkTreeView *, GtkCellRenderer *,
224 static GtkTreeViewColumn *make_data_column (struct import_assistant *,
225 GtkTreeView *, bool input,
227 static GtkTreeView *create_data_tree_view (bool input, GtkContainer *parent,
228 struct import_assistant *);
229 static char *escape_underscores (const char *in);
230 static void push_watch_cursor (struct import_assistant *);
231 static void pop_watch_cursor (struct import_assistant *);
233 /* Pops up the Text Data Import assistant. */
235 text_data_import_assistant (GtkWindow *parent_window)
237 struct import_assistant *ia;
239 ia = xzalloc (sizeof *ia);
240 if (!init_file (ia, parent_window))
246 init_assistant (ia, parent_window);
247 init_intro_page (ia);
248 init_first_line_page (ia);
249 init_separators_page (ia);
250 init_formats_page (ia);
252 gtk_widget_show_all (GTK_WIDGET (ia->asst.assistant));
254 ia->asst.main_loop = g_main_loop_new (NULL, false);
255 g_main_loop_run (ia->asst.main_loop);
256 g_main_loop_unref (ia->asst.main_loop);
258 switch (ia->asst.response)
260 case GTK_RESPONSE_APPLY:
261 free (execute_syntax_string (generate_syntax (ia)));
263 case PSPPIRE_RESPONSE_PASTE:
264 free (paste_syntax_to_window (generate_syntax (ia)));
270 destroy_formats_page (ia);
271 destroy_separators_page (ia);
272 destroy_assistant (ia);
277 /* Emits PSPP syntax to S that applies the dictionary attributes
278 (such as missing values and value labels) of the variables in
281 apply_dict (const struct dictionary *dict, struct string *s)
283 size_t var_cnt = dict_get_var_cnt (dict);
286 for (i = 0; i < var_cnt; i++)
288 struct variable *var = dict_get_var (dict, i);
289 const char *name = var_get_name (var);
290 enum val_type type = var_get_type (var);
291 int width = var_get_width (var);
292 enum measure measure = var_get_measure (var);
293 enum alignment alignment = var_get_alignment (var);
294 const struct fmt_spec *format = var_get_print_format (var);
296 if (var_has_missing_values (var))
298 const struct missing_values *mv = var_get_missing_values (var);
301 syntax_gen_pspp (s, "MISSING VALUES %ss (", name);
302 for (j = 0; j < mv_n_values (mv); j++)
305 ds_put_cstr (s, ", ");
306 syntax_gen_value (s, mv_get_value (mv, j), width, format);
309 if (mv_has_range (mv))
312 if (mv_has_value (mv))
313 ds_put_cstr (s, ", ");
314 mv_get_range (mv, &low, &high);
315 syntax_gen_num_range (s, low, high, format);
317 ds_put_cstr (s, ").\n");
319 if (var_has_value_labels (var))
321 const struct val_labs *vls = var_get_value_labels (var);
322 const struct val_lab **labels = val_labs_sorted (vls);
323 size_t n_labels = val_labs_count (vls);
326 syntax_gen_pspp (s, "VALUE LABELS %ss", name);
327 for (i = 0; i < n_labels; i++)
329 const struct val_lab *vl = labels[i];
330 ds_put_cstr (s, "\n ");
331 syntax_gen_value (s, &vl->value, width, format);
332 ds_put_byte (s, ' ');
333 syntax_gen_string (s, ss_cstr (val_lab_get_label (vl)));
336 ds_put_cstr (s, ".\n");
338 if (var_has_label (var))
339 syntax_gen_pspp (s, "VARIABLE LABELS %ss %sq.\n",
340 name, var_get_label (var));
341 if (measure != var_default_measure (type))
342 syntax_gen_pspp (s, "VARIABLE LEVEL %ss (%ss).\n",
344 (measure == MEASURE_NOMINAL ? "NOMINAL"
345 : measure == MEASURE_ORDINAL ? "ORDINAL"
347 if (alignment != var_default_alignment (type))
348 syntax_gen_pspp (s, "VARIABLE ALIGNMENT %ss (%ss).\n",
350 (alignment == ALIGN_LEFT ? "LEFT"
351 : alignment == ALIGN_CENTRE ? "CENTER"
353 if (var_get_display_width (var) != var_default_display_width (width))
354 syntax_gen_pspp (s, "VARIABLE WIDTH %ss (%d).\n",
355 name, var_get_display_width (var));
359 /* Generates and returns PSPP syntax to execute the import
360 operation described by IA. The caller must free the syntax
363 generate_syntax (const struct import_assistant *ia)
365 struct string s = DS_EMPTY_INITIALIZER;
374 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (
375 ia->intro.n_cases_button)))
376 ds_put_format (&s, " /IMPORTCASES=FIRST %d\n",
377 gtk_spin_button_get_value_as_int (
378 GTK_SPIN_BUTTON (ia->intro.n_cases_spin)));
379 else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (
380 ia->intro.percent_button)))
381 ds_put_format (&s, " /IMPORTCASES=PERCENT %d\n",
382 gtk_spin_button_get_value_as_int (
383 GTK_SPIN_BUTTON (ia->intro.percent_spin)));
385 ds_put_cstr (&s, " /IMPORTCASES=ALL\n");
387 " /ARRANGEMENT=DELIMITED\n"
389 if (ia->first_line.skip_lines > 0)
390 ds_put_format (&s, " /FIRSTCASE=%d\n", ia->first_line.skip_lines + 1);
391 ds_put_cstr (&s, " /DELIMITERS=\"");
392 if (ds_find_byte (&ia->separators.separators, '\t') != SIZE_MAX)
393 ds_put_cstr (&s, "\\t");
394 if (ds_find_byte (&ia->separators.separators, '\\') != SIZE_MAX)
395 ds_put_cstr (&s, "\\\\");
396 for (i = 0; i < ds_length (&ia->separators.separators); i++)
398 char c = ds_at (&ia->separators.separators, i);
400 ds_put_cstr (&s, "\"\"");
401 else if (c != '\t' && c != '\\')
404 ds_put_cstr (&s, "\"\n");
405 if (!ds_is_empty (&ia->separators.quotes))
406 syntax_gen_pspp (&s, " /QUALIFIER=%sq\n", ds_cstr (&ia->separators.quotes));
407 if (!ds_is_empty (&ia->separators.quotes) && ia->separators.escape)
408 ds_put_cstr (&s, " /ESCAPE\n");
409 ds_put_cstr (&s, " /VARIABLES=\n");
411 var_cnt = dict_get_var_cnt (ia->formats.dict);
412 for (i = 0; i < var_cnt; i++)
414 struct variable *var = dict_get_var (ia->formats.dict, i);
415 char format_string[FMT_STRING_LEN_MAX + 1];
416 fmt_to_string (var_get_print_format (var), format_string);
417 ds_put_format (&s, " %s %s%s\n",
418 var_get_name (var), format_string,
419 i == var_cnt - 1 ? "." : "");
422 apply_dict (ia->formats.dict, &s);
427 /* Choosing a file and reading it. */
429 static char *choose_file (GtkWindow *parent_window);
431 /* Obtains the file to import from the user and initializes IA's
432 file substructure. PARENT_WINDOW must be the window to use
433 as the file chooser window's parent.
435 Returns true if successful, false if the file name could not
436 be obtained or the file could not be read. */
438 init_file (struct import_assistant *ia, GtkWindow *parent_window)
440 struct file *file = &ia->file;
441 enum { MAX_PREVIEW_LINES = 1000 }; /* Max number of lines to read. */
442 enum { MAX_LINE_LEN = 16384 }; /* Max length of an acceptable line. */
445 file->file_name = choose_file (parent_window);
446 if (file->file_name == NULL)
449 stream = fopen (file->file_name, "r");
452 msg (ME, _("Could not open `%s': %s"),
453 file->file_name, strerror (errno));
457 file->lines = xnmalloc (MAX_PREVIEW_LINES, sizeof *file->lines);
458 for (; file->line_cnt < MAX_PREVIEW_LINES; file->line_cnt++)
460 struct string *line = &file->lines[file->line_cnt];
462 ds_init_empty (line);
463 if (!ds_read_line (line, stream, MAX_LINE_LEN))
467 else if (ferror (stream))
468 msg (ME, _("Error reading `%s': %s"),
469 file->file_name, strerror (errno));
471 msg (ME, _("Failed to read `%s', because it contains a line "
472 "over %d bytes long and therefore appears not to be "
474 file->file_name, MAX_LINE_LEN);
479 ds_chomp (line, '\n');
480 ds_chomp (line, '\r');
483 if (file->line_cnt == 0)
485 msg (ME, _("`%s' is empty."), file->file_name);
491 /* Estimate the number of lines in the file. */
492 if (file->line_cnt < MAX_PREVIEW_LINES)
493 file->total_lines = file->line_cnt;
497 off_t position = ftello (stream);
498 if (fstat (fileno (stream), &s) == 0 && position > 0)
499 file->total_lines = (double) file->line_cnt / position * s.st_size;
501 file->total_lines = 0;
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);
520 /* Obtains the file to read from the user and returns the name of
521 the file as a string that must be freed with g_free if
522 successful, otherwise a null pointer. PARENT_WINDOW must be
523 the window to use as the file chooser window's parent. */
525 choose_file (GtkWindow *parent_window)
530 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 switch (gtk_dialog_run (GTK_DIALOG (dialog)))
539 case GTK_RESPONSE_ACCEPT:
540 file_name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
546 gtk_widget_destroy (dialog);
553 static void close_assistant (struct import_assistant *, int response);
554 static void on_prepare (GtkAssistant *assistant, GtkWidget *page,
555 struct import_assistant *);
556 static void on_cancel (GtkAssistant *assistant, struct import_assistant *);
557 static void on_close (GtkAssistant *assistant, struct import_assistant *);
558 static void on_paste (GtkButton *button, struct import_assistant *);
559 static void on_reset (GtkButton *button, struct import_assistant *);
560 static void close_assistant (struct import_assistant *, int response);
562 /* Initializes IA's asst substructure. PARENT_WINDOW must be the
563 window to use as the assistant window's parent. */
565 init_assistant (struct import_assistant *ia, GtkWindow *parent_window)
567 struct assistant *a = &ia->asst;
569 a->builder = builder_new ("text-data-import.ui");
570 a->assistant = GTK_ASSISTANT (gtk_assistant_new ());
571 g_signal_connect (a->assistant, "prepare", G_CALLBACK (on_prepare), ia);
572 g_signal_connect (a->assistant, "cancel", G_CALLBACK (on_cancel), ia);
573 g_signal_connect (a->assistant, "close", G_CALLBACK (on_close), ia);
574 a->paste_button = gtk_button_new_from_stock (GTK_STOCK_PASTE);
575 gtk_assistant_add_action_widget (a->assistant, a->paste_button);
576 g_signal_connect (a->paste_button, "clicked", G_CALLBACK (on_paste), ia);
577 a->reset_button = gtk_button_new_from_stock ("pspp-stock-reset");
578 gtk_assistant_add_action_widget (a->assistant, a->reset_button);
579 g_signal_connect (a->reset_button, "clicked", G_CALLBACK (on_reset), ia);
580 gtk_window_set_title (GTK_WINDOW (a->assistant),
581 _("Importing Delimited Text Data"));
582 gtk_window_set_transient_for (GTK_WINDOW (a->assistant), parent_window);
583 gtk_window_set_icon_name (GTK_WINDOW (a->assistant), "psppicon");
585 a->prop_renderer = gtk_cell_renderer_text_new ();
586 g_object_ref_sink (a->prop_renderer);
587 a->fixed_renderer = gtk_cell_renderer_text_new ();
588 g_object_ref_sink (a->fixed_renderer);
589 g_object_set (G_OBJECT (a->fixed_renderer),
590 "family", "Monospace",
594 /* Frees IA's asst substructure. */
596 destroy_assistant (struct import_assistant *ia)
598 struct assistant *a = &ia->asst;
600 g_object_unref (a->prop_renderer);
601 g_object_unref (a->fixed_renderer);
602 g_object_unref (a->builder);
605 /* Appends a page of the given TYPE, with PAGE as its content, to
606 the GtkAssistant encapsulated by IA. Returns the GtkWidget
607 that represents the page. */
609 add_page_to_assistant (struct import_assistant *ia,
610 GtkWidget *page, GtkAssistantPageType type)
616 title = gtk_window_get_title (GTK_WINDOW (page));
617 title_copy = xstrdup (title ? title : "");
619 content = gtk_bin_get_child (GTK_BIN (page));
621 g_object_ref (content);
622 gtk_container_remove (GTK_CONTAINER (page), content);
624 gtk_widget_destroy (page);
626 gtk_assistant_append_page (ia->asst.assistant, content);
627 gtk_assistant_set_page_type (ia->asst.assistant, content, type);
628 gtk_assistant_set_page_title (ia->asst.assistant, content, title_copy);
629 gtk_assistant_set_page_complete (ia->asst.assistant, content, true);
636 /* Called just before PAGE is displayed as the current page of
637 ASSISTANT, this updates IA content according to the new
640 on_prepare (GtkAssistant *assistant, GtkWidget *page,
641 struct import_assistant *ia)
644 if (gtk_assistant_get_page_type (assistant, page)
645 == GTK_ASSISTANT_PAGE_CONFIRM)
646 gtk_widget_grab_focus (assistant->apply);
648 gtk_widget_grab_focus (assistant->forward);
650 if (page == ia->separators.page)
651 prepare_separators_page (ia);
652 else if (page == ia->formats.page)
653 prepare_formats_page (ia);
655 gtk_widget_show (ia->asst.reset_button);
656 if (page == ia->formats.page)
657 gtk_widget_show (ia->asst.paste_button);
659 gtk_widget_hide (ia->asst.paste_button);
662 /* Called when the Cancel button in the assistant is clicked. */
664 on_cancel (GtkAssistant *assistant, struct import_assistant *ia)
666 close_assistant (ia, GTK_RESPONSE_CANCEL);
669 /* Called when the Apply button on the last page of the assistant
672 on_close (GtkAssistant *assistant, struct import_assistant *ia)
674 close_assistant (ia, GTK_RESPONSE_APPLY);
677 /* Called when the Paste button on the last page of the assistant
680 on_paste (GtkButton *button, struct import_assistant *ia)
682 close_assistant (ia, PSPPIRE_RESPONSE_PASTE);
685 /* Called when the Reset button is clicked. */
687 on_reset (GtkButton *button, struct import_assistant *ia)
689 gint page_num = gtk_assistant_get_current_page (ia->asst.assistant);
690 GtkWidget *page = gtk_assistant_get_nth_page (ia->asst.assistant, page_num);
692 if (page == ia->intro.page)
693 reset_intro_page (ia);
694 else if (page == ia->first_line.page)
695 reset_first_line_page (ia);
696 else if (page == ia->separators.page)
697 reset_separators_page (ia);
698 else if (page == ia->formats.page)
699 reset_formats_page (ia);
702 /* Causes the assistant to close, returning RESPONSE for
703 interpretation by text_data_import_assistant. */
705 close_assistant (struct import_assistant *ia, int response)
707 ia->asst.response = response;
708 g_main_loop_quit (ia->asst.main_loop);
709 gtk_widget_hide (GTK_WIDGET (ia->asst.assistant));
712 /* The "intro" page of the assistant. */
714 static void on_intro_amount_changed (struct import_assistant *);
716 /* Initializes IA's intro substructure. */
718 init_intro_page (struct import_assistant *ia)
720 GtkBuilder *builder = ia->asst.builder;
721 struct intro_page *p = &ia->intro;
723 GtkWidget *hbox_n_cases ;
724 GtkWidget *hbox_percent ;
728 p->n_cases_spin = gtk_spin_button_new_with_range (0, INT_MAX, 100);
730 hbox_n_cases = widget_scanf (_("Only the first %4d cases"), &p->n_cases_spin);
732 table = get_widget_assert (builder, "button-table");
734 gtk_table_attach_defaults (GTK_TABLE (table), hbox_n_cases,
738 p->percent_spin = gtk_spin_button_new_with_range (0, 100, 10);
740 hbox_percent = widget_scanf (_("Only the first %3d %% of file (approximately)"), &p->percent_spin);
742 gtk_table_attach_defaults (GTK_TABLE (table), hbox_percent,
746 p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Intro"),
747 GTK_ASSISTANT_PAGE_INTRO);
749 p->all_cases_button = get_widget_assert (builder, "import-all-cases");
751 p->n_cases_button = get_widget_assert (builder, "import-n-cases");
753 p->percent_button = get_widget_assert (builder, "import-percent");
755 g_signal_connect_swapped (p->all_cases_button, "toggled",
756 G_CALLBACK (on_intro_amount_changed), ia);
757 g_signal_connect_swapped (p->n_cases_button, "toggled",
758 G_CALLBACK (on_intro_amount_changed), ia);
759 g_signal_connect_swapped (p->percent_button, "toggled",
760 G_CALLBACK (on_intro_amount_changed), ia);
762 on_intro_amount_changed (ia);
765 ds_put_cstr (&s, _("This assistant will guide you through the process of "
766 "importing data into PSPP from a text file with one line "
767 "per case, in which fields are separated by tabs, "
768 "commas, or other delimiters.\n\n"));
769 if (ia->file.total_is_exact)
771 &s, ngettext ("The selected file contains %zu line of text. ",
772 "The selected file contains %zu lines of text. ",
775 else if (ia->file.total_lines > 0)
779 "The selected file contains approximately %lu line of text. ",
780 "The selected file contains approximately %lu lines of text. ",
781 ia->file.total_lines),
782 ia->file.total_lines);
785 "Only the first %zu line of the file will be shown for "
786 "preview purposes in the following screens. ",
787 "Only the first %zu lines of the file will be shown for "
788 "preview purposes in the following screens. ",
792 ds_put_cstr (&s, _("You may choose below how much of the file should "
793 "actually be imported."));
794 gtk_label_set_text (GTK_LABEL (get_widget_assert (builder, "intro-label")),
799 /* Resets IA's intro page to its initial state. */
801 reset_intro_page (struct import_assistant *ia)
803 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->intro.all_cases_button),
807 /* Called when one of the radio buttons is clicked. */
809 on_intro_amount_changed (struct import_assistant *ia)
811 struct intro_page *p = &ia->intro;
813 gtk_widget_set_sensitive (p->n_cases_spin,
814 gtk_toggle_button_get_active (
815 GTK_TOGGLE_BUTTON (p->n_cases_button)));
817 gtk_widget_set_sensitive (p->percent_spin,
818 gtk_toggle_button_get_active (
819 GTK_TOGGLE_BUTTON (p->percent_button)));
822 /* The "first line" page of the assistant. */
824 static GtkTreeView *create_lines_tree_view (GtkContainer *parent_window,
825 struct import_assistant *);
826 static void on_first_line_change (GtkTreeSelection *,
827 struct import_assistant *);
828 static void on_variable_names_cb_toggle (GtkToggleButton *,
829 struct import_assistant *);
830 static void set_first_line (struct import_assistant *);
831 static void get_first_line (struct import_assistant *);
833 /* Initializes IA's first_line substructure. */
835 init_first_line_page (struct import_assistant *ia)
837 struct first_line_page *p = &ia->first_line;
838 GtkBuilder *builder = ia->asst.builder;
840 p->page = add_page_to_assistant (ia, get_widget_assert (builder, "FirstLine"),
841 GTK_ASSISTANT_PAGE_CONTENT);
842 gtk_widget_destroy (get_widget_assert (builder, "first-line"));
843 p->tree_view = create_lines_tree_view (
844 GTK_CONTAINER (get_widget_assert (builder, "first-line-scroller")), ia);
845 p->variable_names_cb = get_widget_assert (builder, "variable-names");
846 gtk_tree_selection_set_mode (
847 gtk_tree_view_get_selection (GTK_TREE_VIEW (p->tree_view)),
848 GTK_SELECTION_BROWSE);
850 g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (p->tree_view)),
851 "changed", G_CALLBACK (on_first_line_change), ia);
852 g_signal_connect (p->variable_names_cb, "toggled",
853 G_CALLBACK (on_variable_names_cb_toggle), ia);
856 /* Resets the first_line page to its initial content. */
858 reset_first_line_page (struct import_assistant *ia)
860 ia->first_line.skip_lines = 0;
861 ia->first_line.variable_names = false;
865 /* Creates and returns a tree view that contains each of the
866 lines in IA's file as a row. */
868 create_lines_tree_view (GtkContainer *parent, struct import_assistant *ia)
870 GtkTreeView *tree_view;
871 GtkTreeViewColumn *column;
872 size_t max_line_length;
873 gint content_width, header_width;
875 gchar *title = _("Text");
877 make_tree_view (ia, 0, &tree_view);
879 column = gtk_tree_view_column_new_with_attributes
881 title, ia->asst.fixed_renderer,
882 "text", TEXT_IMPORT_MODEL_COLUMN_LINE,
885 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
888 for (i = 0; i < ia->file.line_cnt; i++)
890 size_t w = ds_length (&ia->file.lines[i]);
891 max_line_length = MAX (max_line_length, w);
894 content_width = get_monospace_width (tree_view, ia->asst.fixed_renderer,
896 header_width = get_string_width (tree_view, ia->asst.prop_renderer, title);
897 gtk_tree_view_column_set_fixed_width (column, MAX (content_width,
899 gtk_tree_view_append_column (tree_view, column);
901 gtk_tree_view_set_fixed_height_mode (tree_view, true);
903 gtk_container_add (parent, GTK_WIDGET (tree_view));
904 gtk_widget_show (GTK_WIDGET (tree_view));
909 /* Called when the line selected in the first_line tree view
912 on_first_line_change (GtkTreeSelection *selection UNUSED,
913 struct import_assistant *ia)
918 /* Called when the checkbox that indicates whether variable
919 names are in the row above the first line is toggled. */
921 on_variable_names_cb_toggle (GtkToggleButton *variable_names_cb UNUSED,
922 struct import_assistant *ia)
927 /* Sets the widgets to match IA's first_line substructure. */
929 set_first_line (struct import_assistant *ia)
933 path = gtk_tree_path_new_from_indices (ia->first_line.skip_lines, -1);
934 gtk_tree_view_set_cursor (GTK_TREE_VIEW (ia->first_line.tree_view),
936 gtk_tree_path_free (path);
938 gtk_toggle_button_set_active (
939 GTK_TOGGLE_BUTTON (ia->first_line.variable_names_cb),
940 ia->first_line.variable_names);
941 gtk_widget_set_sensitive (ia->first_line.variable_names_cb,
942 ia->first_line.skip_lines > 0);
945 /* Sets IA's first_line substructure to match the widgets. */
947 get_first_line (struct import_assistant *ia)
949 GtkTreeSelection *selection;
953 selection = gtk_tree_view_get_selection (ia->first_line.tree_view);
954 if (gtk_tree_selection_get_selected (selection, &model, &iter))
956 GtkTreePath *path = gtk_tree_model_get_path (model, &iter);
957 int row = gtk_tree_path_get_indices (path)[0];
958 gtk_tree_path_free (path);
960 ia->first_line.skip_lines = row;
961 ia->first_line.variable_names =
962 (ia->first_line.skip_lines > 0
963 && gtk_toggle_button_get_active (
964 GTK_TOGGLE_BUTTON (ia->first_line.variable_names_cb)));
966 gtk_widget_set_sensitive (ia->first_line.variable_names_cb,
967 ia->first_line.skip_lines > 0);
970 /* The "separators" page of the assistant. */
972 static void revise_fields_preview (struct import_assistant *ia);
973 static void choose_likely_separators (struct import_assistant *ia);
974 static void find_commonest_chars (unsigned long int histogram[UCHAR_MAX + 1],
975 const char *targets, const char *def,
976 struct string *result);
977 static void clear_fields (struct import_assistant *ia);
978 static void revise_fields_preview (struct import_assistant *);
979 static void set_separators (struct import_assistant *);
980 static void get_separators (struct import_assistant *);
981 static void on_separators_custom_entry_notify (GObject *UNUSED,
983 struct import_assistant *);
984 static void on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
985 struct import_assistant *);
986 static void on_quote_combo_change (GtkComboBox *combo,
987 struct import_assistant *);
988 static void on_quote_cb_toggle (GtkToggleButton *quote_cb,
989 struct import_assistant *);
990 static void on_separator_toggle (GtkToggleButton *, struct import_assistant *);
991 static void render_input_cell (GtkTreeViewColumn *tree_column,
992 GtkCellRenderer *cell,
993 GtkTreeModel *model, GtkTreeIter *iter,
995 static gboolean on_query_input_tooltip (GtkWidget *widget, gint wx, gint wy,
996 gboolean keyboard_mode UNUSED,
998 struct import_assistant *);
1000 /* A common field separator and its identifying name. */
1003 const char *name; /* Name (for use with get_widget_assert). */
1004 int c; /* Separator character. */
1007 /* All the separators in the dialog box. */
1008 static const struct separator separators[] =
1020 #define SEPARATOR_CNT (sizeof separators / sizeof *separators)
1023 set_quote_list (GtkComboBoxEntry *cb)
1025 GtkListStore *list = gtk_list_store_new (1, G_TYPE_STRING);
1028 const gchar *seperator[3] = {"'\"", "\'", "\""};
1030 for (i = 0; i < 3; i++)
1032 const gchar *s = seperator[i];
1034 /* Add a new row to the model */
1035 gtk_list_store_append (list, &iter);
1036 gtk_list_store_set (list, &iter,
1042 gtk_combo_box_set_model (GTK_COMBO_BOX (cb), GTK_TREE_MODEL (list));
1044 gtk_combo_box_entry_set_text_column (cb, 0);
1047 /* Initializes IA's separators substructure. */
1049 init_separators_page (struct import_assistant *ia)
1051 GtkBuilder *builder = ia->asst.builder;
1052 struct separators_page *p = &ia->separators;
1055 choose_likely_separators (ia);
1057 p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Separators"),
1058 GTK_ASSISTANT_PAGE_CONTENT);
1059 p->custom_cb = get_widget_assert (builder, "custom-cb");
1060 p->custom_entry = get_widget_assert (builder, "custom-entry");
1061 p->quote_combo = get_widget_assert (builder, "quote-combo");
1062 p->quote_entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (p->quote_combo)));
1063 p->quote_cb = get_widget_assert (builder, "quote-cb");
1064 p->escape_cb = get_widget_assert (builder, "escape");
1066 set_separators (ia);
1067 set_quote_list (GTK_COMBO_BOX_ENTRY (p->quote_combo));
1068 p->fields_tree_view = GTK_TREE_VIEW (get_widget_assert (builder, "fields"));
1069 g_signal_connect (p->quote_combo, "changed",
1070 G_CALLBACK (on_quote_combo_change), ia);
1071 g_signal_connect (p->quote_cb, "toggled",
1072 G_CALLBACK (on_quote_cb_toggle), ia);
1073 g_signal_connect (p->custom_entry, "notify::text",
1074 G_CALLBACK (on_separators_custom_entry_notify), ia);
1075 g_signal_connect (p->custom_cb, "toggled",
1076 G_CALLBACK (on_separators_custom_cb_toggle), ia);
1077 for (i = 0; i < SEPARATOR_CNT; i++)
1078 g_signal_connect (get_widget_assert (builder, separators[i].name),
1079 "toggled", G_CALLBACK (on_separator_toggle), ia);
1080 g_signal_connect (p->escape_cb, "toggled",
1081 G_CALLBACK (on_separator_toggle), ia);
1084 /* Frees IA's separators substructure. */
1086 destroy_separators_page (struct import_assistant *ia)
1088 struct separators_page *s = &ia->separators;
1090 ds_destroy (&s->separators);
1091 ds_destroy (&s->quotes);
1095 /* Called just before the separators page becomes visible in the
1098 prepare_separators_page (struct import_assistant *ia)
1100 revise_fields_preview (ia);
1103 /* Called when the Reset button is clicked on the separators
1104 page, resets the separators to the defaults. */
1106 reset_separators_page (struct import_assistant *ia)
1108 choose_likely_separators (ia);
1109 set_separators (ia);
1112 /* Frees and clears the column data in IA's separators
1115 clear_fields (struct import_assistant *ia)
1117 struct separators_page *s = &ia->separators;
1119 if (s->column_cnt > 0)
1124 for (row = 0; row < ia->file.line_cnt; row++)
1126 const struct string *line = &ia->file.lines[row];
1127 const char *line_start = ds_data (line);
1128 const char *line_end = ds_end (line);
1130 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1132 char *s = ss_data (col->contents[row]);
1133 if (!(s >= line_start && s <= line_end))
1134 ss_dealloc (&col->contents[row]);
1138 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1141 free (col->contents);
1150 /* Breaks the file data in IA into columns based on the
1151 separators set in IA's separators substructure. */
1153 split_fields (struct import_assistant *ia)
1155 struct separators_page *s = &ia->separators;
1156 size_t columns_allocated;
1162 /* Is space in the set of separators? */
1163 space_sep = ss_find_byte (ds_ss (&s->separators), ' ') != SIZE_MAX;
1165 /* Split all the lines, not just those from
1166 ia->first_line.skip_lines on, so that we split the line that
1167 contains variables names if ia->first_line.variable_names is
1169 columns_allocated = 0;
1170 for (row = 0; row < ia->file.line_cnt; row++)
1172 struct string *line = &ia->file.lines[row];
1173 struct substring text = ds_ss (line);
1176 for (column_idx = 0; ; column_idx++)
1178 struct substring field;
1179 struct column *column;
1182 ss_ltrim (&text, ss_cstr (" "));
1183 if (ss_is_empty (text))
1185 if (column_idx != 0)
1189 else if (!ds_is_empty (&s->quotes)
1190 && ds_find_byte (&s->quotes, text.string[0]) != SIZE_MAX)
1192 int quote = ss_get_byte (&text);
1194 ss_get_until (&text, quote, &field);
1201 while ((c = ss_get_byte (&text)) != EOF)
1203 ds_put_byte (&s, c);
1204 else if (ss_match_byte (&text, quote))
1205 ds_put_byte (&s, quote);
1212 ss_get_bytes (&text, ss_cspan (text, ds_ss (&s->separators)),
1215 if (column_idx >= s->column_cnt)
1217 struct column *column;
1219 if (s->column_cnt >= columns_allocated)
1220 s->columns = x2nrealloc (s->columns, &columns_allocated,
1221 sizeof *s->columns);
1222 column = &s->columns[s->column_cnt++];
1223 column->name = NULL;
1225 column->contents = xcalloc (ia->file.line_cnt,
1226 sizeof *column->contents);
1228 column = &s->columns[column_idx];
1229 column->contents[row] = field;
1230 if (ss_length (field) > column->width)
1231 column->width = ss_length (field);
1234 ss_ltrim (&text, ss_cstr (" "));
1235 if (ss_is_empty (text))
1237 if (ss_find_byte (ds_ss (&s->separators), ss_first (text))
1239 ss_advance (&text, 1);
1244 /* Chooses a name for each column on the separators page */
1246 choose_column_names (struct import_assistant *ia)
1248 const struct first_line_page *f = &ia->first_line;
1249 struct separators_page *s = &ia->separators;
1250 struct dictionary *dict;
1251 unsigned long int generated_name_count = 0;
1255 dict = dict_create ();
1256 name_row = f->variable_names && f->skip_lines ? f->skip_lines : 0;
1257 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1261 hint = name_row ? ss_xstrdup (col->contents[name_row - 1]) : NULL;
1262 name = dict_make_unique_var_name (dict, hint, &generated_name_count);
1266 dict_create_var_assert (dict, name, 0);
1268 dict_destroy (dict);
1271 /* Picks the most likely separator and quote characters based on
1274 choose_likely_separators (struct import_assistant *ia)
1276 unsigned long int histogram[UCHAR_MAX + 1] = { 0 };
1279 /* Construct a histogram of all the characters used in the
1281 for (row = 0; row < ia->file.line_cnt; row++)
1283 struct substring line = ds_ss (&ia->file.lines[row]);
1284 size_t length = ss_length (line);
1286 for (i = 0; i < length; i++)
1287 histogram[(unsigned char) line.string[i]]++;
1290 find_commonest_chars (histogram, "\"'", "", &ia->separators.quotes);
1291 find_commonest_chars (histogram, ",;:/|!\t-", ",",
1292 &ia->separators.separators);
1293 ia->separators.escape = true;
1296 /* Chooses the most common character among those in TARGETS,
1297 based on the frequency data in HISTOGRAM, and stores it in
1298 RESULT. If there is a tie for the most common character among
1299 those in TARGETS, the earliest character is chosen. If none
1300 of the TARGETS appear at all, then DEF is used as a
1303 find_commonest_chars (unsigned long int histogram[UCHAR_MAX + 1],
1304 const char *targets, const char *def,
1305 struct string *result)
1307 unsigned char max = 0;
1308 unsigned long int max_count = 0;
1310 for (; *targets != '\0'; targets++)
1312 unsigned char c = *targets;
1313 unsigned long int count = histogram[c];
1314 if (count > max_count)
1323 ds_put_byte (result, max);
1326 ds_assign_cstr (result, def);
1329 /* Revises the contents of the fields tree view based on the
1330 currently chosen set of separators. */
1332 revise_fields_preview (struct import_assistant *ia)
1336 push_watch_cursor (ia);
1338 w = GTK_WIDGET (ia->separators.fields_tree_view);
1339 gtk_widget_destroy (w);
1340 get_separators (ia);
1342 choose_column_names (ia);
1343 ia->separators.fields_tree_view = create_data_tree_view (
1345 GTK_CONTAINER (get_widget_assert (ia->asst.builder, "fields-scroller")),
1348 pop_watch_cursor (ia);
1351 /* Sets the widgets to match IA's separators substructure. */
1353 set_separators (struct import_assistant *ia)
1355 struct separators_page *s = &ia->separators;
1357 struct string custom;
1362 ds_init_empty (&custom);
1364 for (i = 0; i < ds_length (&s->separators); i++)
1366 unsigned char c = ds_at (&s->separators, i);
1369 for (j = 0; j < SEPARATOR_CNT; j++)
1371 const struct separator *s = &separators[j];
1379 ds_put_byte (&custom, c);
1383 for (i = 0; i < SEPARATOR_CNT; i++)
1385 const struct separator *s = &separators[i];
1386 GtkWidget *button = get_widget_assert (ia->asst.builder, s->name);
1387 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
1388 (seps & (1u << i)) != 0);
1390 any_custom = !ds_is_empty (&custom);
1391 gtk_entry_set_text (GTK_ENTRY (s->custom_entry), ds_cstr (&custom));
1392 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->custom_cb),
1394 gtk_widget_set_sensitive (s->custom_entry, any_custom);
1395 ds_destroy (&custom);
1397 any_quotes = !ds_is_empty (&s->quotes);
1399 gtk_entry_set_text (s->quote_entry,
1400 any_quotes ? ds_cstr (&s->quotes) : "\"");
1401 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->quote_cb),
1403 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->escape_cb),
1405 gtk_widget_set_sensitive (s->quote_combo, any_quotes);
1406 gtk_widget_set_sensitive (s->escape_cb, any_quotes);
1409 /* Sets IA's separators substructure to match the widgets. */
1411 get_separators (struct import_assistant *ia)
1413 struct separators_page *s = &ia->separators;
1416 ds_clear (&s->separators);
1417 for (i = 0; i < SEPARATOR_CNT; i++)
1419 const struct separator *sep = &separators[i];
1420 GtkWidget *button = get_widget_assert (ia->asst.builder, sep->name);
1421 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
1422 ds_put_byte (&s->separators, sep->c);
1425 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->custom_cb)))
1426 ds_put_cstr (&s->separators,
1427 gtk_entry_get_text (GTK_ENTRY (s->custom_entry)));
1429 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->quote_cb)))
1431 gchar *text = gtk_combo_box_get_active_text (
1432 GTK_COMBO_BOX (s->quote_combo));
1433 ds_assign_cstr (&s->quotes, text);
1437 ds_clear (&s->quotes);
1438 s->escape = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->escape_cb));
1441 /* Called when the user changes the entry field for custom
1444 on_separators_custom_entry_notify (GObject *gobject UNUSED,
1445 GParamSpec *arg1 UNUSED,
1446 struct import_assistant *ia)
1448 revise_fields_preview (ia);
1451 /* Called when the user toggles the checkbox that enables custom
1454 on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
1455 struct import_assistant *ia)
1457 bool is_active = gtk_toggle_button_get_active (custom_cb);
1458 gtk_widget_set_sensitive (ia->separators.custom_entry, is_active);
1459 revise_fields_preview (ia);
1462 /* Called when the user changes the selection in the combo box
1463 that selects a quote character. */
1465 on_quote_combo_change (GtkComboBox *combo, struct import_assistant *ia)
1467 revise_fields_preview (ia);
1470 /* Called when the user toggles the checkbox that enables
1473 on_quote_cb_toggle (GtkToggleButton *quote_cb, struct import_assistant *ia)
1475 bool is_active = gtk_toggle_button_get_active (quote_cb);
1476 gtk_widget_set_sensitive (ia->separators.quote_combo, is_active);
1477 gtk_widget_set_sensitive (ia->separators.escape_cb, is_active);
1478 revise_fields_preview (ia);
1481 /* Called when the user toggles one of the separators
1484 on_separator_toggle (GtkToggleButton *toggle UNUSED,
1485 struct import_assistant *ia)
1487 revise_fields_preview (ia);
1490 /* Called to render one of the cells in the fields preview tree
1493 render_input_cell (GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1494 GtkTreeModel *model, GtkTreeIter *iter,
1497 struct import_assistant *ia = ia_;
1498 struct substring field;
1502 column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1504 row = text_import_model_iter_to_row (iter) + ia->first_line.skip_lines;
1505 field = ia->separators.columns[column].contents[row];
1506 if (field.string != NULL)
1508 GValue text = {0, };
1509 g_value_init (&text, G_TYPE_STRING);
1510 g_value_take_string (&text, ss_xstrdup (field));
1511 g_object_set_property (G_OBJECT (cell), "text", &text);
1512 g_value_unset (&text);
1513 g_object_set (cell, "background-set", FALSE, (void *) NULL);
1518 "background", "red",
1519 "background-set", TRUE,
1523 /* Called to render a tooltip on one of the cells in the fields
1524 preview tree view. */
1526 on_query_input_tooltip (GtkWidget *widget, gint wx, gint wy,
1527 gboolean keyboard_mode UNUSED,
1528 GtkTooltip *tooltip, struct import_assistant *ia)
1532 if (!get_tooltip_location (widget, wx, wy, ia, &row, &column))
1535 if (ia->separators.columns[column].contents[row].string != NULL)
1538 gtk_tooltip_set_text (tooltip,
1539 _("This input line has too few separators "
1540 "to fill in this field."));
1544 /* The "formats" page of the assistant. */
1546 static void on_variable_change (PsppireDict *dict, int idx,
1547 struct import_assistant *);
1548 static void clear_modified_vars (struct import_assistant *);
1550 /* Initializes IA's formats substructure. */
1552 init_formats_page (struct import_assistant *ia)
1554 GtkBuilder *builder = ia->asst.builder;
1555 struct formats_page *p = &ia->formats;
1557 p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Formats"),
1558 GTK_ASSISTANT_PAGE_CONFIRM);
1559 p->data_tree_view = GTK_TREE_VIEW (get_widget_assert (builder, "data"));
1560 p->modified_vars = NULL;
1561 p->modified_var_cnt = 0;
1565 /* Frees IA's formats substructure. */
1567 destroy_formats_page (struct import_assistant *ia)
1569 struct formats_page *p = &ia->formats;
1571 if (p->psppire_dict != NULL)
1573 /* This destroys p->dict also. */
1574 g_object_unref (p->psppire_dict);
1576 clear_modified_vars (ia);
1579 /* Called just before the formats page of the assistant is
1582 prepare_formats_page (struct import_assistant *ia)
1584 struct dictionary *dict;
1585 PsppireDict *psppire_dict;
1586 PsppireVarStore *var_store;
1587 GtkBin *vars_scroller;
1588 GtkWidget *old_var_sheet;
1589 PsppireVarSheet *var_sheet;
1590 struct separators_page *s = &ia->separators;
1591 struct formats_page *p = &ia->formats;
1592 struct fmt_guesser *fg;
1593 unsigned long int number = 0;
1596 push_watch_cursor (ia);
1598 dict = dict_create ();
1599 fg = fmt_guesser_create ();
1600 for (column_idx = 0; column_idx < s->column_cnt; column_idx++)
1602 struct variable *modified_var;
1604 modified_var = (column_idx < p->modified_var_cnt
1605 ? p->modified_vars[column_idx] : NULL);
1606 if (modified_var == NULL)
1608 struct column *column = &s->columns[column_idx];
1609 struct variable *var;
1610 struct fmt_spec format;
1614 /* Choose variable name. */
1615 name = dict_make_unique_var_name (dict, column->name, &number);
1617 /* Choose variable format. */
1618 fmt_guesser_clear (fg);
1619 for (row = ia->first_line.skip_lines; row < ia->file.line_cnt; row++)
1620 fmt_guesser_add (fg, column->contents[row]);
1621 fmt_guesser_guess (fg, &format);
1622 fmt_fix_input (&format);
1624 /* Create variable. */
1625 var = dict_create_var_assert (dict, name, fmt_var_width (&format));
1626 var_set_both_formats (var, &format);
1634 name = dict_make_unique_var_name (dict, var_get_name (modified_var),
1636 dict_clone_var_as_assert (dict, modified_var, name);
1640 fmt_guesser_destroy (fg);
1642 psppire_dict = psppire_dict_new_from_dict (dict);
1643 g_signal_connect (psppire_dict, "variable_changed",
1644 G_CALLBACK (on_variable_change), ia);
1645 ia->formats.dict = dict;
1646 ia->formats.psppire_dict = psppire_dict;
1648 /* XXX: PsppireVarStore doesn't hold a reference to
1649 psppire_dict for now, but it should. After it does, we
1650 should g_object_ref the psppire_dict here, since we also
1651 hold a reference via ia->formats.dict. */
1652 var_store = psppire_var_store_new (psppire_dict);
1653 g_object_set (var_store,
1654 "format-type", PSPPIRE_VAR_STORE_INPUT_FORMATS,
1656 var_sheet = PSPPIRE_VAR_SHEET (psppire_var_sheet_new ());
1657 g_object_set (var_sheet,
1659 "may-create-vars", FALSE,
1662 vars_scroller = GTK_BIN (get_widget_assert (ia->asst.builder, "vars-scroller"));
1663 old_var_sheet = gtk_bin_get_child (vars_scroller);
1664 if (old_var_sheet != NULL)
1665 gtk_widget_destroy (old_var_sheet);
1666 gtk_container_add (GTK_CONTAINER (vars_scroller), GTK_WIDGET (var_sheet));
1667 gtk_widget_show (GTK_WIDGET (var_sheet));
1669 gtk_widget_destroy (GTK_WIDGET (ia->formats.data_tree_view));
1670 ia->formats.data_tree_view = create_data_tree_view (
1672 GTK_CONTAINER (get_widget_assert (ia->asst.builder, "data-scroller")),
1675 pop_watch_cursor (ia);
1678 /* Clears the set of user-modified variables from IA's formats
1679 substructure. This discards user modifications to variable
1680 formats, thereby causing formats to revert to their
1683 clear_modified_vars (struct import_assistant *ia)
1685 struct formats_page *p = &ia->formats;
1688 for (i = 0; i < p->modified_var_cnt; i++)
1689 var_destroy (p->modified_vars[i]);
1690 free (p->modified_vars);
1691 p->modified_vars = NULL;
1692 p->modified_var_cnt = 0;
1695 /* Resets the formats page to its defaults, discarding user
1698 reset_formats_page (struct import_assistant *ia)
1700 clear_modified_vars (ia);
1701 prepare_formats_page (ia);
1704 /* Called when the user changes one of the variables in the
1707 on_variable_change (PsppireDict *dict, int dict_idx,
1708 struct import_assistant *ia)
1710 struct formats_page *p = &ia->formats;
1711 GtkTreeView *tv = ia->formats.data_tree_view;
1712 gint column_idx = dict_idx + 1;
1714 push_watch_cursor (ia);
1716 /* Remove previous column and replace with new column. */
1717 gtk_tree_view_remove_column (tv, gtk_tree_view_get_column (tv, column_idx));
1718 gtk_tree_view_insert_column (tv, make_data_column (ia, tv, false, dict_idx),
1721 /* Save a copy of the modified variable in modified_vars, so
1722 that its attributes will be preserved if we back up to the
1723 previous page with the Prev button and then come back
1725 if (dict_idx >= p->modified_var_cnt)
1728 p->modified_vars = xnrealloc (p->modified_vars, dict_idx + 1,
1729 sizeof *p->modified_vars);
1730 for (i = 0; i <= dict_idx; i++)
1731 p->modified_vars[i] = NULL;
1732 p->modified_var_cnt = dict_idx + 1;
1734 if (p->modified_vars[dict_idx])
1735 var_destroy (p->modified_vars[dict_idx]);
1736 p->modified_vars[dict_idx]
1737 = var_clone (psppire_dict_get_variable (dict, dict_idx));
1739 pop_watch_cursor (ia);
1742 /* Parses the contents of the field at (ROW,COLUMN) according to
1743 its variable format. If OUTPUTP is non-null, then *OUTPUTP
1744 receives the formatted output for that field (which must be
1745 freed with free). If TOOLTIPP is non-null, then *TOOLTIPP
1746 receives a message suitable for use in a tooltip, if one is
1747 needed, or a null pointer otherwise. Returns true if a
1748 tooltip message is needed, otherwise false. */
1750 parse_field (struct import_assistant *ia,
1751 size_t row, size_t column,
1752 char **outputp, char **tooltipp)
1754 struct substring field;
1756 struct variable *var;
1757 const struct fmt_spec *in;
1758 struct fmt_spec out;
1762 field = ia->separators.columns[column].contents[row];
1763 var = dict_get_var (ia->formats.dict, column);
1764 value_init (&val, var_get_width (var));
1765 in = var_get_print_format (var);
1766 out = fmt_for_output_from_input (in);
1768 if (field.string != NULL)
1772 error = data_in (field, C_ENCODING, in->type, &val, var_get_width (var),
1773 dict_get_encoding (ia->formats.dict));
1776 tooltip = xasprintf (_("Cannot parse field content `%.*s' as "
1778 (int) field.length, field.string,
1779 fmt_name (in->type), error);
1785 tooltip = xstrdup (_("This input line has too few separators "
1786 "to fill in this field."));
1787 value_set_missing (&val, var_get_width (var));
1789 if (outputp != NULL)
1791 *outputp = data_out (&val, dict_get_encoding (ia->formats.dict), &out);
1793 value_destroy (&val, var_get_width (var));
1795 ok = tooltip == NULL;
1796 if (tooltipp != NULL)
1797 *tooltipp = tooltip;
1803 /* Called to render one of the cells in the data preview tree
1806 render_output_cell (GtkTreeViewColumn *tree_column,
1807 GtkCellRenderer *cell,
1808 GtkTreeModel *model,
1812 struct import_assistant *ia = ia_;
1814 GValue gvalue = { 0, };
1817 ok = parse_field (ia,
1818 (text_import_model_iter_to_row (iter)
1819 + ia->first_line.skip_lines),
1820 GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1824 g_value_init (&gvalue, G_TYPE_STRING);
1825 g_value_take_string (&gvalue, output);
1826 g_object_set_property (G_OBJECT (cell), "text", &gvalue);
1827 g_value_unset (&gvalue);
1830 g_object_set (cell, "background-set", FALSE, (void *) NULL);
1833 "background", "red",
1834 "background-set", TRUE,
1838 /* Called to render a tooltip for one of the cells in the data
1839 preview tree view. */
1841 on_query_output_tooltip (GtkWidget *widget, gint wx, gint wy,
1842 gboolean keyboard_mode UNUSED,
1843 GtkTooltip *tooltip, struct import_assistant *ia)
1848 if (!get_tooltip_location (widget, wx, wy, ia, &row, &column))
1851 if (parse_field (ia, row, column, NULL, &text))
1854 gtk_tooltip_set_text (tooltip, text);
1859 /* Utility functions used by multiple pages of the assistant. */
1862 get_tooltip_location (GtkWidget *widget, gint wx, gint wy,
1863 const struct import_assistant *ia,
1864 size_t *row, size_t *column)
1866 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
1870 GtkTreeViewColumn *tree_column;
1871 GtkTreeModel *tree_model;
1874 /* Check that WIDGET is really visible on the screen before we
1875 do anything else. This is a bug fix for a sticky situation:
1876 when text_data_import_assistant() returns, it frees the data
1877 necessary to compose the tool tip message, but there may be
1878 a tool tip under preparation at that point (even if there is
1879 no visible tool tip) that will call back into us a little
1880 bit later. Perhaps the correct solution to this problem is
1881 to make the data related to the tool tips part of a GObject
1882 that only gets destroyed when all references are released,
1883 but this solution appears to be effective too. */
1884 if (!GTK_WIDGET_MAPPED (widget))
1887 gtk_tree_view_convert_widget_to_bin_window_coords (tree_view,
1889 if (!gtk_tree_view_get_path_at_pos (tree_view, bx, by,
1890 &path, &tree_column, NULL, NULL))
1893 *column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1896 tree_model = gtk_tree_view_get_model (tree_view);
1897 ok = gtk_tree_model_get_iter (tree_model, &iter, path);
1898 gtk_tree_path_free (path);
1902 *row = text_import_model_iter_to_row (&iter) + ia->first_line.skip_lines;
1907 make_tree_view (const struct import_assistant *ia,
1909 GtkTreeView **tree_view)
1911 GtkTreeModel *model;
1913 *tree_view = GTK_TREE_VIEW (gtk_tree_view_new ());
1914 model = GTK_TREE_MODEL (text_import_model_new (
1915 ia->file.lines + first_line,
1916 ia->file.line_cnt - first_line, first_line));
1917 gtk_tree_view_set_model (*tree_view, model);
1919 add_line_number_column (ia, *tree_view);
1923 add_line_number_column (const struct import_assistant *ia,
1924 GtkTreeView *treeview)
1926 GtkTreeViewColumn *column;
1928 column = gtk_tree_view_column_new_with_attributes (
1929 _("Line"), ia->asst.prop_renderer,
1930 "text", TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER,
1932 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
1933 gtk_tree_view_column_set_fixed_width (
1934 column, get_monospace_width (treeview, ia->asst.prop_renderer, 5));
1935 gtk_tree_view_append_column (treeview, column);
1939 get_monospace_width (GtkTreeView *treeview, GtkCellRenderer *renderer,
1946 ds_put_byte_multiple (&s, '0', char_cnt);
1947 ds_put_byte (&s, ' ');
1948 width = get_string_width (treeview, renderer, ds_cstr (&s));
1955 get_string_width (GtkTreeView *treeview, GtkCellRenderer *renderer,
1959 g_object_set (G_OBJECT (renderer), "text", string, (void *) NULL);
1960 gtk_cell_renderer_get_size (renderer, GTK_WIDGET (treeview),
1961 NULL, NULL, NULL, &width, NULL);
1965 static GtkTreeViewColumn *
1966 make_data_column (struct import_assistant *ia, GtkTreeView *tree_view,
1967 bool input, gint dict_idx)
1969 struct variable *var = NULL;
1970 struct column *column = NULL;
1972 gint content_width, header_width;
1973 GtkTreeViewColumn *tree_column;
1977 column = &ia->separators.columns[dict_idx];
1979 var = dict_get_var (ia->formats.dict, dict_idx);
1981 name = escape_underscores (input ? column->name : var_get_name (var));
1982 char_cnt = input ? column->width : var_get_print_format (var)->w;
1983 content_width = get_monospace_width (tree_view, ia->asst.fixed_renderer,
1985 header_width = get_string_width (tree_view, ia->asst.prop_renderer,
1988 tree_column = gtk_tree_view_column_new ();
1989 g_object_set_data (G_OBJECT (tree_column), "column-number",
1990 GINT_TO_POINTER (dict_idx));
1991 gtk_tree_view_column_set_title (tree_column, name);
1992 gtk_tree_view_column_pack_start (tree_column, ia->asst.fixed_renderer,
1994 gtk_tree_view_column_set_cell_data_func (
1995 tree_column, ia->asst.fixed_renderer,
1996 input ? render_input_cell : render_output_cell, ia, NULL);
1997 gtk_tree_view_column_set_sizing (tree_column, GTK_TREE_VIEW_COLUMN_FIXED);
1998 gtk_tree_view_column_set_fixed_width (tree_column, MAX (content_width,
2006 static GtkTreeView *
2007 create_data_tree_view (bool input, GtkContainer *parent,
2008 struct import_assistant *ia)
2010 GtkTreeView *tree_view;
2013 make_tree_view (ia, ia->first_line.skip_lines, &tree_view);
2014 gtk_tree_selection_set_mode (gtk_tree_view_get_selection (tree_view),
2015 GTK_SELECTION_NONE);
2017 for (i = 0; i < ia->separators.column_cnt; i++)
2018 gtk_tree_view_append_column (tree_view,
2019 make_data_column (ia, tree_view, input, i));
2021 g_object_set (G_OBJECT (tree_view), "has-tooltip", TRUE, (void *) NULL);
2022 g_signal_connect (tree_view, "query-tooltip",
2023 G_CALLBACK (input ? on_query_input_tooltip
2024 : on_query_output_tooltip), ia);
2025 gtk_tree_view_set_fixed_height_mode (tree_view, true);
2027 gtk_container_add (parent, GTK_WIDGET (tree_view));
2028 gtk_widget_show (GTK_WIDGET (tree_view));
2034 escape_underscores (const char *in)
2036 char *out = xmalloc (2 * strlen (in) + 1);
2040 for (; *in != '\0'; in++)
2051 /* TextImportModel, a GtkTreeModel implementation used by some
2052 pages of the assistant. */
2054 #define G_TYPE_TEXT_IMPORT_MODEL (text_import_model_get_type ())
2055 #define TEXT_IMPORT_MODEL(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), G_TYPE_TEXT_IMPORT_MODEL, TextImportModel))
2056 #define TEXT_IMPORT_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), G_TYPE_TEXT_IMPORT_MODEL, TextImportModelClass))
2057 #define IS_TEXT_IMPORT_MODEL(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), G_TYPE_TEXT_IMPORT_MODEL))
2058 #define IS_TEXT_IMPORT_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), G_TYPE_TEXT_IMPORT_MODEL))
2059 #define TEXT_IMPORT_MODEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), G_TYPE_TEXT_IMPORT_MODEL, TextImportModelClass))
2061 /* Random number used in 'stamp' member of GtkTreeIter. */
2062 #define TREE_MODEL_STAMP 0x7efd67d3
2064 struct TextImportModel
2067 struct string *lines;
2072 struct TextImportModelClass
2074 GObjectClass parent_class;
2077 GType text_import_model_get_type (void);
2078 static void text_import_model_tree_model_init (gpointer iface, gpointer data);
2081 text_import_model_get_type (void)
2083 static GType object_type = 0;
2087 static const GTypeInfo object_info = {
2088 sizeof (TextImportModelClass),
2089 (GBaseInitFunc) NULL,
2090 (GBaseFinalizeFunc) NULL,
2091 NULL, /* class_init */
2092 NULL, /* class_finalize */
2093 NULL, /* class_data */
2094 sizeof (TextImportModel),
2095 0, /* n_preallocs */
2096 NULL, /* instance_init */
2099 static const GInterfaceInfo tree_model_info = {
2100 text_import_model_tree_model_init,
2105 object_type = g_type_register_static (G_TYPE_OBJECT,
2109 g_type_add_interface_static (object_type, GTK_TYPE_TREE_MODEL,
2119 /* Creates and returns a new TextImportModel that contains the
2120 LINE_CNT lines in LINES. The lines before FIRST_LINE in LINES
2121 are not part of the model, but they are included in the line
2122 numbers in the TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER column.
2124 The caller retains responsibility for freeing LINES and must
2125 ensure that its lifetime and that of the strings that it
2126 contains exceeds that of the TextImportModel. */
2128 text_import_model_new (struct string *lines, size_t line_cnt,
2131 TextImportModel *new_text_import_model
2132 = g_object_new (G_TYPE_TEXT_IMPORT_MODEL, NULL);
2133 new_text_import_model->lines = lines;
2134 new_text_import_model->line_cnt = line_cnt;
2135 new_text_import_model->first_line = first_line;
2136 return new_text_import_model;
2141 tree_model_iter_has_child (GtkTreeModel *tree_model,
2148 tree_model_iter_parent (GtkTreeModel *tree_model,
2155 static GtkTreeModelFlags
2156 tree_model_get_flags (GtkTreeModel *model)
2158 g_return_val_if_fail (IS_TEXT_IMPORT_MODEL (model), (GtkTreeModelFlags) 0);
2160 return GTK_TREE_MODEL_LIST_ONLY | GTK_TREE_MODEL_ITERS_PERSIST;
2165 tree_model_n_columns (GtkTreeModel *model)
2171 tree_model_column_type (GtkTreeModel *model, gint index)
2173 return (index == TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER ? G_TYPE_INT
2174 : index == TEXT_IMPORT_MODEL_COLUMN_LINE ? G_TYPE_STRING
2179 init_iter (TextImportModel *list, gint idx, GtkTreeIter *iter)
2181 if (idx < 0 || idx >= list->line_cnt)
2184 iter->user_data = GINT_TO_POINTER (-1);
2189 iter->stamp = TREE_MODEL_STAMP;
2190 iter->user_data = GINT_TO_POINTER (idx);
2196 tree_model_get_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreePath *path)
2198 gint *indices, depth;
2200 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2202 g_return_val_if_fail (path, FALSE);
2204 indices = gtk_tree_path_get_indices (path);
2205 depth = gtk_tree_path_get_depth (path);
2207 g_return_val_if_fail (depth == 1, FALSE);
2209 return init_iter (list, indices[0], iter);
2214 tree_model_iter_next (GtkTreeModel *model, GtkTreeIter *iter)
2216 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2219 assert (iter->stamp == TREE_MODEL_STAMP);
2221 idx = GPOINTER_TO_INT (iter->user_data);
2222 return init_iter (list, idx == -1 ? -1 : idx + 1, iter);
2225 static GtkTreePath *
2226 tree_model_get_path (GtkTreeModel *model, GtkTreeIter *iter)
2230 g_return_val_if_fail (iter->stamp == TREE_MODEL_STAMP, FALSE);
2232 path = gtk_tree_path_new ();
2233 gtk_tree_path_append_index (path, GPOINTER_TO_INT (iter->user_data));
2239 tree_model_get_value (GtkTreeModel *model, GtkTreeIter *iter,
2240 gint column, GValue *value)
2242 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2245 g_return_if_fail (iter->stamp == TREE_MODEL_STAMP);
2247 idx = GPOINTER_TO_INT (iter->user_data);
2248 assert (idx >= 0 && idx < list->line_cnt);
2252 g_value_init (value, G_TYPE_INT);
2253 g_value_set_int (value, idx + list->first_line + 1);
2257 g_value_init (value, G_TYPE_STRING);
2258 g_value_set_static_string (value, ds_cstr (&list->lines[idx]));
2263 tree_model_iter_children (GtkTreeModel *tree_model,
2265 GtkTreeIter *parent)
2271 tree_model_n_children (GtkTreeModel *model, GtkTreeIter *iter)
2273 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2275 return iter == NULL ? list->line_cnt : 0;
2279 tree_model_nth_child (GtkTreeModel *model, GtkTreeIter *iter,
2280 GtkTreeIter *parent, gint n)
2282 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2283 g_return_val_if_fail (IS_TEXT_IMPORT_MODEL (model), FALSE);
2287 return init_iter (list, n, iter);
2291 text_import_model_tree_model_init (gpointer iface_, gpointer data UNUSED)
2293 GtkTreeModelIface *iface = (GtkTreeModelIface *) iface_;
2295 iface->get_flags = tree_model_get_flags;
2296 iface->get_n_columns = tree_model_n_columns;
2297 iface->get_column_type = tree_model_column_type;
2298 iface->get_iter = tree_model_get_iter;
2299 iface->iter_next = tree_model_iter_next;
2300 iface->get_path = tree_model_get_path;
2301 iface->get_value = tree_model_get_value;
2303 iface->iter_children = tree_model_iter_children;
2304 iface->iter_has_child = tree_model_iter_has_child;
2305 iface->iter_n_children = tree_model_n_children;
2306 iface->iter_nth_child = tree_model_nth_child;
2307 iface->iter_parent = tree_model_iter_parent;
2311 text_import_model_iter_to_row (const GtkTreeIter *iter)
2313 assert (iter->stamp == TREE_MODEL_STAMP);
2314 return GPOINTER_TO_INT (iter->user_data);
2317 /* Increments the "watch cursor" level, setting the cursor for
2318 the assistant window to a watch face to indicate to the user
2319 that the ongoing operation may take some time. */
2321 push_watch_cursor (struct import_assistant *ia)
2323 if (++ia->asst.watch_cursor == 1)
2325 GtkWidget *widget = GTK_WIDGET (ia->asst.assistant);
2326 GdkDisplay *display = gtk_widget_get_display (widget);
2327 GdkCursor *cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
2328 gdk_window_set_cursor (widget->window, cursor);
2329 gdk_cursor_unref (cursor);
2330 gdk_display_flush (display);
2334 /* Decrements the "watch cursor" level. If the level reaches
2335 zero, the cursor is reset to its default shape. */
2337 pop_watch_cursor (struct import_assistant *ia)
2339 if (--ia->asst.watch_cursor == 0)
2341 GtkWidget *widget = GTK_WIDGET (ia->asst.assistant);
2342 gdk_window_set_cursor (widget->window, NULL);