1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2008, 2009, 2010 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/>. */
22 #include "widget-io.h"
23 #include "checkbox-treeview.h"
24 #include "descriptives-dialog.h"
28 #include <gtk-contrib/psppire-sheet.h>
33 #include <data/data-in.h>
34 #include <data/data-out.h>
35 #include <data/format-guesser.h>
36 #include <data/value-labels.h>
37 #include <language/data-io/data-parser.h>
38 #include <language/syntax-string-source.h>
39 #include <libpspp/assertion.h>
40 #include <libpspp/message.h>
41 #include <ui/syntax-gen.h>
42 #include <ui/gui/psppire-data-window.h>
43 #include <ui/gui/dialog-common.h>
44 #include <ui/gui/helper.h>
45 #include <ui/gui/psppire-dialog.h>
46 #include <ui/gui/psppire-var-sheet.h>
47 #include <ui/gui/psppire-var-store.h>
54 #define _(msgid) gettext (msgid)
55 #define N_(msgid) msgid
58 /* TextImportModel, a GtkTreeModel used by the text data import
62 TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER, /* 1-based line number in file */
63 TEXT_IMPORT_MODEL_COLUMN_LINE, /* The line from the file. */
65 typedef struct TextImportModel TextImportModel;
66 typedef struct TextImportModelClass TextImportModelClass;
68 TextImportModel *text_import_model_new (struct string *lines, size_t line_cnt,
70 gint text_import_model_iter_to_row (const GtkTreeIter *);
72 struct import_assistant;
74 /* The file to be imported. */
77 char *file_name; /* File name. */
78 unsigned long int total_lines; /* Number of lines in file. */
79 bool total_is_exact; /* Is total_lines exact (or an estimate)? */
81 /* The first several lines of the file. */
85 static bool init_file (struct import_assistant *, GtkWindow *parent);
86 static void destroy_file (struct import_assistant *);
88 /* The main body of the GTK+ assistant and related data. */
92 GtkAssistant *assistant;
94 GtkWidget *paste_button;
95 GtkWidget *reset_button;
99 GtkCellRenderer *prop_renderer;
100 GtkCellRenderer *fixed_renderer;
102 static void init_assistant (struct import_assistant *, GtkWindow *);
103 static void destroy_assistant (struct import_assistant *);
104 static GtkWidget *add_page_to_assistant (struct import_assistant *,
106 GtkAssistantPageType);
108 /* The introduction page of the assistant. */
112 GtkWidget *all_cases_button;
113 GtkWidget *n_cases_button;
114 GtkWidget *n_cases_spin;
115 GtkWidget *percent_button;
116 GtkWidget *percent_spin;
118 static void init_intro_page (struct import_assistant *);
119 static void reset_intro_page (struct import_assistant *);
121 /* Page where the user chooses the first line of data. */
122 struct first_line_page
124 int skip_lines; /* Number of initial lines to skip? */
125 bool variable_names; /* Variable names above first line of data? */
128 GtkTreeView *tree_view;
129 GtkWidget *variable_names_cb;
131 static void init_first_line_page (struct import_assistant *);
132 static void reset_first_line_page (struct import_assistant *);
134 /* Page where the user chooses field separators. */
135 struct separators_page
137 /* How to break lines into columns. */
138 struct string separators; /* Field separators. */
139 struct string quotes; /* Quote characters. */
140 bool escape; /* Doubled quotes yield a quote mark? */
142 /* The columns produced thereby. */
143 struct column *columns; /* Information about each column. */
144 size_t column_cnt; /* Number of columns. */
147 GtkWidget *custom_cb;
148 GtkWidget *custom_entry;
150 GtkWidget *quote_combo;
151 GtkEntry *quote_entry;
152 GtkWidget *escape_cb;
153 GtkTreeView *fields_tree_view;
155 /* The columns that the separators divide the data into. */
158 /* Variable name for this column. This is the variable name
159 used on the separators page; it can be overridden by the
160 user on the formats page. */
163 /* Maximum length of any row in this column. */
166 /* Contents of this column: contents[row] is the contents for
169 A null substring indicates a missing column for that row
170 (because the line contains an insufficient number of
173 contents[] elements may be substrings of the lines[]
174 strings that represent the whole lines of the file, to
175 save memory. Other elements are dynamically allocated
176 with ss_alloc_substring. */
177 struct substring *contents;
179 static void init_separators_page (struct import_assistant *);
180 static void destroy_separators_page (struct import_assistant *);
181 static void prepare_separators_page (struct import_assistant *);
182 static void reset_separators_page (struct import_assistant *);
184 /* Page where the user verifies and adjusts input formats. */
187 struct dictionary *dict;
190 GtkTreeView *data_tree_view;
191 PsppireDict *psppire_dict;
192 struct variable **modified_vars;
193 size_t modified_var_cnt;
195 static void init_formats_page (struct import_assistant *);
196 static void destroy_formats_page (struct import_assistant *);
197 static void prepare_formats_page (struct import_assistant *);
198 static void reset_formats_page (struct import_assistant *);
200 struct import_assistant
203 struct assistant asst;
204 struct intro_page intro;
205 struct first_line_page first_line;
206 struct separators_page separators;
207 struct formats_page formats;
210 static void apply_dict (const struct dictionary *, struct string *);
211 static char *generate_syntax (const struct import_assistant *);
213 static gboolean get_tooltip_location (GtkWidget *widget, gint wx, gint wy,
214 const struct import_assistant *,
215 size_t *row, size_t *column);
216 static void make_tree_view (const struct import_assistant *ia,
218 GtkTreeView **tree_view);
219 static void add_line_number_column (const struct import_assistant *,
221 static gint get_monospace_width (GtkTreeView *, GtkCellRenderer *,
223 static gint get_string_width (GtkTreeView *, GtkCellRenderer *,
225 static GtkTreeViewColumn *make_data_column (struct import_assistant *,
226 GtkTreeView *, bool input,
228 static GtkTreeView *create_data_tree_view (bool input, GtkContainer *parent,
229 struct import_assistant *);
230 static void escape_underscores (const char *in, char *out);
231 static void push_watch_cursor (struct import_assistant *);
232 static void pop_watch_cursor (struct import_assistant *);
234 /* Pops up the Text Data Import assistant. */
236 text_data_import_assistant (GtkWindow *parent_window)
238 struct import_assistant *ia;
240 ia = xzalloc (sizeof *ia);
241 if (!init_file (ia, parent_window))
247 init_assistant (ia, parent_window);
248 init_intro_page (ia);
249 init_first_line_page (ia);
250 init_separators_page (ia);
251 init_formats_page (ia);
253 gtk_widget_show_all (GTK_WIDGET (ia->asst.assistant));
255 ia->asst.main_loop = g_main_loop_new (NULL, false);
256 g_main_loop_run (ia->asst.main_loop);
257 g_main_loop_unref (ia->asst.main_loop);
259 switch (ia->asst.response)
261 case GTK_RESPONSE_APPLY:
263 char *syntax = generate_syntax (ia);
264 execute_syntax (create_syntax_string_source (syntax));
268 case PSPPIRE_RESPONSE_PASTE:
270 char *syntax = generate_syntax (ia);
271 paste_syntax_to_window (syntax);
279 destroy_formats_page (ia);
280 destroy_separators_page (ia);
281 destroy_assistant (ia);
286 /* Emits PSPP syntax to S that applies the dictionary attributes
287 (such as missing values and value labels) of the variables in
290 apply_dict (const struct dictionary *dict, struct string *s)
292 size_t var_cnt = dict_get_var_cnt (dict);
295 for (i = 0; i < var_cnt; i++)
297 struct variable *var = dict_get_var (dict, i);
298 const char *name = var_get_name (var);
299 enum val_type type = var_get_type (var);
300 int width = var_get_width (var);
301 enum measure measure = var_get_measure (var);
302 enum alignment alignment = var_get_alignment (var);
303 const struct fmt_spec *format = var_get_print_format (var);
305 if (var_has_missing_values (var))
307 const struct missing_values *mv = var_get_missing_values (var);
310 syntax_gen_pspp (s, "MISSING VALUES %ss (", name);
311 for (j = 0; j < mv_n_values (mv); j++)
314 ds_put_cstr (s, ", ");
315 syntax_gen_value (s, mv_get_value (mv, j), width, format);
318 if (mv_has_range (mv))
321 if (mv_has_value (mv))
322 ds_put_cstr (s, ", ");
323 mv_get_range (mv, &low, &high);
324 syntax_gen_num_range (s, low, high, format);
326 ds_put_cstr (s, ").\n");
328 if (var_has_value_labels (var))
330 const struct val_labs *vls = var_get_value_labels (var);
331 const struct val_lab **labels = val_labs_sorted (vls);
332 size_t n_labels = val_labs_count (vls);
335 syntax_gen_pspp (s, "VALUE LABELS %ss", name);
336 for (i = 0; i < n_labels; i++)
338 const struct val_lab *vl = labels[i];
339 ds_put_cstr (s, "\n ");
340 syntax_gen_value (s, &vl->value, width, format);
341 ds_put_char (s, ' ');
342 syntax_gen_string (s, ss_cstr (val_lab_get_label (vl)));
345 ds_put_cstr (s, ".\n");
347 if (var_has_label (var))
348 syntax_gen_pspp (s, "VARIABLE LABELS %ss %sq.\n",
349 name, var_get_label (var));
350 if (measure != var_default_measure (type))
351 syntax_gen_pspp (s, "VARIABLE LEVEL %ss (%ss).\n",
353 (measure == MEASURE_NOMINAL ? "NOMINAL"
354 : measure == MEASURE_ORDINAL ? "ORDINAL"
356 if (alignment != var_default_alignment (type))
357 syntax_gen_pspp (s, "VARIABLE ALIGNMENT %ss (%ss).\n",
359 (alignment == ALIGN_LEFT ? "LEFT"
360 : alignment == ALIGN_CENTRE ? "CENTER"
362 if (var_get_display_width (var) != var_default_display_width (width))
363 syntax_gen_pspp (s, "VARIABLE WIDTH %ss (%d).\n",
364 name, var_get_display_width (var));
368 /* Generates and returns PSPP syntax to execute the import
369 operation described by IA. The caller must free the syntax
372 generate_syntax (const struct import_assistant *ia)
374 struct string s = DS_EMPTY_INITIALIZER;
383 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (
384 ia->intro.n_cases_button)))
385 ds_put_format (&s, " /IMPORTCASES=FIRST %d\n",
386 gtk_spin_button_get_value_as_int (
387 GTK_SPIN_BUTTON (ia->intro.n_cases_spin)));
388 else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (
389 ia->intro.percent_button)))
390 ds_put_format (&s, " /IMPORTCASES=PERCENT %d\n",
391 gtk_spin_button_get_value_as_int (
392 GTK_SPIN_BUTTON (ia->intro.percent_spin)));
394 ds_put_cstr (&s, " /IMPORTCASES=ALL\n");
396 " /ARRANGEMENT=DELIMITED\n"
398 if (ia->first_line.skip_lines > 0)
399 ds_put_format (&s, " /FIRSTCASE=%d\n", ia->first_line.skip_lines + 1);
400 ds_put_cstr (&s, " /DELIMITERS=\"");
401 if (ds_find_char (&ia->separators.separators, '\t') != SIZE_MAX)
402 ds_put_cstr (&s, "\\t");
403 if (ds_find_char (&ia->separators.separators, '\\') != SIZE_MAX)
404 ds_put_cstr (&s, "\\\\");
405 for (i = 0; i < ds_length (&ia->separators.separators); i++)
407 char c = ds_at (&ia->separators.separators, i);
409 ds_put_cstr (&s, "\"\"");
410 else if (c != '\t' && c != '\\')
413 ds_put_cstr (&s, "\"\n");
414 if (!ds_is_empty (&ia->separators.quotes))
415 syntax_gen_pspp (&s, " /QUALIFIER=%sq\n", ds_cstr (&ia->separators.quotes));
416 if (!ds_is_empty (&ia->separators.quotes) && ia->separators.escape)
417 ds_put_cstr (&s, " /ESCAPE\n");
418 ds_put_cstr (&s, " /VARIABLES=\n");
420 var_cnt = dict_get_var_cnt (ia->formats.dict);
421 for (i = 0; i < var_cnt; i++)
423 struct variable *var = dict_get_var (ia->formats.dict, i);
424 char format_string[FMT_STRING_LEN_MAX + 1];
425 fmt_to_string (var_get_print_format (var), format_string);
426 ds_put_format (&s, " %s %s%s\n",
427 var_get_name (var), format_string,
428 i == var_cnt - 1 ? "." : "");
431 apply_dict (ia->formats.dict, &s);
436 /* Choosing a file and reading it. */
438 static char *choose_file (GtkWindow *parent_window);
440 /* Obtains the file to import from the user and initializes IA's
441 file substructure. PARENT_WINDOW must be the window to use
442 as the file chooser window's parent.
444 Returns true if successful, false if the file name could not
445 be obtained or the file could not be read. */
447 init_file (struct import_assistant *ia, GtkWindow *parent_window)
449 struct file *file = &ia->file;
450 enum { MAX_PREVIEW_LINES = 1000 }; /* Max number of lines to read. */
451 enum { MAX_LINE_LEN = 16384 }; /* Max length of an acceptable line. */
454 file->file_name = choose_file (parent_window);
455 if (file->file_name == NULL)
458 stream = fopen (file->file_name, "r");
461 msg (ME, _("Could not open `%s': %s"),
462 file->file_name, strerror (errno));
466 file->lines = xnmalloc (MAX_PREVIEW_LINES, sizeof *file->lines);
467 for (; file->line_cnt < MAX_PREVIEW_LINES; file->line_cnt++)
469 struct string *line = &file->lines[file->line_cnt];
471 ds_init_empty (line);
472 if (!ds_read_line (line, stream, MAX_LINE_LEN))
476 else if (ferror (stream))
477 msg (ME, _("Error reading `%s': %s"),
478 file->file_name, strerror (errno));
480 msg (ME, _("Failed to read `%s', because it contains a line "
481 "over %d bytes long and therefore appears not to be "
483 file->file_name, MAX_LINE_LEN);
488 ds_chomp (line, '\n');
489 ds_chomp (line, '\r');
492 if (file->line_cnt == 0)
494 msg (ME, _("`%s' is empty."), file->file_name);
500 /* Estimate the number of lines in the file. */
501 if (file->line_cnt < MAX_PREVIEW_LINES)
502 file->total_lines = file->line_cnt;
506 off_t position = ftello (stream);
507 if (fstat (fileno (stream), &s) == 0 && position > 0)
508 file->total_lines = (double) file->line_cnt / position * s.st_size;
510 file->total_lines = 0;
516 /* Frees IA's file substructure. */
518 destroy_file (struct import_assistant *ia)
520 struct file *f = &ia->file;
523 for (i = 0; i < f->line_cnt; i++)
524 ds_destroy (&f->lines[i]);
526 g_free (f->file_name);
529 /* Obtains the file to read from the user and returns the name of
530 the file as a string that must be freed with g_free if
531 successful, otherwise a null pointer. PARENT_WINDOW must be
532 the window to use as the file chooser window's parent. */
534 choose_file (GtkWindow *parent_window)
539 dialog = gtk_file_chooser_dialog_new (_("Import Delimited Text Data"),
541 GTK_FILE_CHOOSER_ACTION_OPEN,
542 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
543 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
546 switch (gtk_dialog_run (GTK_DIALOG (dialog)))
548 case GTK_RESPONSE_ACCEPT:
549 file_name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
555 gtk_widget_destroy (dialog);
562 static void close_assistant (struct import_assistant *, int response);
563 static void on_prepare (GtkAssistant *assistant, GtkWidget *page,
564 struct import_assistant *);
565 static void on_cancel (GtkAssistant *assistant, struct import_assistant *);
566 static void on_close (GtkAssistant *assistant, struct import_assistant *);
567 static void on_paste (GtkButton *button, struct import_assistant *);
568 static void on_reset (GtkButton *button, struct import_assistant *);
569 static void close_assistant (struct import_assistant *, int response);
571 /* Initializes IA's asst substructure. PARENT_WINDOW must be the
572 window to use as the assistant window's parent. */
574 init_assistant (struct import_assistant *ia, GtkWindow *parent_window)
576 struct assistant *a = &ia->asst;
578 a->builder = builder_new ("text-data-import.ui");
579 a->assistant = GTK_ASSISTANT (gtk_assistant_new ());
580 g_signal_connect (a->assistant, "prepare", G_CALLBACK (on_prepare), ia);
581 g_signal_connect (a->assistant, "cancel", G_CALLBACK (on_cancel), ia);
582 g_signal_connect (a->assistant, "close", G_CALLBACK (on_close), ia);
583 a->paste_button = gtk_button_new_from_stock (GTK_STOCK_PASTE);
584 gtk_assistant_add_action_widget (a->assistant, a->paste_button);
585 g_signal_connect (a->paste_button, "clicked", G_CALLBACK (on_paste), ia);
586 a->reset_button = gtk_button_new_from_stock ("pspp-stock-reset");
587 gtk_assistant_add_action_widget (a->assistant, a->reset_button);
588 g_signal_connect (a->reset_button, "clicked", G_CALLBACK (on_reset), ia);
589 gtk_window_set_title (GTK_WINDOW (a->assistant),
590 _("Importing Delimited Text Data"));
591 gtk_window_set_transient_for (GTK_WINDOW (a->assistant), parent_window);
592 gtk_window_set_icon_name (GTK_WINDOW (a->assistant), "psppicon");
594 a->prop_renderer = gtk_cell_renderer_text_new ();
595 g_object_ref_sink (a->prop_renderer);
596 a->fixed_renderer = gtk_cell_renderer_text_new ();
597 g_object_ref_sink (a->fixed_renderer);
598 g_object_set (G_OBJECT (a->fixed_renderer),
599 "family", "Monospace",
603 /* Frees IA's asst substructure. */
605 destroy_assistant (struct import_assistant *ia)
607 struct assistant *a = &ia->asst;
609 g_object_unref (a->prop_renderer);
610 g_object_unref (a->fixed_renderer);
611 g_object_unref (a->builder);
614 /* Appends a page of the given TYPE, with PAGE as its content, to
615 the GtkAssistant encapsulated by IA. Returns the GtkWidget
616 that represents the page. */
618 add_page_to_assistant (struct import_assistant *ia,
619 GtkWidget *page, GtkAssistantPageType type)
625 title = gtk_window_get_title (GTK_WINDOW (page));
626 title_copy = xstrdup (title ? title : "");
628 content = gtk_bin_get_child (GTK_BIN (page));
630 g_object_ref (content);
631 gtk_container_remove (GTK_CONTAINER (page), content);
633 gtk_widget_destroy (page);
635 gtk_assistant_append_page (ia->asst.assistant, content);
636 gtk_assistant_set_page_type (ia->asst.assistant, content, type);
637 gtk_assistant_set_page_title (ia->asst.assistant, content, title_copy);
638 gtk_assistant_set_page_complete (ia->asst.assistant, content, true);
645 /* Called just before PAGE is displayed as the current page of
646 ASSISTANT, this updates IA content according to the new
649 on_prepare (GtkAssistant *assistant, GtkWidget *page,
650 struct import_assistant *ia)
653 if (gtk_assistant_get_page_type (assistant, page)
654 == GTK_ASSISTANT_PAGE_CONFIRM)
655 gtk_widget_grab_focus (assistant->apply);
657 gtk_widget_grab_focus (assistant->forward);
659 if (page == ia->separators.page)
660 prepare_separators_page (ia);
661 else if (page == ia->formats.page)
662 prepare_formats_page (ia);
664 gtk_widget_show (ia->asst.reset_button);
665 if (page == ia->formats.page)
666 gtk_widget_show (ia->asst.paste_button);
668 gtk_widget_hide (ia->asst.paste_button);
671 /* Called when the Cancel button in the assistant is clicked. */
673 on_cancel (GtkAssistant *assistant, struct import_assistant *ia)
675 close_assistant (ia, GTK_RESPONSE_CANCEL);
678 /* Called when the Apply button on the last page of the assistant
681 on_close (GtkAssistant *assistant, struct import_assistant *ia)
683 close_assistant (ia, GTK_RESPONSE_APPLY);
686 /* Called when the Paste button on the last page of the assistant
689 on_paste (GtkButton *button, struct import_assistant *ia)
691 close_assistant (ia, PSPPIRE_RESPONSE_PASTE);
694 /* Called when the Reset button is clicked. */
696 on_reset (GtkButton *button, struct import_assistant *ia)
698 gint page_num = gtk_assistant_get_current_page (ia->asst.assistant);
699 GtkWidget *page = gtk_assistant_get_nth_page (ia->asst.assistant, page_num);
701 if (page == ia->intro.page)
702 reset_intro_page (ia);
703 else if (page == ia->first_line.page)
704 reset_first_line_page (ia);
705 else if (page == ia->separators.page)
706 reset_separators_page (ia);
707 else if (page == ia->formats.page)
708 reset_formats_page (ia);
711 /* Causes the assistant to close, returning RESPONSE for
712 interpretation by text_data_import_assistant. */
714 close_assistant (struct import_assistant *ia, int response)
716 ia->asst.response = response;
717 g_main_loop_quit (ia->asst.main_loop);
718 gtk_widget_hide (GTK_WIDGET (ia->asst.assistant));
721 /* The "intro" page of the assistant. */
723 static void on_intro_amount_changed (struct import_assistant *);
725 /* Initializes IA's intro substructure. */
727 init_intro_page (struct import_assistant *ia)
729 GtkBuilder *builder = ia->asst.builder;
730 struct intro_page *p = &ia->intro;
732 GtkWidget *hbox_n_cases ;
733 GtkWidget *hbox_percent ;
737 p->n_cases_spin = gtk_spin_button_new_with_range (0, INT_MAX, 100);
739 hbox_n_cases = widget_scanf (_("Only the first %4d cases"), &p->n_cases_spin);
741 table = get_widget_assert (builder, "button-table");
743 gtk_table_attach_defaults (GTK_TABLE (table), hbox_n_cases,
747 p->percent_spin = gtk_spin_button_new_with_range (0, 100, 10);
749 hbox_percent = widget_scanf (_("Only the first %3d %% of file (approximately)"), &p->percent_spin);
751 gtk_table_attach_defaults (GTK_TABLE (table), hbox_percent,
755 p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Intro"),
756 GTK_ASSISTANT_PAGE_INTRO);
758 p->all_cases_button = get_widget_assert (builder, "import-all-cases");
760 p->n_cases_button = get_widget_assert (builder, "import-n-cases");
762 p->percent_button = get_widget_assert (builder, "import-percent");
764 g_signal_connect_swapped (p->all_cases_button, "toggled",
765 G_CALLBACK (on_intro_amount_changed), ia);
766 g_signal_connect_swapped (p->n_cases_button, "toggled",
767 G_CALLBACK (on_intro_amount_changed), ia);
768 g_signal_connect_swapped (p->percent_button, "toggled",
769 G_CALLBACK (on_intro_amount_changed), ia);
771 on_intro_amount_changed (ia);
774 ds_put_cstr (&s, _("This assistant will guide you through the process of "
775 "importing data into PSPP from a text file with one line "
776 "per case, in which fields are separated by tabs, "
777 "commas, or other delimiters.\n\n"));
778 if (ia->file.total_is_exact)
780 &s, ngettext ("The selected file contains %zu line of text. ",
781 "The selected file contains %zu lines of text. ",
784 else if (ia->file.total_lines > 0)
788 "The selected file contains approximately %lu line of text. ",
789 "The selected file contains approximately %lu lines of text. ",
790 ia->file.total_lines),
791 ia->file.total_lines);
794 "Only the first %zu line of the file will be shown for "
795 "preview purposes in the following screens. ",
796 "Only the first %zu lines of the file will be shown for "
797 "preview purposes in the following screens. ",
801 ds_put_cstr (&s, _("You may choose below how much of the file should "
802 "actually be imported."));
803 gtk_label_set_text (GTK_LABEL (get_widget_assert (builder, "intro-label")),
808 /* Resets IA's intro page to its initial state. */
810 reset_intro_page (struct import_assistant *ia)
812 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->intro.all_cases_button),
816 /* Called when one of the radio buttons is clicked. */
818 on_intro_amount_changed (struct import_assistant *ia)
820 struct intro_page *p = &ia->intro;
822 gtk_widget_set_sensitive (p->n_cases_spin,
823 gtk_toggle_button_get_active (
824 GTK_TOGGLE_BUTTON (p->n_cases_button)));
826 gtk_widget_set_sensitive (p->percent_spin,
827 gtk_toggle_button_get_active (
828 GTK_TOGGLE_BUTTON (p->percent_button)));
831 /* The "first line" page of the assistant. */
833 static GtkTreeView *create_lines_tree_view (GtkContainer *parent_window,
834 struct import_assistant *);
835 static void on_first_line_change (GtkTreeSelection *,
836 struct import_assistant *);
837 static void on_variable_names_cb_toggle (GtkToggleButton *,
838 struct import_assistant *);
839 static void set_first_line (struct import_assistant *);
840 static void get_first_line (struct import_assistant *);
842 /* Initializes IA's first_line substructure. */
844 init_first_line_page (struct import_assistant *ia)
846 struct first_line_page *p = &ia->first_line;
847 GtkBuilder *builder = ia->asst.builder;
849 p->page = add_page_to_assistant (ia, get_widget_assert (builder, "FirstLine"),
850 GTK_ASSISTANT_PAGE_CONTENT);
851 gtk_widget_destroy (get_widget_assert (builder, "first-line"));
852 p->tree_view = create_lines_tree_view (
853 GTK_CONTAINER (get_widget_assert (builder, "first-line-scroller")), ia);
854 p->variable_names_cb = get_widget_assert (builder, "variable-names");
855 gtk_tree_selection_set_mode (
856 gtk_tree_view_get_selection (GTK_TREE_VIEW (p->tree_view)),
857 GTK_SELECTION_BROWSE);
859 g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (p->tree_view)),
860 "changed", G_CALLBACK (on_first_line_change), ia);
861 g_signal_connect (p->variable_names_cb, "toggled",
862 G_CALLBACK (on_variable_names_cb_toggle), ia);
865 /* Resets the first_line page to its initial content. */
867 reset_first_line_page (struct import_assistant *ia)
869 ia->first_line.skip_lines = 0;
870 ia->first_line.variable_names = false;
874 /* Creates and returns a tree view that contains each of the
875 lines in IA's file as a row. */
877 create_lines_tree_view (GtkContainer *parent, struct import_assistant *ia)
879 GtkTreeView *tree_view;
880 GtkTreeViewColumn *column;
881 size_t max_line_length;
882 gint content_width, header_width;
884 gchar *title = _("Text");
886 make_tree_view (ia, 0, &tree_view);
888 column = gtk_tree_view_column_new_with_attributes
890 title, ia->asst.fixed_renderer,
891 "text", TEXT_IMPORT_MODEL_COLUMN_LINE,
894 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
897 for (i = 0; i < ia->file.line_cnt; i++)
899 size_t w = ds_length (&ia->file.lines[i]);
900 max_line_length = MAX (max_line_length, w);
903 content_width = get_monospace_width (tree_view, ia->asst.fixed_renderer,
905 header_width = get_string_width (tree_view, ia->asst.prop_renderer, title);
906 gtk_tree_view_column_set_fixed_width (column, MAX (content_width,
908 gtk_tree_view_append_column (tree_view, column);
910 gtk_tree_view_set_fixed_height_mode (tree_view, true);
912 gtk_container_add (parent, GTK_WIDGET (tree_view));
913 gtk_widget_show (GTK_WIDGET (tree_view));
918 /* Called when the line selected in the first_line tree view
921 on_first_line_change (GtkTreeSelection *selection UNUSED,
922 struct import_assistant *ia)
927 /* Called when the checkbox that indicates whether variable
928 names are in the row above the first line is toggled. */
930 on_variable_names_cb_toggle (GtkToggleButton *variable_names_cb UNUSED,
931 struct import_assistant *ia)
936 /* Sets the widgets to match IA's first_line substructure. */
938 set_first_line (struct import_assistant *ia)
942 path = gtk_tree_path_new_from_indices (ia->first_line.skip_lines, -1);
943 gtk_tree_view_set_cursor (GTK_TREE_VIEW (ia->first_line.tree_view),
945 gtk_tree_path_free (path);
947 gtk_toggle_button_set_active (
948 GTK_TOGGLE_BUTTON (ia->first_line.variable_names_cb),
949 ia->first_line.variable_names);
950 gtk_widget_set_sensitive (ia->first_line.variable_names_cb,
951 ia->first_line.skip_lines > 0);
954 /* Sets IA's first_line substructure to match the widgets. */
956 get_first_line (struct import_assistant *ia)
958 GtkTreeSelection *selection;
962 selection = gtk_tree_view_get_selection (ia->first_line.tree_view);
963 if (gtk_tree_selection_get_selected (selection, &model, &iter))
965 GtkTreePath *path = gtk_tree_model_get_path (model, &iter);
966 int row = gtk_tree_path_get_indices (path)[0];
967 gtk_tree_path_free (path);
969 ia->first_line.skip_lines = row;
970 ia->first_line.variable_names =
971 (ia->first_line.skip_lines > 0
972 && gtk_toggle_button_get_active (
973 GTK_TOGGLE_BUTTON (ia->first_line.variable_names_cb)));
975 gtk_widget_set_sensitive (ia->first_line.variable_names_cb,
976 ia->first_line.skip_lines > 0);
979 /* The "separators" page of the assistant. */
981 static void revise_fields_preview (struct import_assistant *ia);
982 static void choose_likely_separators (struct import_assistant *ia);
983 static void find_commonest_chars (unsigned long int histogram[UCHAR_MAX + 1],
984 const char *targets, const char *def,
985 struct string *result);
986 static void clear_fields (struct import_assistant *ia);
987 static void revise_fields_preview (struct import_assistant *);
988 static void set_separators (struct import_assistant *);
989 static void get_separators (struct import_assistant *);
990 static void on_separators_custom_entry_notify (GObject *UNUSED,
992 struct import_assistant *);
993 static void on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
994 struct import_assistant *);
995 static void on_quote_combo_change (GtkComboBox *combo,
996 struct import_assistant *);
997 static void on_quote_cb_toggle (GtkToggleButton *quote_cb,
998 struct import_assistant *);
999 static void on_separator_toggle (GtkToggleButton *, struct import_assistant *);
1000 static void render_input_cell (GtkTreeViewColumn *tree_column,
1001 GtkCellRenderer *cell,
1002 GtkTreeModel *model, GtkTreeIter *iter,
1004 static gboolean on_query_input_tooltip (GtkWidget *widget, gint wx, gint wy,
1005 gboolean keyboard_mode UNUSED,
1006 GtkTooltip *tooltip,
1007 struct import_assistant *);
1009 /* A common field separator and its identifying name. */
1012 const char *name; /* Name (for use with get_widget_assert). */
1013 int c; /* Separator character. */
1016 /* All the separators in the dialog box. */
1017 static const struct separator separators[] =
1029 #define SEPARATOR_CNT (sizeof separators / sizeof *separators)
1032 set_quote_list (GtkComboBoxEntry *cb)
1034 GtkListStore *list = gtk_list_store_new (1, G_TYPE_STRING);
1037 const gchar *seperator[3] = {"'\"", "\'", "\""};
1039 for (i = 0; i < 3; i++)
1041 const gchar *s = seperator[i];
1043 /* Add a new row to the model */
1044 gtk_list_store_append (list, &iter);
1045 gtk_list_store_set (list, &iter,
1051 gtk_combo_box_set_model (GTK_COMBO_BOX (cb), GTK_TREE_MODEL (list));
1053 gtk_combo_box_entry_set_text_column (cb, 0);
1056 /* Initializes IA's separators substructure. */
1058 init_separators_page (struct import_assistant *ia)
1060 GtkBuilder *builder = ia->asst.builder;
1061 struct separators_page *p = &ia->separators;
1064 choose_likely_separators (ia);
1066 p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Separators"),
1067 GTK_ASSISTANT_PAGE_CONTENT);
1068 p->custom_cb = get_widget_assert (builder, "custom-cb");
1069 p->custom_entry = get_widget_assert (builder, "custom-entry");
1070 p->quote_combo = get_widget_assert (builder, "quote-combo");
1071 p->quote_entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (p->quote_combo)));
1072 p->quote_cb = get_widget_assert (builder, "quote-cb");
1073 p->escape_cb = get_widget_assert (builder, "escape");
1075 set_separators (ia);
1076 set_quote_list (GTK_COMBO_BOX_ENTRY (p->quote_combo));
1077 p->fields_tree_view = GTK_TREE_VIEW (get_widget_assert (builder, "fields"));
1078 g_signal_connect (p->quote_combo, "changed",
1079 G_CALLBACK (on_quote_combo_change), ia);
1080 g_signal_connect (p->quote_cb, "toggled",
1081 G_CALLBACK (on_quote_cb_toggle), ia);
1082 g_signal_connect (p->custom_entry, "notify::text",
1083 G_CALLBACK (on_separators_custom_entry_notify), ia);
1084 g_signal_connect (p->custom_cb, "toggled",
1085 G_CALLBACK (on_separators_custom_cb_toggle), ia);
1086 for (i = 0; i < SEPARATOR_CNT; i++)
1087 g_signal_connect (get_widget_assert (builder, separators[i].name),
1088 "toggled", G_CALLBACK (on_separator_toggle), ia);
1089 g_signal_connect (p->escape_cb, "toggled",
1090 G_CALLBACK (on_separator_toggle), ia);
1093 /* Frees IA's separators substructure. */
1095 destroy_separators_page (struct import_assistant *ia)
1097 struct separators_page *s = &ia->separators;
1099 ds_destroy (&s->separators);
1100 ds_destroy (&s->quotes);
1104 /* Called just before the separators page becomes visible in the
1107 prepare_separators_page (struct import_assistant *ia)
1109 revise_fields_preview (ia);
1112 /* Called when the Reset button is clicked on the separators
1113 page, resets the separators to the defaults. */
1115 reset_separators_page (struct import_assistant *ia)
1117 choose_likely_separators (ia);
1118 set_separators (ia);
1121 /* Frees and clears the column data in IA's separators
1124 clear_fields (struct import_assistant *ia)
1126 struct separators_page *s = &ia->separators;
1128 if (s->column_cnt > 0)
1133 for (row = 0; row < ia->file.line_cnt; row++)
1135 const struct string *line = &ia->file.lines[row];
1136 const char *line_start = ds_data (line);
1137 const char *line_end = ds_end (line);
1139 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1141 char *s = ss_data (col->contents[row]);
1142 if (!(s >= line_start && s <= line_end))
1143 ss_dealloc (&col->contents[row]);
1147 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1150 free (col->contents);
1159 /* Breaks the file data in IA into columns based on the
1160 separators set in IA's separators substructure. */
1162 split_fields (struct import_assistant *ia)
1164 struct separators_page *s = &ia->separators;
1165 size_t columns_allocated;
1171 /* Is space in the set of separators? */
1172 space_sep = ss_find_char (ds_ss (&s->separators), ' ') != SIZE_MAX;
1174 /* Split all the lines, not just those from
1175 ia->first_line.skip_lines on, so that we split the line that
1176 contains variables names if ia->first_line.variable_names is
1178 columns_allocated = 0;
1179 for (row = 0; row < ia->file.line_cnt; row++)
1181 struct string *line = &ia->file.lines[row];
1182 struct substring text = ds_ss (line);
1185 for (column_idx = 0; ; column_idx++)
1187 struct substring field;
1188 struct column *column;
1191 ss_ltrim (&text, ss_cstr (" "));
1192 if (ss_is_empty (text))
1194 if (column_idx != 0)
1198 else if (!ds_is_empty (&s->quotes)
1199 && ds_find_char (&s->quotes, text.string[0]) != SIZE_MAX)
1201 int quote = ss_get_char (&text);
1203 ss_get_until (&text, quote, &field);
1210 while ((c = ss_get_char (&text)) != EOF)
1212 ds_put_char (&s, c);
1213 else if (ss_match_char (&text, quote))
1214 ds_put_char (&s, quote);
1221 ss_get_chars (&text, ss_cspan (text, ds_ss (&s->separators)),
1224 if (column_idx >= s->column_cnt)
1226 struct column *column;
1228 if (s->column_cnt >= columns_allocated)
1229 s->columns = x2nrealloc (s->columns, &columns_allocated,
1230 sizeof *s->columns);
1231 column = &s->columns[s->column_cnt++];
1232 column->name = NULL;
1234 column->contents = xcalloc (ia->file.line_cnt,
1235 sizeof *column->contents);
1237 column = &s->columns[column_idx];
1238 column->contents[row] = field;
1239 if (ss_length (field) > column->width)
1240 column->width = ss_length (field);
1243 ss_ltrim (&text, ss_cstr (" "));
1244 if (ss_is_empty (text))
1246 if (ss_find_char (ds_ss (&s->separators), ss_first (text))
1248 ss_advance (&text, 1);
1253 /* Chooses a name for each column on the separators page */
1255 choose_column_names (struct import_assistant *ia)
1257 const struct first_line_page *f = &ia->first_line;
1258 struct separators_page *s = &ia->separators;
1259 struct dictionary *dict;
1260 unsigned long int generated_name_count = 0;
1264 dict = dict_create ();
1265 name_row = f->variable_names && f->skip_lines ? f->skip_lines : 0;
1266 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1268 char name[VAR_NAME_LEN + 1];
1271 hint = name_row ? ss_xstrdup (col->contents[name_row - 1]) : NULL;
1272 if (!dict_make_unique_var_name (dict, hint, &generated_name_count, name))
1276 col->name = xstrdup (name);
1277 dict_create_var_assert (dict, name, 0);
1279 dict_destroy (dict);
1282 /* Picks the most likely separator and quote characters based on
1285 choose_likely_separators (struct import_assistant *ia)
1287 unsigned long int histogram[UCHAR_MAX + 1] = { 0 };
1290 /* Construct a histogram of all the characters used in the
1292 for (row = 0; row < ia->file.line_cnt; row++)
1294 struct substring line = ds_ss (&ia->file.lines[row]);
1295 size_t length = ss_length (line);
1297 for (i = 0; i < length; i++)
1298 histogram[(unsigned char) line.string[i]]++;
1301 find_commonest_chars (histogram, "\"'", "", &ia->separators.quotes);
1302 find_commonest_chars (histogram, ",;:/|!\t-", ",",
1303 &ia->separators.separators);
1304 ia->separators.escape = true;
1307 /* Chooses the most common character among those in TARGETS,
1308 based on the frequency data in HISTOGRAM, and stores it in
1309 RESULT. If there is a tie for the most common character among
1310 those in TARGETS, the earliest character is chosen. If none
1311 of the TARGETS appear at all, then DEF is used as a
1314 find_commonest_chars (unsigned long int histogram[UCHAR_MAX + 1],
1315 const char *targets, const char *def,
1316 struct string *result)
1318 unsigned char max = 0;
1319 unsigned long int max_count = 0;
1321 for (; *targets != '\0'; targets++)
1323 unsigned char c = *targets;
1324 unsigned long int count = histogram[c];
1325 if (count > max_count)
1334 ds_put_char (result, max);
1337 ds_assign_cstr (result, def);
1340 /* Revises the contents of the fields tree view based on the
1341 currently chosen set of separators. */
1343 revise_fields_preview (struct import_assistant *ia)
1347 push_watch_cursor (ia);
1349 w = GTK_WIDGET (ia->separators.fields_tree_view);
1350 gtk_widget_destroy (w);
1351 get_separators (ia);
1353 choose_column_names (ia);
1354 ia->separators.fields_tree_view = create_data_tree_view (
1356 GTK_CONTAINER (get_widget_assert (ia->asst.builder, "fields-scroller")),
1359 pop_watch_cursor (ia);
1362 /* Sets the widgets to match IA's separators substructure. */
1364 set_separators (struct import_assistant *ia)
1366 struct separators_page *s = &ia->separators;
1368 struct string custom;
1373 ds_init_empty (&custom);
1375 for (i = 0; i < ds_length (&s->separators); i++)
1377 unsigned char c = ds_at (&s->separators, i);
1380 for (j = 0; j < SEPARATOR_CNT; j++)
1382 const struct separator *s = &separators[j];
1390 ds_put_char (&custom, c);
1394 for (i = 0; i < SEPARATOR_CNT; i++)
1396 const struct separator *s = &separators[i];
1397 GtkWidget *button = get_widget_assert (ia->asst.builder, s->name);
1398 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
1399 (seps & (1u << i)) != 0);
1401 any_custom = !ds_is_empty (&custom);
1402 gtk_entry_set_text (GTK_ENTRY (s->custom_entry), ds_cstr (&custom));
1403 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->custom_cb),
1405 gtk_widget_set_sensitive (s->custom_entry, any_custom);
1406 ds_destroy (&custom);
1408 any_quotes = !ds_is_empty (&s->quotes);
1410 gtk_entry_set_text (s->quote_entry,
1411 any_quotes ? ds_cstr (&s->quotes) : "\"");
1412 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->quote_cb),
1414 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->escape_cb),
1416 gtk_widget_set_sensitive (s->quote_combo, any_quotes);
1417 gtk_widget_set_sensitive (s->escape_cb, any_quotes);
1420 /* Sets IA's separators substructure to match the widgets. */
1422 get_separators (struct import_assistant *ia)
1424 struct separators_page *s = &ia->separators;
1427 ds_clear (&s->separators);
1428 for (i = 0; i < SEPARATOR_CNT; i++)
1430 const struct separator *sep = &separators[i];
1431 GtkWidget *button = get_widget_assert (ia->asst.builder, sep->name);
1432 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
1433 ds_put_char (&s->separators, sep->c);
1436 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->custom_cb)))
1437 ds_put_cstr (&s->separators,
1438 gtk_entry_get_text (GTK_ENTRY (s->custom_entry)));
1440 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->quote_cb)))
1442 gchar *text = gtk_combo_box_get_active_text (
1443 GTK_COMBO_BOX (s->quote_combo));
1444 ds_assign_cstr (&s->quotes, text);
1448 ds_clear (&s->quotes);
1449 s->escape = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->escape_cb));
1452 /* Called when the user changes the entry field for custom
1455 on_separators_custom_entry_notify (GObject *gobject UNUSED,
1456 GParamSpec *arg1 UNUSED,
1457 struct import_assistant *ia)
1459 revise_fields_preview (ia);
1462 /* Called when the user toggles the checkbox that enables custom
1465 on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
1466 struct import_assistant *ia)
1468 bool is_active = gtk_toggle_button_get_active (custom_cb);
1469 gtk_widget_set_sensitive (ia->separators.custom_entry, is_active);
1470 revise_fields_preview (ia);
1473 /* Called when the user changes the selection in the combo box
1474 that selects a quote character. */
1476 on_quote_combo_change (GtkComboBox *combo, struct import_assistant *ia)
1478 revise_fields_preview (ia);
1481 /* Called when the user toggles the checkbox that enables
1484 on_quote_cb_toggle (GtkToggleButton *quote_cb, struct import_assistant *ia)
1486 bool is_active = gtk_toggle_button_get_active (quote_cb);
1487 gtk_widget_set_sensitive (ia->separators.quote_combo, is_active);
1488 gtk_widget_set_sensitive (ia->separators.escape_cb, is_active);
1489 revise_fields_preview (ia);
1492 /* Called when the user toggles one of the separators
1495 on_separator_toggle (GtkToggleButton *toggle UNUSED,
1496 struct import_assistant *ia)
1498 revise_fields_preview (ia);
1501 /* Called to render one of the cells in the fields preview tree
1504 render_input_cell (GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1505 GtkTreeModel *model, GtkTreeIter *iter,
1508 struct import_assistant *ia = ia_;
1509 struct substring field;
1513 column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1515 row = text_import_model_iter_to_row (iter) + ia->first_line.skip_lines;
1516 field = ia->separators.columns[column].contents[row];
1517 if (field.string != NULL)
1519 GValue text = {0, };
1520 g_value_init (&text, G_TYPE_STRING);
1521 g_value_take_string (&text, ss_xstrdup (field));
1522 g_object_set_property (G_OBJECT (cell), "text", &text);
1523 g_value_unset (&text);
1524 g_object_set (cell, "background-set", FALSE, (void *) NULL);
1529 "background", "red",
1530 "background-set", TRUE,
1534 /* Called to render a tooltip on one of the cells in the fields
1535 preview tree view. */
1537 on_query_input_tooltip (GtkWidget *widget, gint wx, gint wy,
1538 gboolean keyboard_mode UNUSED,
1539 GtkTooltip *tooltip, struct import_assistant *ia)
1543 if (!get_tooltip_location (widget, wx, wy, ia, &row, &column))
1546 if (ia->separators.columns[column].contents[row].string != NULL)
1549 gtk_tooltip_set_text (tooltip,
1550 _("This input line has too few separators "
1551 "to fill in this field."));
1555 /* The "formats" page of the assistant. */
1557 static void on_variable_change (PsppireDict *dict, int idx,
1558 struct import_assistant *);
1559 static void clear_modified_vars (struct import_assistant *);
1561 /* Initializes IA's formats substructure. */
1563 init_formats_page (struct import_assistant *ia)
1565 GtkBuilder *builder = ia->asst.builder;
1566 struct formats_page *p = &ia->formats;
1568 p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Formats"),
1569 GTK_ASSISTANT_PAGE_CONFIRM);
1570 p->data_tree_view = GTK_TREE_VIEW (get_widget_assert (builder, "data"));
1571 p->modified_vars = NULL;
1572 p->modified_var_cnt = 0;
1576 /* Frees IA's formats substructure. */
1578 destroy_formats_page (struct import_assistant *ia)
1580 struct formats_page *p = &ia->formats;
1582 if (p->psppire_dict != NULL)
1584 /* This destroys p->dict also. */
1585 g_object_unref (p->psppire_dict);
1587 clear_modified_vars (ia);
1590 /* Called just before the formats page of the assistant is
1593 prepare_formats_page (struct import_assistant *ia)
1595 struct dictionary *dict;
1596 PsppireDict *psppire_dict;
1597 PsppireVarStore *var_store;
1598 GtkBin *vars_scroller;
1599 GtkWidget *old_var_sheet;
1600 PsppireVarSheet *var_sheet;
1601 struct separators_page *s = &ia->separators;
1602 struct formats_page *p = &ia->formats;
1603 struct fmt_guesser *fg;
1604 unsigned long int number = 0;
1607 push_watch_cursor (ia);
1609 dict = dict_create ();
1610 fg = fmt_guesser_create ();
1611 for (column_idx = 0; column_idx < s->column_cnt; column_idx++)
1613 struct variable *modified_var;
1614 char name[VAR_NAME_LEN + 1];
1616 modified_var = (column_idx < p->modified_var_cnt
1617 ? p->modified_vars[column_idx] : NULL);
1618 if (modified_var == NULL)
1620 struct column *column = &s->columns[column_idx];
1621 struct variable *var;
1622 struct fmt_spec format;
1625 /* Choose variable name. */
1626 if (!dict_make_unique_var_name (dict, column->name, &number, name))
1629 /* Choose variable format. */
1630 fmt_guesser_clear (fg);
1631 for (row = ia->first_line.skip_lines; row < ia->file.line_cnt; row++)
1632 fmt_guesser_add (fg, column->contents[row]);
1633 fmt_guesser_guess (fg, &format);
1634 fmt_fix_input (&format);
1636 /* Create variable. */
1637 var = dict_create_var_assert (dict, name, fmt_var_width (&format));
1638 var_set_both_formats (var, &format);
1642 if (!dict_make_unique_var_name (dict, var_get_name (modified_var),
1645 dict_clone_var_as_assert (dict, modified_var, name);
1648 fmt_guesser_destroy (fg);
1650 psppire_dict = psppire_dict_new_from_dict (dict);
1651 g_signal_connect (psppire_dict, "variable_changed",
1652 G_CALLBACK (on_variable_change), ia);
1653 ia->formats.dict = dict;
1654 ia->formats.psppire_dict = psppire_dict;
1656 /* XXX: PsppireVarStore doesn't hold a reference to
1657 psppire_dict for now, but it should. After it does, we
1658 should g_object_ref the psppire_dict here, since we also
1659 hold a reference via ia->formats.dict. */
1660 var_store = psppire_var_store_new (psppire_dict);
1661 g_object_set (var_store,
1662 "format-type", PSPPIRE_VAR_STORE_INPUT_FORMATS,
1664 var_sheet = PSPPIRE_VAR_SHEET (psppire_var_sheet_new ());
1665 g_object_set (var_sheet,
1667 "may-create-vars", FALSE,
1670 vars_scroller = GTK_BIN (get_widget_assert (ia->asst.builder, "vars-scroller"));
1671 old_var_sheet = gtk_bin_get_child (vars_scroller);
1672 if (old_var_sheet != NULL)
1673 gtk_widget_destroy (old_var_sheet);
1674 gtk_container_add (GTK_CONTAINER (vars_scroller), GTK_WIDGET (var_sheet));
1675 gtk_widget_show (GTK_WIDGET (var_sheet));
1677 gtk_widget_destroy (GTK_WIDGET (ia->formats.data_tree_view));
1678 ia->formats.data_tree_view = create_data_tree_view (
1680 GTK_CONTAINER (get_widget_assert (ia->asst.builder, "data-scroller")),
1683 pop_watch_cursor (ia);
1686 /* Clears the set of user-modified variables from IA's formats
1687 substructure. This discards user modifications to variable
1688 formats, thereby causing formats to revert to their
1691 clear_modified_vars (struct import_assistant *ia)
1693 struct formats_page *p = &ia->formats;
1696 for (i = 0; i < p->modified_var_cnt; i++)
1697 var_destroy (p->modified_vars[i]);
1698 free (p->modified_vars);
1699 p->modified_vars = NULL;
1700 p->modified_var_cnt = 0;
1703 /* Resets the formats page to its defaults, discarding user
1706 reset_formats_page (struct import_assistant *ia)
1708 clear_modified_vars (ia);
1709 prepare_formats_page (ia);
1712 /* Called when the user changes one of the variables in the
1715 on_variable_change (PsppireDict *dict, int dict_idx,
1716 struct import_assistant *ia)
1718 struct formats_page *p = &ia->formats;
1719 GtkTreeView *tv = ia->formats.data_tree_view;
1720 gint column_idx = dict_idx + 1;
1722 push_watch_cursor (ia);
1724 /* Remove previous column and replace with new column. */
1725 gtk_tree_view_remove_column (tv, gtk_tree_view_get_column (tv, column_idx));
1726 gtk_tree_view_insert_column (tv, make_data_column (ia, tv, false, dict_idx),
1729 /* Save a copy of the modified variable in modified_vars, so
1730 that its attributes will be preserved if we back up to the
1731 previous page with the Prev button and then come back
1733 if (dict_idx >= p->modified_var_cnt)
1736 p->modified_vars = xnrealloc (p->modified_vars, dict_idx + 1,
1737 sizeof *p->modified_vars);
1738 for (i = 0; i <= dict_idx; i++)
1739 p->modified_vars[i] = NULL;
1740 p->modified_var_cnt = dict_idx + 1;
1742 if (p->modified_vars[dict_idx])
1743 var_destroy (p->modified_vars[dict_idx]);
1744 p->modified_vars[dict_idx]
1745 = var_clone (psppire_dict_get_variable (dict, dict_idx));
1747 pop_watch_cursor (ia);
1750 /* Parses the contents of the field at (ROW,COLUMN) according to
1751 its variable format. If OUTPUTP is non-null, then *OUTPUTP
1752 receives the formatted output for that field (which must be
1753 freed with free). If TOOLTIPP is non-null, then *TOOLTIPP
1754 receives a message suitable for use in a tooltip, if one is
1755 needed, or a null pointer otherwise. Returns true if a
1756 tooltip message is needed, otherwise false. */
1758 parse_field (struct import_assistant *ia,
1759 size_t row, size_t column,
1760 char **outputp, char **tooltipp)
1762 struct substring field;
1764 struct variable *var;
1765 const struct fmt_spec *in;
1766 struct fmt_spec out;
1770 field = ia->separators.columns[column].contents[row];
1771 var = dict_get_var (ia->formats.dict, column);
1772 value_init (&val, var_get_width (var));
1773 in = var_get_print_format (var);
1774 out = fmt_for_output_from_input (in);
1776 if (field.string != NULL)
1780 error = data_in (field, LEGACY_NATIVE, in->type, &val,
1781 var_get_width (var),
1782 dict_get_encoding (ia->formats.dict));
1785 tooltip = xasprintf (_("Cannot parse field content `%.*s' as "
1787 (int) field.length, field.string,
1788 fmt_name (in->type), error);
1794 tooltip = xstrdup (_("This input line has too few separators "
1795 "to fill in this field."));
1796 value_set_missing (&val, var_get_width (var));
1798 if (outputp != NULL)
1800 *outputp = data_out (&val, dict_get_encoding (ia->formats.dict), &out);
1802 value_destroy (&val, var_get_width (var));
1804 ok = tooltip == NULL;
1805 if (tooltipp != NULL)
1806 *tooltipp = tooltip;
1812 /* Called to render one of the cells in the data preview tree
1815 render_output_cell (GtkTreeViewColumn *tree_column,
1816 GtkCellRenderer *cell,
1817 GtkTreeModel *model,
1821 struct import_assistant *ia = ia_;
1823 GValue gvalue = { 0, };
1826 ok = parse_field (ia,
1827 (text_import_model_iter_to_row (iter)
1828 + ia->first_line.skip_lines),
1829 GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1833 g_value_init (&gvalue, G_TYPE_STRING);
1834 g_value_take_string (&gvalue, output);
1835 g_object_set_property (G_OBJECT (cell), "text", &gvalue);
1836 g_value_unset (&gvalue);
1839 g_object_set (cell, "background-set", FALSE, (void *) NULL);
1842 "background", "red",
1843 "background-set", TRUE,
1847 /* Called to render a tooltip for one of the cells in the data
1848 preview tree view. */
1850 on_query_output_tooltip (GtkWidget *widget, gint wx, gint wy,
1851 gboolean keyboard_mode UNUSED,
1852 GtkTooltip *tooltip, struct import_assistant *ia)
1857 if (!get_tooltip_location (widget, wx, wy, ia, &row, &column))
1860 if (parse_field (ia, row, column, NULL, &text))
1863 gtk_tooltip_set_text (tooltip, text);
1868 /* Utility functions used by multiple pages of the assistant. */
1871 get_tooltip_location (GtkWidget *widget, gint wx, gint wy,
1872 const struct import_assistant *ia,
1873 size_t *row, size_t *column)
1875 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
1879 GtkTreeViewColumn *tree_column;
1880 GtkTreeModel *tree_model;
1883 /* Check that WIDGET is really visible on the screen before we
1884 do anything else. This is a bug fix for a sticky situation:
1885 when text_data_import_assistant() returns, it frees the data
1886 necessary to compose the tool tip message, but there may be
1887 a tool tip under preparation at that point (even if there is
1888 no visible tool tip) that will call back into us a little
1889 bit later. Perhaps the correct solution to this problem is
1890 to make the data related to the tool tips part of a GObject
1891 that only gets destroyed when all references are released,
1892 but this solution appears to be effective too. */
1893 if (!GTK_WIDGET_MAPPED (widget))
1896 gtk_tree_view_convert_widget_to_bin_window_coords (tree_view,
1898 if (!gtk_tree_view_get_path_at_pos (tree_view, bx, by,
1899 &path, &tree_column, NULL, NULL))
1902 *column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1905 tree_model = gtk_tree_view_get_model (tree_view);
1906 ok = gtk_tree_model_get_iter (tree_model, &iter, path);
1907 gtk_tree_path_free (path);
1911 *row = text_import_model_iter_to_row (&iter) + ia->first_line.skip_lines;
1916 make_tree_view (const struct import_assistant *ia,
1918 GtkTreeView **tree_view)
1920 GtkTreeModel *model;
1922 *tree_view = GTK_TREE_VIEW (gtk_tree_view_new ());
1923 model = GTK_TREE_MODEL (text_import_model_new (
1924 ia->file.lines + first_line,
1925 ia->file.line_cnt - first_line, first_line));
1926 gtk_tree_view_set_model (*tree_view, model);
1928 add_line_number_column (ia, *tree_view);
1932 add_line_number_column (const struct import_assistant *ia,
1933 GtkTreeView *treeview)
1935 GtkTreeViewColumn *column;
1937 column = gtk_tree_view_column_new_with_attributes (
1938 _("Line"), ia->asst.prop_renderer,
1939 "text", TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER,
1941 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
1942 gtk_tree_view_column_set_fixed_width (
1943 column, get_monospace_width (treeview, ia->asst.prop_renderer, 5));
1944 gtk_tree_view_append_column (treeview, column);
1948 get_monospace_width (GtkTreeView *treeview, GtkCellRenderer *renderer,
1955 ds_put_char_multiple (&s, '0', char_cnt);
1956 ds_put_char (&s, ' ');
1957 width = get_string_width (treeview, renderer, ds_cstr (&s));
1964 get_string_width (GtkTreeView *treeview, GtkCellRenderer *renderer,
1968 g_object_set (G_OBJECT (renderer), "text", string, (void *) NULL);
1969 gtk_cell_renderer_get_size (renderer, GTK_WIDGET (treeview),
1970 NULL, NULL, NULL, &width, NULL);
1974 static GtkTreeViewColumn *
1975 make_data_column (struct import_assistant *ia, GtkTreeView *tree_view,
1976 bool input, gint dict_idx)
1978 struct variable *var = NULL;
1979 struct column *column = NULL;
1980 char name[(VAR_NAME_LEN * 2) + 1];
1982 gint content_width, header_width;
1983 GtkTreeViewColumn *tree_column;
1986 column = &ia->separators.columns[dict_idx];
1988 var = dict_get_var (ia->formats.dict, dict_idx);
1990 escape_underscores (input ? column->name : var_get_name (var), name);
1991 char_cnt = input ? column->width : var_get_print_format (var)->w;
1992 content_width = get_monospace_width (tree_view, ia->asst.fixed_renderer,
1994 header_width = get_string_width (tree_view, ia->asst.prop_renderer,
1997 tree_column = gtk_tree_view_column_new ();
1998 g_object_set_data (G_OBJECT (tree_column), "column-number",
1999 GINT_TO_POINTER (dict_idx));
2000 gtk_tree_view_column_set_title (tree_column, name);
2001 gtk_tree_view_column_pack_start (tree_column, ia->asst.fixed_renderer,
2003 gtk_tree_view_column_set_cell_data_func (
2004 tree_column, ia->asst.fixed_renderer,
2005 input ? render_input_cell : render_output_cell, ia, NULL);
2006 gtk_tree_view_column_set_sizing (tree_column, GTK_TREE_VIEW_COLUMN_FIXED);
2007 gtk_tree_view_column_set_fixed_width (tree_column, MAX (content_width,
2013 static GtkTreeView *
2014 create_data_tree_view (bool input, GtkContainer *parent,
2015 struct import_assistant *ia)
2017 GtkTreeView *tree_view;
2020 make_tree_view (ia, ia->first_line.skip_lines, &tree_view);
2021 gtk_tree_selection_set_mode (gtk_tree_view_get_selection (tree_view),
2022 GTK_SELECTION_NONE);
2024 for (i = 0; i < ia->separators.column_cnt; i++)
2025 gtk_tree_view_append_column (tree_view,
2026 make_data_column (ia, tree_view, input, i));
2028 g_object_set (G_OBJECT (tree_view), "has-tooltip", TRUE, (void *) NULL);
2029 g_signal_connect (tree_view, "query-tooltip",
2030 G_CALLBACK (input ? on_query_input_tooltip
2031 : on_query_output_tooltip), ia);
2032 gtk_tree_view_set_fixed_height_mode (tree_view, true);
2034 gtk_container_add (parent, GTK_WIDGET (tree_view));
2035 gtk_widget_show (GTK_WIDGET (tree_view));
2041 escape_underscores (const char *in, char *out)
2043 for (; *in != '\0'; in++)
2052 /* TextImportModel, a GtkTreeModel implementation used by some
2053 pages of the assistant. */
2055 #define G_TYPE_TEXT_IMPORT_MODEL (text_import_model_get_type ())
2056 #define TEXT_IMPORT_MODEL(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), G_TYPE_TEXT_IMPORT_MODEL, TextImportModel))
2057 #define TEXT_IMPORT_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), G_TYPE_TEXT_IMPORT_MODEL, TextImportModelClass))
2058 #define IS_TEXT_IMPORT_MODEL(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), G_TYPE_TEXT_IMPORT_MODEL))
2059 #define IS_TEXT_IMPORT_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), G_TYPE_TEXT_IMPORT_MODEL))
2060 #define TEXT_IMPORT_MODEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), G_TYPE_TEXT_IMPORT_MODEL, TextImportModelClass))
2062 /* Random number used in 'stamp' member of GtkTreeIter. */
2063 #define TREE_MODEL_STAMP 0x7efd67d3
2065 struct TextImportModel
2068 struct string *lines;
2073 struct TextImportModelClass
2075 GObjectClass parent_class;
2078 GType text_import_model_get_type (void);
2079 static void text_import_model_tree_model_init (gpointer iface, gpointer data);
2082 text_import_model_get_type (void)
2084 static GType object_type = 0;
2088 static const GTypeInfo object_info = {
2089 sizeof (TextImportModelClass),
2090 (GBaseInitFunc) NULL,
2091 (GBaseFinalizeFunc) NULL,
2092 NULL, /* class_init */
2093 NULL, /* class_finalize */
2094 NULL, /* class_data */
2095 sizeof (TextImportModel),
2096 0, /* n_preallocs */
2097 NULL, /* instance_init */
2100 static const GInterfaceInfo tree_model_info = {
2101 text_import_model_tree_model_init,
2106 object_type = g_type_register_static (G_TYPE_OBJECT,
2110 g_type_add_interface_static (object_type, GTK_TYPE_TREE_MODEL,
2120 /* Creates and returns a new TextImportModel that contains the
2121 LINE_CNT lines in LINES. The lines before FIRST_LINE in LINES
2122 are not part of the model, but they are included in the line
2123 numbers in the TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER column.
2125 The caller retains responsibility for freeing LINES and must
2126 ensure that its lifetime and that of the strings that it
2127 contains exceeds that of the TextImportModel. */
2129 text_import_model_new (struct string *lines, size_t line_cnt,
2132 TextImportModel *new_text_import_model
2133 = g_object_new (G_TYPE_TEXT_IMPORT_MODEL, NULL);
2134 new_text_import_model->lines = lines;
2135 new_text_import_model->line_cnt = line_cnt;
2136 new_text_import_model->first_line = first_line;
2137 return new_text_import_model;
2142 tree_model_iter_has_child (GtkTreeModel *tree_model,
2149 tree_model_iter_parent (GtkTreeModel *tree_model,
2156 static GtkTreeModelFlags
2157 tree_model_get_flags (GtkTreeModel *model)
2159 g_return_val_if_fail (IS_TEXT_IMPORT_MODEL (model), (GtkTreeModelFlags) 0);
2161 return GTK_TREE_MODEL_LIST_ONLY | GTK_TREE_MODEL_ITERS_PERSIST;
2166 tree_model_n_columns (GtkTreeModel *model)
2172 tree_model_column_type (GtkTreeModel *model, gint index)
2174 return (index == TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER ? G_TYPE_INT
2175 : index == TEXT_IMPORT_MODEL_COLUMN_LINE ? G_TYPE_STRING
2180 init_iter (TextImportModel *list, gint idx, GtkTreeIter *iter)
2182 if (idx < 0 || idx >= list->line_cnt)
2185 iter->user_data = GINT_TO_POINTER (-1);
2190 iter->stamp = TREE_MODEL_STAMP;
2191 iter->user_data = GINT_TO_POINTER (idx);
2197 tree_model_get_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreePath *path)
2199 gint *indices, depth;
2201 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2203 g_return_val_if_fail (path, FALSE);
2205 indices = gtk_tree_path_get_indices (path);
2206 depth = gtk_tree_path_get_depth (path);
2208 g_return_val_if_fail (depth == 1, FALSE);
2210 return init_iter (list, indices[0], iter);
2215 tree_model_iter_next (GtkTreeModel *model, GtkTreeIter *iter)
2217 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2220 assert (iter->stamp == TREE_MODEL_STAMP);
2222 idx = GPOINTER_TO_INT (iter->user_data);
2223 return init_iter (list, idx == -1 ? -1 : idx + 1, iter);
2226 static GtkTreePath *
2227 tree_model_get_path (GtkTreeModel *model, GtkTreeIter *iter)
2231 g_return_val_if_fail (iter->stamp == TREE_MODEL_STAMP, FALSE);
2233 path = gtk_tree_path_new ();
2234 gtk_tree_path_append_index (path, GPOINTER_TO_INT (iter->user_data));
2240 tree_model_get_value (GtkTreeModel *model, GtkTreeIter *iter,
2241 gint column, GValue *value)
2243 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2246 g_return_if_fail (iter->stamp == TREE_MODEL_STAMP);
2248 idx = GPOINTER_TO_INT (iter->user_data);
2249 assert (idx >= 0 && idx < list->line_cnt);
2253 g_value_init (value, G_TYPE_INT);
2254 g_value_set_int (value, idx + list->first_line + 1);
2258 g_value_init (value, G_TYPE_STRING);
2259 g_value_set_static_string (value, ds_cstr (&list->lines[idx]));
2264 tree_model_iter_children (GtkTreeModel *tree_model,
2266 GtkTreeIter *parent)
2272 tree_model_n_children (GtkTreeModel *model, GtkTreeIter *iter)
2274 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2276 return iter == NULL ? list->line_cnt : 0;
2280 tree_model_nth_child (GtkTreeModel *model, GtkTreeIter *iter,
2281 GtkTreeIter *parent, gint n)
2283 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2284 g_return_val_if_fail (IS_TEXT_IMPORT_MODEL (model), FALSE);
2288 return init_iter (list, n, iter);
2292 text_import_model_tree_model_init (gpointer iface_, gpointer data UNUSED)
2294 GtkTreeModelIface *iface = (GtkTreeModelIface *) iface_;
2296 iface->get_flags = tree_model_get_flags;
2297 iface->get_n_columns = tree_model_n_columns;
2298 iface->get_column_type = tree_model_column_type;
2299 iface->get_iter = tree_model_get_iter;
2300 iface->iter_next = tree_model_iter_next;
2301 iface->get_path = tree_model_get_path;
2302 iface->get_value = tree_model_get_value;
2304 iface->iter_children = tree_model_iter_children;
2305 iface->iter_has_child = tree_model_iter_has_child;
2306 iface->iter_n_children = tree_model_n_children;
2307 iface->iter_nth_child = tree_model_nth_child;
2308 iface->iter_parent = tree_model_iter_parent;
2312 text_import_model_iter_to_row (const GtkTreeIter *iter)
2314 assert (iter->stamp == TREE_MODEL_STAMP);
2315 return GPOINTER_TO_INT (iter->user_data);
2318 /* Increments the "watch cursor" level, setting the cursor for
2319 the assistant window to a watch face to indicate to the user
2320 that the ongoing operation may take some time. */
2322 push_watch_cursor (struct import_assistant *ia)
2324 if (++ia->asst.watch_cursor == 1)
2326 GtkWidget *widget = GTK_WIDGET (ia->asst.assistant);
2327 GdkDisplay *display = gtk_widget_get_display (widget);
2328 GdkCursor *cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
2329 gdk_window_set_cursor (widget->window, cursor);
2330 gdk_cursor_unref (cursor);
2331 gdk_display_flush (display);
2335 /* Decrements the "watch cursor" level. If the level reaches
2336 zero, the cursor is reset to its default shape. */
2338 pop_watch_cursor (struct import_assistant *ia)
2340 if (--ia->asst.watch_cursor == 0)
2342 GtkWidget *widget = GTK_WIDGET (ia->asst.assistant);
2343 gdk_window_set_cursor (widget->window, NULL);