1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2008, 2009 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/>. */
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 (GObject *o, 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_in_new_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++)
315 ds_put_cstr (s, ", ");
316 mv_get_value (mv, &value, j);
317 syntax_gen_value (s, &value, width, format);
320 if (mv_has_range (mv))
323 if (mv_has_value (mv))
324 ds_put_cstr (s, ", ");
325 mv_get_range (mv, &low, &high);
326 syntax_gen_num_range (s, low, high, format);
328 ds_put_cstr (s, ").\n");
330 if (var_has_value_labels (var))
332 const struct val_labs *vls = var_get_value_labels (var);
333 const struct val_lab **labels = val_labs_sorted (vls);
334 size_t n_labels = val_labs_count (vls);
337 syntax_gen_pspp (s, "VALUE LABELS %ss", name);
338 for (i = 0; i < n_labels; i++)
340 const struct val_lab *vl = labels[i];
341 ds_put_cstr (s, "\n ");
342 syntax_gen_value (s, &vl->value, width, format);
343 ds_put_char (s, ' ');
344 syntax_gen_string (s, ss_cstr (val_lab_get_label (vl)));
347 ds_put_cstr (s, ".\n");
349 if (var_has_label (var))
350 syntax_gen_pspp (s, "VARIABLE LABELS %ss %sq.\n",
351 name, var_get_label (var));
352 if (measure != var_default_measure (type))
353 syntax_gen_pspp (s, "VARIABLE LEVEL %ss (%ss).\n",
355 (measure == MEASURE_NOMINAL ? "NOMINAL"
356 : measure == MEASURE_ORDINAL ? "ORDINAL"
358 if (alignment != var_default_alignment (type))
359 syntax_gen_pspp (s, "VARIABLE ALIGNMENT %ss (%ss).\n",
361 (alignment == ALIGN_LEFT ? "LEFT"
362 : alignment == ALIGN_CENTRE ? "CENTER"
364 if (var_get_display_width (var) != var_default_display_width (width))
365 syntax_gen_pspp (s, "VARIABLE WIDTH %ss (%d).\n",
366 name, var_get_display_width (var));
370 /* Generates and returns PSPP syntax to execute the import
371 operation described by IA. The caller must free the syntax
374 generate_syntax (const struct import_assistant *ia)
376 struct string s = DS_EMPTY_INITIALIZER;
385 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (
386 ia->intro.n_cases_button)))
387 ds_put_format (&s, " /IMPORTCASES=FIRST %d\n",
388 gtk_spin_button_get_value_as_int (
389 GTK_SPIN_BUTTON (ia->intro.n_cases_spin)));
390 else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (
391 ia->intro.percent_button)))
392 ds_put_format (&s, " /IMPORTCASES=PERCENT %d\n",
393 gtk_spin_button_get_value_as_int (
394 GTK_SPIN_BUTTON (ia->intro.percent_spin)));
396 ds_put_cstr (&s, " /IMPORTCASES=ALL\n");
398 " /ARRANGEMENT=DELIMITED\n"
400 if (ia->first_line.skip_lines > 0)
401 ds_put_format (&s, " /FIRSTCASE=%d\n", ia->first_line.skip_lines + 1);
402 ds_put_cstr (&s, " /DELIMITERS=\"");
403 if (ds_find_char (&ia->separators.separators, '\t') != SIZE_MAX)
404 ds_put_cstr (&s, "\\t");
405 if (ds_find_char (&ia->separators.separators, '\\') != SIZE_MAX)
406 ds_put_cstr (&s, "\\\\");
407 for (i = 0; i < ds_length (&ia->separators.separators); i++)
409 char c = ds_at (&ia->separators.separators, i);
411 ds_put_cstr (&s, "\"\"");
412 else if (c != '\t' && c != '\\')
415 ds_put_cstr (&s, "\"\n");
416 if (!ds_is_empty (&ia->separators.quotes))
417 syntax_gen_pspp (&s, " /QUALIFIER=%sq\n", ds_cstr (&ia->separators.quotes));
418 if (!ds_is_empty (&ia->separators.quotes) && ia->separators.escape)
419 ds_put_cstr (&s, " /ESCAPE\n");
420 ds_put_cstr (&s, " /VARIABLES=\n");
422 var_cnt = dict_get_var_cnt (ia->formats.dict);
423 for (i = 0; i < var_cnt; i++)
425 struct variable *var = dict_get_var (ia->formats.dict, i);
426 char format_string[FMT_STRING_LEN_MAX + 1];
427 fmt_to_string (var_get_print_format (var), format_string);
428 ds_put_format (&s, " %s %s%s\n",
429 var_get_name (var), format_string,
430 i == var_cnt - 1 ? "." : "");
433 apply_dict (ia->formats.dict, &s);
438 /* Choosing a file and reading it. */
440 static char *choose_file (GtkWindow *parent_window);
442 /* Obtains the file to import from the user and initializes IA's
443 file substructure. PARENT_WINDOW must be the window to use
444 as the file chooser window's parent.
446 Returns true if successful, false if the file name could not
447 be obtained or the file could not be read. */
449 init_file (struct import_assistant *ia, GtkWindow *parent_window)
451 struct file *file = &ia->file;
452 enum { MAX_PREVIEW_LINES = 1000 }; /* Max number of lines to read. */
453 enum { MAX_LINE_LEN = 16384 }; /* Max length of an acceptable line. */
456 file->file_name = choose_file (parent_window);
457 if (file->file_name == NULL)
460 stream = fopen (file->file_name, "r");
463 msg (ME, _("Could not open \"%s\": %s"),
464 file->file_name, strerror (errno));
468 file->lines = xnmalloc (MAX_PREVIEW_LINES, sizeof *file->lines);
469 for (; file->line_cnt < MAX_PREVIEW_LINES; file->line_cnt++)
471 struct string *line = &file->lines[file->line_cnt];
473 ds_init_empty (line);
474 if (!ds_read_line (line, stream, MAX_LINE_LEN))
478 else if (ferror (stream))
479 msg (ME, _("Error reading \"%s\": %s"),
480 file->file_name, strerror (errno));
482 msg (ME, _("Failed to read \"%s\", because it contains a line "
483 "over %d bytes long and therefore appears not to be "
485 file->file_name, MAX_LINE_LEN);
490 ds_chomp (line, '\n');
491 ds_chomp (line, '\r');
494 if (file->line_cnt == 0)
496 msg (ME, _("\"%s\" is empty."), file->file_name);
502 /* Estimate the number of lines in the file. */
503 if (file->line_cnt < MAX_PREVIEW_LINES)
504 file->total_lines = file->line_cnt;
508 off_t position = ftello (stream);
509 if (fstat (fileno (stream), &s) == 0 && position > 0)
510 file->total_lines = (double) file->line_cnt / position * s.st_size;
512 file->total_lines = 0;
518 /* Frees IA's file substructure. */
520 destroy_file (struct import_assistant *ia)
522 struct file *f = &ia->file;
525 for (i = 0; i < f->line_cnt; i++)
526 ds_destroy (&f->lines[i]);
528 g_free (f->file_name);
531 /* Obtains the file to read from the user and returns the name of
532 the file as a string that must be freed with g_free if
533 successful, otherwise a null pointer. PARENT_WINDOW must be
534 the window to use as the file chooser window's parent. */
536 choose_file (GtkWindow *parent_window)
541 dialog = gtk_file_chooser_dialog_new (_("Import Delimited Text Data"),
543 GTK_FILE_CHOOSER_ACTION_OPEN,
544 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
545 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
548 switch (gtk_dialog_run (GTK_DIALOG (dialog)))
550 case GTK_RESPONSE_ACCEPT:
551 file_name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
557 gtk_widget_destroy (dialog);
564 static void close_assistant (struct import_assistant *, int response);
565 static void on_prepare (GtkAssistant *assistant, GtkWidget *page,
566 struct import_assistant *);
567 static void on_cancel (GtkAssistant *assistant, struct import_assistant *);
568 static void on_close (GtkAssistant *assistant, struct import_assistant *);
569 static void on_paste (GtkButton *button, struct import_assistant *);
570 static void on_reset (GtkButton *button, struct import_assistant *);
571 static void close_assistant (struct import_assistant *, int response);
573 /* Initializes IA's asst substructure. PARENT_WINDOW must be the
574 window to use as the assistant window's parent. */
576 init_assistant (struct import_assistant *ia, GtkWindow *parent_window)
578 struct assistant *a = &ia->asst;
580 a->builder = builder_new ("text-data-import.ui");
581 a->assistant = GTK_ASSISTANT (gtk_assistant_new ());
582 g_signal_connect (a->assistant, "prepare", G_CALLBACK (on_prepare), ia);
583 g_signal_connect (a->assistant, "cancel", G_CALLBACK (on_cancel), ia);
584 g_signal_connect (a->assistant, "close", G_CALLBACK (on_close), ia);
585 a->paste_button = gtk_button_new_from_stock (GTK_STOCK_PASTE);
586 gtk_assistant_add_action_widget (a->assistant, a->paste_button);
587 g_signal_connect (a->paste_button, "clicked", G_CALLBACK (on_paste), ia);
588 a->reset_button = gtk_button_new_from_stock ("pspp-stock-reset");
589 gtk_assistant_add_action_widget (a->assistant, a->reset_button);
590 g_signal_connect (a->reset_button, "clicked", G_CALLBACK (on_reset), ia);
591 gtk_window_set_title (GTK_WINDOW (a->assistant),
592 _("Importing Delimited Text Data"));
593 gtk_window_set_transient_for (GTK_WINDOW (a->assistant), parent_window);
594 gtk_window_set_icon_name (GTK_WINDOW (a->assistant), "psppicon");
596 a->prop_renderer = gtk_cell_renderer_text_new ();
597 g_object_ref_sink (a->prop_renderer);
598 a->fixed_renderer = gtk_cell_renderer_text_new ();
599 g_object_ref_sink (a->fixed_renderer);
600 g_object_set (G_OBJECT (a->fixed_renderer),
601 "family", "Monospace",
605 /* Frees IA's asst substructure. */
607 destroy_assistant (struct import_assistant *ia)
609 struct assistant *a = &ia->asst;
611 g_object_unref (a->prop_renderer);
612 g_object_unref (a->fixed_renderer);
613 g_object_unref (a->builder);
616 /* Appends a page of the given TYPE, with PAGE as its content, to
617 the GtkAssistant encapsulated by IA. Returns the GtkWidget
618 that represents the page. */
620 add_page_to_assistant (struct import_assistant *ia,
621 GtkWidget *page, GtkAssistantPageType type)
627 title = gtk_window_get_title (GTK_WINDOW (page));
628 title_copy = xstrdup (title ? title : "");
630 content = gtk_bin_get_child (GTK_BIN (page));
632 g_object_ref (content);
633 gtk_container_remove (GTK_CONTAINER (page), content);
635 gtk_widget_destroy (page);
637 gtk_assistant_append_page (ia->asst.assistant, content);
638 gtk_assistant_set_page_type (ia->asst.assistant, content, type);
639 gtk_assistant_set_page_title (ia->asst.assistant, content, title_copy);
640 gtk_assistant_set_page_complete (ia->asst.assistant, content, true);
647 /* Called just before PAGE is displayed as the current page of
648 ASSISTANT, this updates IA content according to the new
651 on_prepare (GtkAssistant *assistant, GtkWidget *page,
652 struct import_assistant *ia)
655 if (gtk_assistant_get_page_type (assistant, page)
656 == GTK_ASSISTANT_PAGE_CONFIRM)
657 gtk_widget_grab_focus (assistant->apply);
659 gtk_widget_grab_focus (assistant->forward);
661 if (page == ia->separators.page)
662 prepare_separators_page (ia);
663 else if (page == ia->formats.page)
664 prepare_formats_page (ia);
666 gtk_widget_show (ia->asst.reset_button);
667 if (page == ia->formats.page)
668 gtk_widget_show (ia->asst.paste_button);
670 gtk_widget_hide (ia->asst.paste_button);
673 /* Called when the Cancel button in the assistant is clicked. */
675 on_cancel (GtkAssistant *assistant, struct import_assistant *ia)
677 close_assistant (ia, GTK_RESPONSE_CANCEL);
680 /* Called when the Apply button on the last page of the assistant
683 on_close (GtkAssistant *assistant, struct import_assistant *ia)
685 close_assistant (ia, GTK_RESPONSE_APPLY);
688 /* Called when the Paste button on the last page of the assistant
691 on_paste (GtkButton *button, struct import_assistant *ia)
693 close_assistant (ia, PSPPIRE_RESPONSE_PASTE);
696 /* Called when the Reset button is clicked. */
698 on_reset (GtkButton *button, struct import_assistant *ia)
700 gint page_num = gtk_assistant_get_current_page (ia->asst.assistant);
701 GtkWidget *page = gtk_assistant_get_nth_page (ia->asst.assistant, page_num);
703 if (page == ia->intro.page)
704 reset_intro_page (ia);
705 else if (page == ia->first_line.page)
706 reset_first_line_page (ia);
707 else if (page == ia->separators.page)
708 reset_separators_page (ia);
709 else if (page == ia->formats.page)
710 reset_formats_page (ia);
713 /* Causes the assistant to close, returning RESPONSE for
714 interpretation by text_data_import_assistant. */
716 close_assistant (struct import_assistant *ia, int response)
718 ia->asst.response = response;
719 g_main_loop_quit (ia->asst.main_loop);
720 gtk_widget_hide (GTK_WIDGET (ia->asst.assistant));
723 /* The "intro" page of the assistant. */
725 static void on_intro_amount_changed (GtkToggleButton *button,
726 struct import_assistant *);
728 /* Initializes IA's intro substructure. */
730 init_intro_page (struct import_assistant *ia)
732 GtkBuilder *builder = ia->asst.builder;
733 struct intro_page *p = &ia->intro;
736 p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Intro"),
737 GTK_ASSISTANT_PAGE_INTRO);
738 p->all_cases_button = get_widget_assert (builder, "import-all-cases");
739 p->n_cases_button = get_widget_assert (builder, "import-n-cases");
740 p->n_cases_spin = get_widget_assert (builder, "n-cases-spin");
741 p->percent_button = get_widget_assert (builder, "import-percent");
742 p->percent_spin = get_widget_assert (builder, "percent-spin");
743 g_signal_connect (p->all_cases_button, "toggled",
744 G_CALLBACK (on_intro_amount_changed), ia);
745 g_signal_connect (p->n_cases_button, "toggled",
746 G_CALLBACK (on_intro_amount_changed), ia);
747 g_signal_connect (p->percent_button, "toggled",
748 G_CALLBACK (on_intro_amount_changed), ia);
751 ds_put_cstr (&s, _("This assistant will guide you through the process of "
752 "importing data into PSPP from a text file with one line "
753 "per case, in which fields are separated by tabs, "
754 "commas, or other delimiters.\n\n"));
755 if (ia->file.total_is_exact)
757 &s, ngettext ("The selected file contains %zu line of text. ",
758 "The selected file contains %zu lines of text. ",
761 else if (ia->file.total_lines > 0)
765 "The selected file contains approximately %lu line of text. ",
766 "The selected file contains approximately %lu lines of text. ",
767 ia->file.total_lines),
768 ia->file.total_lines);
771 "Only the first %zu line of the file will be shown for "
772 "preview purposes in the following screens. ",
773 "Only the first %zu lines of the file will be shown for "
774 "preview purposes in the following screens. ",
778 ds_put_cstr (&s, _("You may choose below how much of the file should "
779 "actually be imported."));
780 gtk_label_set_text (GTK_LABEL (get_widget_assert (builder, "intro-label")),
785 /* Resets IA's intro page to its initial state. */
787 reset_intro_page (struct import_assistant *ia)
789 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->intro.all_cases_button),
793 /* Called when one of the radio buttons is clicked. */
795 on_intro_amount_changed (GtkToggleButton *button UNUSED,
796 struct import_assistant *ia)
798 struct intro_page *p = &ia->intro;
800 gtk_widget_set_sensitive (p->n_cases_spin,
801 gtk_toggle_button_get_active (
802 GTK_TOGGLE_BUTTON (p->n_cases_button)));
804 gtk_widget_set_sensitive (ia->intro.percent_spin,
805 gtk_toggle_button_get_active (
806 GTK_TOGGLE_BUTTON (p->percent_button)));
809 /* The "first line" page of the assistant. */
811 static GtkTreeView *create_lines_tree_view (GtkContainer *parent_window,
812 struct import_assistant *);
813 static void on_first_line_change (GtkTreeSelection *,
814 struct import_assistant *);
815 static void on_variable_names_cb_toggle (GtkToggleButton *,
816 struct import_assistant *);
817 static void set_first_line (struct import_assistant *);
818 static void get_first_line (struct import_assistant *);
820 /* Initializes IA's first_line substructure. */
822 init_first_line_page (struct import_assistant *ia)
824 struct first_line_page *p = &ia->first_line;
825 GtkBuilder *builder = ia->asst.builder;
827 p->page = add_page_to_assistant (ia, get_widget_assert (builder, "FirstLine"),
828 GTK_ASSISTANT_PAGE_CONTENT);
829 gtk_widget_destroy (get_widget_assert (builder, "first-line"));
830 p->tree_view = create_lines_tree_view (
831 GTK_CONTAINER (get_widget_assert (builder, "first-line-scroller")), ia);
832 p->variable_names_cb = get_widget_assert (builder, "variable-names");
833 gtk_tree_selection_set_mode (
834 gtk_tree_view_get_selection (GTK_TREE_VIEW (p->tree_view)),
835 GTK_SELECTION_BROWSE);
837 g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (p->tree_view)),
838 "changed", G_CALLBACK (on_first_line_change), ia);
839 g_signal_connect (p->variable_names_cb, "toggled",
840 G_CALLBACK (on_variable_names_cb_toggle), ia);
843 /* Resets the first_line page to its initial content. */
845 reset_first_line_page (struct import_assistant *ia)
847 ia->first_line.skip_lines = 0;
848 ia->first_line.variable_names = false;
852 /* Creates and returns a tree view that contains each of the
853 lines in IA's file as a row. */
855 create_lines_tree_view (GtkContainer *parent, struct import_assistant *ia)
857 GtkTreeView *tree_view;
858 GtkTreeViewColumn *column;
859 size_t max_line_length;
860 gint content_width, header_width;
863 make_tree_view (ia, 0, &tree_view);
865 column = gtk_tree_view_column_new_with_attributes (
866 "Text", ia->asst.fixed_renderer,
867 "text", TEXT_IMPORT_MODEL_COLUMN_LINE,
869 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
872 for (i = 0; i < ia->file.line_cnt; i++)
874 size_t w = ds_length (&ia->file.lines[i]);
875 max_line_length = MAX (max_line_length, w);
878 content_width = get_monospace_width (tree_view, ia->asst.fixed_renderer,
880 header_width = get_string_width (tree_view, ia->asst.prop_renderer, "Text");
881 gtk_tree_view_column_set_fixed_width (column, MAX (content_width,
883 gtk_tree_view_append_column (tree_view, column);
885 gtk_tree_view_set_fixed_height_mode (tree_view, true);
887 gtk_container_add (parent, GTK_WIDGET (tree_view));
888 gtk_widget_show (GTK_WIDGET (tree_view));
893 /* Called when the line selected in the first_line tree view
896 on_first_line_change (GtkTreeSelection *selection UNUSED,
897 struct import_assistant *ia)
902 /* Called when the checkbox that indicates whether variable
903 names are in the row above the first line is toggled. */
905 on_variable_names_cb_toggle (GtkToggleButton *variable_names_cb UNUSED,
906 struct import_assistant *ia)
911 /* Sets the widgets to match IA's first_line substructure. */
913 set_first_line (struct import_assistant *ia)
917 path = gtk_tree_path_new_from_indices (ia->first_line.skip_lines, -1);
918 gtk_tree_view_set_cursor (GTK_TREE_VIEW (ia->first_line.tree_view),
920 gtk_tree_path_free (path);
922 gtk_toggle_button_set_active (
923 GTK_TOGGLE_BUTTON (ia->first_line.variable_names_cb),
924 ia->first_line.variable_names);
925 gtk_widget_set_sensitive (ia->first_line.variable_names_cb,
926 ia->first_line.skip_lines > 0);
929 /* Sets IA's first_line substructure to match the widgets. */
931 get_first_line (struct import_assistant *ia)
933 GtkTreeSelection *selection;
937 selection = gtk_tree_view_get_selection (ia->first_line.tree_view);
938 if (gtk_tree_selection_get_selected (selection, &model, &iter))
940 GtkTreePath *path = gtk_tree_model_get_path (model, &iter);
941 int row = gtk_tree_path_get_indices (path)[0];
942 gtk_tree_path_free (path);
944 ia->first_line.skip_lines = row;
945 ia->first_line.variable_names =
946 (ia->first_line.skip_lines > 0
947 && gtk_toggle_button_get_active (
948 GTK_TOGGLE_BUTTON (ia->first_line.variable_names_cb)));
950 gtk_widget_set_sensitive (ia->first_line.variable_names_cb,
951 ia->first_line.skip_lines > 0);
954 /* The "separators" page of the assistant. */
956 static void revise_fields_preview (struct import_assistant *ia);
957 static void choose_likely_separators (struct import_assistant *ia);
958 static void find_commonest_chars (unsigned long int histogram[UCHAR_MAX + 1],
959 const char *targets, const char *def,
960 struct string *result);
961 static void clear_fields (struct import_assistant *ia);
962 static void revise_fields_preview (struct import_assistant *);
963 static void set_separators (struct import_assistant *);
964 static void get_separators (struct import_assistant *);
965 static void on_separators_custom_entry_notify (GObject *UNUSED,
967 struct import_assistant *);
968 static void on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
969 struct import_assistant *);
970 static void on_quote_combo_change (GtkComboBox *combo,
971 struct import_assistant *);
972 static void on_quote_cb_toggle (GtkToggleButton *quote_cb,
973 struct import_assistant *);
974 static void on_separator_toggle (GtkToggleButton *, struct import_assistant *);
975 static void render_input_cell (GtkTreeViewColumn *tree_column,
976 GtkCellRenderer *cell,
977 GtkTreeModel *model, GtkTreeIter *iter,
979 static gboolean on_query_input_tooltip (GtkWidget *widget, gint wx, gint wy,
980 gboolean keyboard_mode UNUSED,
982 struct import_assistant *);
984 /* A common field separator and its identifying name. */
987 const char *name; /* Name (for use with get_widget_assert). */
988 int c; /* Separator character. */
991 /* All the separators in the dialog box. */
992 static const struct separator separators[] =
1004 #define SEPARATOR_CNT (sizeof separators / sizeof *separators)
1007 set_quote_list (GtkComboBoxEntry *cb)
1009 GtkListStore *list = gtk_list_store_new (1, G_TYPE_STRING);
1012 const gchar *seperator[3] = {"'\"", "\'", "\""};
1014 for (i = 0; i < 3; i++)
1016 const gchar *s = seperator[i];
1018 /* Add a new row to the model */
1019 gtk_list_store_append (list, &iter);
1020 gtk_list_store_set (list, &iter,
1026 gtk_combo_box_set_model (GTK_COMBO_BOX (cb), GTK_TREE_MODEL (list));
1028 gtk_combo_box_entry_set_text_column (cb, 0);
1031 /* Initializes IA's separators substructure. */
1033 init_separators_page (struct import_assistant *ia)
1035 GtkBuilder *builder = ia->asst.builder;
1036 struct separators_page *p = &ia->separators;
1039 choose_likely_separators (ia);
1041 p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Separators"),
1042 GTK_ASSISTANT_PAGE_CONTENT);
1043 p->custom_cb = get_widget_assert (builder, "custom-cb");
1044 p->custom_entry = get_widget_assert (builder, "custom-entry");
1045 p->quote_combo = get_widget_assert (builder, "quote-combo");
1046 p->quote_entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (p->quote_combo)));
1047 p->quote_cb = get_widget_assert (builder, "quote-cb");
1048 p->escape_cb = get_widget_assert (builder, "escape");
1050 set_separators (ia);
1051 set_quote_list (GTK_COMBO_BOX_ENTRY (p->quote_combo));
1052 p->fields_tree_view = GTK_TREE_VIEW (get_widget_assert (builder, "fields"));
1053 g_signal_connect (p->quote_combo, "changed",
1054 G_CALLBACK (on_quote_combo_change), ia);
1055 g_signal_connect (p->quote_cb, "toggled",
1056 G_CALLBACK (on_quote_cb_toggle), ia);
1057 g_signal_connect (p->custom_entry, "notify::text",
1058 G_CALLBACK (on_separators_custom_entry_notify), ia);
1059 g_signal_connect (p->custom_cb, "toggled",
1060 G_CALLBACK (on_separators_custom_cb_toggle), ia);
1061 for (i = 0; i < SEPARATOR_CNT; i++)
1062 g_signal_connect (get_widget_assert (builder, separators[i].name),
1063 "toggled", G_CALLBACK (on_separator_toggle), ia);
1064 g_signal_connect (p->escape_cb, "toggled",
1065 G_CALLBACK (on_separator_toggle), ia);
1068 /* Frees IA's separators substructure. */
1070 destroy_separators_page (struct import_assistant *ia)
1072 struct separators_page *s = &ia->separators;
1074 ds_destroy (&s->separators);
1075 ds_destroy (&s->quotes);
1079 /* Called just before the separators page becomes visible in the
1082 prepare_separators_page (struct import_assistant *ia)
1084 revise_fields_preview (ia);
1087 /* Called when the Reset button is clicked on the separators
1088 page, resets the separators to the defaults. */
1090 reset_separators_page (struct import_assistant *ia)
1092 choose_likely_separators (ia);
1093 set_separators (ia);
1096 /* Frees and clears the column data in IA's separators
1099 clear_fields (struct import_assistant *ia)
1101 struct separators_page *s = &ia->separators;
1103 if (s->column_cnt > 0)
1108 for (row = 0; row < ia->file.line_cnt; row++)
1110 const struct string *line = &ia->file.lines[row];
1111 const char *line_start = ds_data (line);
1112 const char *line_end = ds_end (line);
1114 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1116 char *s = ss_data (col->contents[row]);
1117 if (!(s >= line_start && s <= line_end))
1118 ss_dealloc (&col->contents[row]);
1122 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1125 free (col->contents);
1134 /* Breaks the file data in IA into columns based on the
1135 separators set in IA's separators substructure. */
1137 split_fields (struct import_assistant *ia)
1139 struct separators_page *s = &ia->separators;
1140 size_t columns_allocated;
1146 /* Is space in the set of separators? */
1147 space_sep = ss_find_char (ds_ss (&s->separators), ' ') != SIZE_MAX;
1149 /* Split all the lines, not just those from
1150 ia->first_line.skip_lines on, so that we split the line that
1151 contains variables names if ia->first_line.variable_names is
1153 columns_allocated = 0;
1154 for (row = 0; row < ia->file.line_cnt; row++)
1156 struct string *line = &ia->file.lines[row];
1157 struct substring text = ds_ss (line);
1160 for (column_idx = 0; ; column_idx++)
1162 struct substring field;
1163 struct column *column;
1166 ss_ltrim (&text, ss_cstr (" "));
1167 if (ss_is_empty (text))
1169 if (column_idx != 0)
1173 else if (!ds_is_empty (&s->quotes)
1174 && ds_find_char (&s->quotes, text.string[0]) != SIZE_MAX)
1176 int quote = ss_get_char (&text);
1178 ss_get_until (&text, quote, &field);
1185 while ((c = ss_get_char (&text)) != EOF)
1187 ds_put_char (&s, c);
1188 else if (ss_match_char (&text, quote))
1189 ds_put_char (&s, quote);
1196 ss_get_chars (&text, ss_cspan (text, ds_ss (&s->separators)),
1199 if (column_idx >= s->column_cnt)
1201 struct column *column;
1203 if (s->column_cnt >= columns_allocated)
1204 s->columns = x2nrealloc (s->columns, &columns_allocated,
1205 sizeof *s->columns);
1206 column = &s->columns[s->column_cnt++];
1207 column->name = NULL;
1209 column->contents = xcalloc (ia->file.line_cnt,
1210 sizeof *column->contents);
1212 column = &s->columns[column_idx];
1213 column->contents[row] = field;
1214 if (ss_length (field) > column->width)
1215 column->width = ss_length (field);
1218 ss_ltrim (&text, ss_cstr (" "));
1219 if (ss_is_empty (text))
1221 if (ss_find_char (ds_ss (&s->separators), ss_first (text))
1223 ss_advance (&text, 1);
1228 /* Chooses a name for each column on the separators page */
1230 choose_column_names (struct import_assistant *ia)
1232 const struct first_line_page *f = &ia->first_line;
1233 struct separators_page *s = &ia->separators;
1234 struct dictionary *dict;
1235 unsigned long int generated_name_count = 0;
1239 dict = dict_create ();
1240 name_row = f->variable_names && f->skip_lines ? f->skip_lines : 0;
1241 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1243 char name[VAR_NAME_LEN + 1];
1246 hint = name_row ? ss_xstrdup (col->contents[name_row - 1]) : NULL;
1247 if (!dict_make_unique_var_name (dict, hint, &generated_name_count, name))
1251 col->name = xstrdup (name);
1252 dict_create_var_assert (dict, name, 0);
1254 dict_destroy (dict);
1257 /* Picks the most likely separator and quote characters based on
1260 choose_likely_separators (struct import_assistant *ia)
1262 unsigned long int histogram[UCHAR_MAX + 1] = { 0 };
1265 /* Construct a histogram of all the characters used in the
1267 for (row = 0; row < ia->file.line_cnt; row++)
1269 struct substring line = ds_ss (&ia->file.lines[row]);
1270 size_t length = ss_length (line);
1272 for (i = 0; i < length; i++)
1273 histogram[(unsigned char) line.string[i]]++;
1276 find_commonest_chars (histogram, "\"'", "", &ia->separators.quotes);
1277 find_commonest_chars (histogram, ",;:/|!\t-", ",",
1278 &ia->separators.separators);
1279 ia->separators.escape = true;
1282 /* Chooses the most common character among those in TARGETS,
1283 based on the frequency data in HISTOGRAM, and stores it in
1284 RESULT. If there is a tie for the most common character among
1285 those in TARGETS, the earliest character is chosen. If none
1286 of the TARGETS appear at all, then DEF is used as a
1289 find_commonest_chars (unsigned long int histogram[UCHAR_MAX + 1],
1290 const char *targets, const char *def,
1291 struct string *result)
1293 unsigned char max = 0;
1294 unsigned long int max_count = 0;
1296 for (; *targets != '\0'; targets++)
1298 unsigned char c = *targets;
1299 unsigned long int count = histogram[c];
1300 if (count > max_count)
1309 ds_put_char (result, max);
1312 ds_assign_cstr (result, def);
1315 /* Revises the contents of the fields tree view based on the
1316 currently chosen set of separators. */
1318 revise_fields_preview (struct import_assistant *ia)
1322 push_watch_cursor (ia);
1324 w = GTK_WIDGET (ia->separators.fields_tree_view);
1325 gtk_widget_destroy (w);
1326 get_separators (ia);
1328 choose_column_names (ia);
1329 ia->separators.fields_tree_view = create_data_tree_view (
1331 GTK_CONTAINER (get_widget_assert (ia->asst.builder, "fields-scroller")),
1334 pop_watch_cursor (ia);
1337 /* Sets the widgets to match IA's separators substructure. */
1339 set_separators (struct import_assistant *ia)
1341 struct separators_page *s = &ia->separators;
1343 struct string custom;
1348 ds_init_empty (&custom);
1350 for (i = 0; i < ds_length (&s->separators); i++)
1352 unsigned char c = ds_at (&s->separators, i);
1355 for (j = 0; j < SEPARATOR_CNT; j++)
1357 const struct separator *s = &separators[j];
1365 ds_put_char (&custom, c);
1369 for (i = 0; i < SEPARATOR_CNT; i++)
1371 const struct separator *s = &separators[i];
1372 GtkWidget *button = get_widget_assert (ia->asst.builder, s->name);
1373 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
1374 (seps & (1u << i)) != 0);
1376 any_custom = !ds_is_empty (&custom);
1377 gtk_entry_set_text (GTK_ENTRY (s->custom_entry), ds_cstr (&custom));
1378 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->custom_cb),
1380 gtk_widget_set_sensitive (s->custom_entry, any_custom);
1381 ds_destroy (&custom);
1383 any_quotes = !ds_is_empty (&s->quotes);
1385 gtk_entry_set_text (s->quote_entry,
1386 any_quotes ? ds_cstr (&s->quotes) : "\"");
1387 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->quote_cb),
1389 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->escape_cb),
1391 gtk_widget_set_sensitive (s->quote_combo, any_quotes);
1392 gtk_widget_set_sensitive (s->escape_cb, any_quotes);
1395 /* Sets IA's separators substructure to match the widgets. */
1397 get_separators (struct import_assistant *ia)
1399 struct separators_page *s = &ia->separators;
1402 ds_clear (&s->separators);
1403 for (i = 0; i < SEPARATOR_CNT; i++)
1405 const struct separator *sep = &separators[i];
1406 GtkWidget *button = get_widget_assert (ia->asst.builder, sep->name);
1407 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
1408 ds_put_char (&s->separators, sep->c);
1411 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->custom_cb)))
1412 ds_put_cstr (&s->separators,
1413 gtk_entry_get_text (GTK_ENTRY (s->custom_entry)));
1415 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->quote_cb)))
1417 gchar *text = gtk_combo_box_get_active_text (
1418 GTK_COMBO_BOX (s->quote_combo));
1419 ds_assign_cstr (&s->quotes, text);
1423 ds_clear (&s->quotes);
1424 s->escape = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->escape_cb));
1427 /* Called when the user changes the entry field for custom
1430 on_separators_custom_entry_notify (GObject *gobject UNUSED,
1431 GParamSpec *arg1 UNUSED,
1432 struct import_assistant *ia)
1434 revise_fields_preview (ia);
1437 /* Called when the user toggles the checkbox that enables custom
1440 on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
1441 struct import_assistant *ia)
1443 bool is_active = gtk_toggle_button_get_active (custom_cb);
1444 gtk_widget_set_sensitive (ia->separators.custom_entry, is_active);
1445 revise_fields_preview (ia);
1448 /* Called when the user changes the selection in the combo box
1449 that selects a quote character. */
1451 on_quote_combo_change (GtkComboBox *combo, struct import_assistant *ia)
1453 revise_fields_preview (ia);
1456 /* Called when the user toggles the checkbox that enables
1459 on_quote_cb_toggle (GtkToggleButton *quote_cb, struct import_assistant *ia)
1461 bool is_active = gtk_toggle_button_get_active (quote_cb);
1462 gtk_widget_set_sensitive (ia->separators.quote_combo, is_active);
1463 gtk_widget_set_sensitive (ia->separators.escape_cb, is_active);
1464 revise_fields_preview (ia);
1467 /* Called when the user toggles one of the separators
1470 on_separator_toggle (GtkToggleButton *toggle UNUSED,
1471 struct import_assistant *ia)
1473 revise_fields_preview (ia);
1476 /* Called to render one of the cells in the fields preview tree
1479 render_input_cell (GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1480 GtkTreeModel *model, GtkTreeIter *iter,
1483 struct import_assistant *ia = ia_;
1484 struct substring field;
1488 column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1490 row = text_import_model_iter_to_row (iter) + ia->first_line.skip_lines;
1491 field = ia->separators.columns[column].contents[row];
1492 if (field.string != NULL)
1494 GValue text = {0, };
1495 g_value_init (&text, G_TYPE_STRING);
1496 g_value_take_string (&text, ss_xstrdup (field));
1497 g_object_set_property (G_OBJECT (cell), "text", &text);
1498 g_value_unset (&text);
1499 g_object_set (cell, "background-set", FALSE, (void *) NULL);
1504 "background", "red",
1505 "background-set", TRUE,
1509 /* Called to render a tooltip on one of the cells in the fields
1510 preview tree view. */
1512 on_query_input_tooltip (GtkWidget *widget, gint wx, gint wy,
1513 gboolean keyboard_mode UNUSED,
1514 GtkTooltip *tooltip, struct import_assistant *ia)
1518 if (!get_tooltip_location (widget, wx, wy, ia, &row, &column))
1521 if (ia->separators.columns[column].contents[row].string != NULL)
1524 gtk_tooltip_set_text (tooltip,
1525 _("This input line has too few separators "
1526 "to fill in this field."));
1530 /* The "formats" page of the assistant. */
1532 static void on_variable_change (PsppireDict *dict, int idx,
1533 struct import_assistant *);
1534 static void clear_modified_vars (struct import_assistant *);
1536 /* Initializes IA's formats substructure. */
1538 init_formats_page (struct import_assistant *ia)
1540 GtkBuilder *builder = ia->asst.builder;
1541 struct formats_page *p = &ia->formats;
1543 p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Formats"),
1544 GTK_ASSISTANT_PAGE_CONFIRM);
1545 p->data_tree_view = GTK_TREE_VIEW (get_widget_assert (builder, "data"));
1546 p->modified_vars = NULL;
1547 p->modified_var_cnt = 0;
1550 /* Frees IA's formats substructure. */
1552 destroy_formats_page (struct import_assistant *ia)
1554 struct formats_page *p = &ia->formats;
1556 if (p->psppire_dict != NULL)
1558 /* This destroys p->dict also. */
1559 g_object_unref (p->psppire_dict);
1561 clear_modified_vars (ia);
1564 /* Called just before the formats page of the assistant is
1567 prepare_formats_page (struct import_assistant *ia)
1569 struct dictionary *dict;
1570 PsppireDict *psppire_dict;
1571 PsppireVarStore *var_store;
1572 GtkBin *vars_scroller;
1573 GtkWidget *old_var_sheet;
1574 PsppireVarSheet *var_sheet;
1575 struct separators_page *s = &ia->separators;
1576 struct formats_page *p = &ia->formats;
1577 struct fmt_guesser *fg;
1578 unsigned long int number = 0;
1581 push_watch_cursor (ia);
1583 dict = dict_create ();
1584 fg = fmt_guesser_create ();
1585 for (column_idx = 0; column_idx < s->column_cnt; column_idx++)
1587 struct variable *modified_var;
1588 char name[VAR_NAME_LEN + 1];
1590 modified_var = (column_idx < p->modified_var_cnt
1591 ? p->modified_vars[column_idx] : NULL);
1592 if (modified_var == NULL)
1594 struct column *column = &s->columns[column_idx];
1595 struct variable *var;
1596 struct fmt_spec format;
1599 /* Choose variable name. */
1600 if (!dict_make_unique_var_name (dict, column->name, &number, name))
1603 /* Choose variable format. */
1604 fmt_guesser_clear (fg);
1605 for (row = ia->first_line.skip_lines; row < ia->file.line_cnt; row++)
1606 fmt_guesser_add (fg, column->contents[row]);
1607 fmt_guesser_guess (fg, &format);
1608 fmt_fix_input (&format);
1610 /* Create variable. */
1611 var = dict_create_var_assert (dict, name, fmt_var_width (&format));
1612 var_set_both_formats (var, &format);
1616 if (!dict_make_unique_var_name (dict, var_get_name (modified_var),
1619 dict_clone_var_assert (dict, modified_var, name);
1622 fmt_guesser_destroy (fg);
1624 psppire_dict = psppire_dict_new_from_dict (dict);
1625 g_signal_connect (psppire_dict, "variable_changed",
1626 G_CALLBACK (on_variable_change), ia);
1627 ia->formats.dict = dict;
1628 ia->formats.psppire_dict = psppire_dict;
1630 /* XXX: PsppireVarStore doesn't hold a reference to
1631 psppire_dict for now, but it should. After it does, we
1632 should g_object_ref the psppire_dict here, since we also
1633 hold a reference via ia->formats.dict. */
1634 var_store = psppire_var_store_new (psppire_dict);
1635 g_object_set (var_store,
1636 "format-type", PSPPIRE_VAR_STORE_INPUT_FORMATS,
1638 var_sheet = PSPPIRE_VAR_SHEET (psppire_var_sheet_new ());
1639 g_object_set (var_sheet,
1641 "may-create-vars", FALSE,
1644 vars_scroller = GTK_BIN (get_widget_assert (ia->asst.builder, "vars-scroller"));
1645 old_var_sheet = gtk_bin_get_child (vars_scroller);
1646 if (old_var_sheet != NULL)
1647 gtk_widget_destroy (old_var_sheet);
1648 gtk_container_add (GTK_CONTAINER (vars_scroller), GTK_WIDGET (var_sheet));
1649 gtk_widget_show (GTK_WIDGET (var_sheet));
1651 gtk_widget_destroy (GTK_WIDGET (ia->formats.data_tree_view));
1652 ia->formats.data_tree_view = create_data_tree_view (
1654 GTK_CONTAINER (get_widget_assert (ia->asst.builder, "data-scroller")),
1657 pop_watch_cursor (ia);
1660 /* Clears the set of user-modified variables from IA's formats
1661 substructure. This discards user modifications to variable
1662 formats, thereby causing formats to revert to their
1665 clear_modified_vars (struct import_assistant *ia)
1667 struct formats_page *p = &ia->formats;
1670 for (i = 0; i < p->modified_var_cnt; i++)
1671 var_destroy (p->modified_vars[i]);
1672 free (p->modified_vars);
1673 p->modified_vars = NULL;
1674 p->modified_var_cnt = 0;
1677 /* Resets the formats page to its defaults, discarding user
1680 reset_formats_page (struct import_assistant *ia)
1682 clear_modified_vars (ia);
1683 prepare_formats_page (ia);
1686 /* Called when the user changes one of the variables in the
1689 on_variable_change (PsppireDict *dict, int dict_idx,
1690 struct import_assistant *ia)
1692 struct formats_page *p = &ia->formats;
1693 GtkTreeView *tv = ia->formats.data_tree_view;
1694 gint column_idx = dict_idx + 1;
1696 push_watch_cursor (ia);
1698 /* Remove previous column and replace with new column. */
1699 gtk_tree_view_remove_column (tv, gtk_tree_view_get_column (tv, column_idx));
1700 gtk_tree_view_insert_column (tv, make_data_column (ia, tv, false, dict_idx),
1703 /* Save a copy of the modified variable in modified_vars, so
1704 that its attributes will be preserved if we back up to the
1705 previous page with the Prev button and then come back
1707 if (dict_idx >= p->modified_var_cnt)
1710 p->modified_vars = xnrealloc (p->modified_vars, dict_idx + 1,
1711 sizeof *p->modified_vars);
1712 for (i = 0; i <= dict_idx; i++)
1713 p->modified_vars[i] = NULL;
1714 p->modified_var_cnt = dict_idx + 1;
1716 if (p->modified_vars[dict_idx])
1717 var_destroy (p->modified_vars[dict_idx]);
1718 p->modified_vars[dict_idx]
1719 = var_clone (psppire_dict_get_variable (dict, dict_idx));
1721 pop_watch_cursor (ia);
1724 /* Parses the contents of the field at (ROW,COLUMN) according to
1725 its variable format. If OUTPUTP is non-null, then *OUTPUTP
1726 receives the formatted output for that field (which must be
1727 freed with free). If TOOLTIPP is non-null, then *TOOLTIPP
1728 receives a message suitable for use in a tooltip, if one is
1729 needed, or a null pointer otherwise. Returns true if a
1730 tooltip message is needed, otherwise false. */
1732 parse_field (struct import_assistant *ia,
1733 size_t row, size_t column,
1734 char **outputp, char **tooltipp)
1736 struct substring field;
1738 struct variable *var;
1739 const struct fmt_spec *in;
1740 struct fmt_spec out;
1744 field = ia->separators.columns[column].contents[row];
1745 var = dict_get_var (ia->formats.dict, column);
1746 value_init (&val, var_get_width (var));
1747 in = var_get_print_format (var);
1748 out = fmt_for_output_from_input (in);
1750 if (field.string != NULL)
1753 if (!data_in (field, LEGACY_NATIVE, in->type, 0, 0, 0,
1754 &val, var_get_width (var)))
1756 char fmt_string[FMT_STRING_LEN_MAX + 1];
1757 fmt_to_string (in, fmt_string);
1758 tooltip = xasprintf (_("Field content \"%.*s\" cannot be parsed in "
1760 (int) field.length, field.string,
1767 tooltip = xstrdup (_("This input line has too few separators "
1768 "to fill in this field."));
1769 value_set_missing (&val, var_get_width (var));
1771 if (outputp != NULL)
1773 char *output = xmalloc (out.w + 1);
1774 data_out (&val, &out, output);
1775 output[out.w] = '\0';
1778 value_destroy (&val, var_get_width (var));
1780 ok = tooltip == NULL;
1781 if (tooltipp != NULL)
1782 *tooltipp = tooltip;
1788 /* Called to render one of the cells in the data preview tree
1791 render_output_cell (GtkTreeViewColumn *tree_column,
1792 GtkCellRenderer *cell,
1793 GtkTreeModel *model,
1797 struct import_assistant *ia = ia_;
1799 GValue gvalue = { 0, };
1802 ok = parse_field (ia,
1803 (text_import_model_iter_to_row (iter)
1804 + ia->first_line.skip_lines),
1805 GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1809 g_value_init (&gvalue, G_TYPE_STRING);
1810 g_value_take_string (&gvalue, output);
1811 g_object_set_property (G_OBJECT (cell), "text", &gvalue);
1812 g_value_unset (&gvalue);
1815 g_object_set (cell, "background-set", FALSE, (void *) NULL);
1818 "background", "red",
1819 "background-set", TRUE,
1823 /* Called to render a tooltip for one of the cells in the data
1824 preview tree view. */
1826 on_query_output_tooltip (GtkWidget *widget, gint wx, gint wy,
1827 gboolean keyboard_mode UNUSED,
1828 GtkTooltip *tooltip, struct import_assistant *ia)
1833 if (!get_tooltip_location (widget, wx, wy, ia, &row, &column))
1836 if (parse_field (ia, row, column, NULL, &text))
1839 gtk_tooltip_set_text (tooltip, text);
1844 /* Utility functions used by multiple pages of the assistant. */
1847 get_tooltip_location (GtkWidget *widget, gint wx, gint wy,
1848 const struct import_assistant *ia,
1849 size_t *row, size_t *column)
1851 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
1855 GtkTreeViewColumn *tree_column;
1856 GtkTreeModel *tree_model;
1859 /* Check that WIDGET is really visible on the screen before we
1860 do anything else. This is a bug fix for a sticky situation:
1861 when text_data_import_assistant() returns, it frees the data
1862 necessary to compose the tool tip message, but there may be
1863 a tool tip under preparation at that point (even if there is
1864 no visible tool tip) that will call back into us a little
1865 bit later. Perhaps the correct solution to this problem is
1866 to make the data related to the tool tips part of a GObject
1867 that only gets destroyed when all references are released,
1868 but this solution appears to be effective too. */
1869 if (!GTK_WIDGET_MAPPED (widget))
1872 gtk_tree_view_convert_widget_to_bin_window_coords (tree_view,
1874 if (!gtk_tree_view_get_path_at_pos (tree_view, bx, by,
1875 &path, &tree_column, NULL, NULL))
1878 *column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1881 tree_model = gtk_tree_view_get_model (tree_view);
1882 ok = gtk_tree_model_get_iter (tree_model, &iter, path);
1883 gtk_tree_path_free (path);
1887 *row = text_import_model_iter_to_row (&iter) + ia->first_line.skip_lines;
1892 make_tree_view (const struct import_assistant *ia,
1894 GtkTreeView **tree_view)
1896 GtkTreeModel *model;
1898 *tree_view = GTK_TREE_VIEW (gtk_tree_view_new ());
1899 model = GTK_TREE_MODEL (text_import_model_new (
1900 ia->file.lines + first_line,
1901 ia->file.line_cnt - first_line, first_line));
1902 gtk_tree_view_set_model (*tree_view, model);
1904 add_line_number_column (ia, *tree_view);
1908 add_line_number_column (const struct import_assistant *ia,
1909 GtkTreeView *treeview)
1911 GtkTreeViewColumn *column;
1913 column = gtk_tree_view_column_new_with_attributes (
1914 "Line", ia->asst.prop_renderer,
1915 "text", TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER,
1917 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
1918 gtk_tree_view_column_set_fixed_width (
1919 column, get_monospace_width (treeview, ia->asst.prop_renderer, 5));
1920 gtk_tree_view_append_column (treeview, column);
1924 get_monospace_width (GtkTreeView *treeview, GtkCellRenderer *renderer,
1931 ds_put_char_multiple (&s, '0', char_cnt);
1932 ds_put_char (&s, ' ');
1933 width = get_string_width (treeview, renderer, ds_cstr (&s));
1940 get_string_width (GtkTreeView *treeview, GtkCellRenderer *renderer,
1944 g_object_set (G_OBJECT (renderer), "text", string, (void *) NULL);
1945 gtk_cell_renderer_get_size (renderer, GTK_WIDGET (treeview),
1946 NULL, NULL, NULL, &width, NULL);
1950 static GtkTreeViewColumn *
1951 make_data_column (struct import_assistant *ia, GtkTreeView *tree_view,
1952 bool input, gint dict_idx)
1954 struct variable *var = NULL;
1955 struct column *column = NULL;
1956 char name[(VAR_NAME_LEN * 2) + 1];
1958 gint content_width, header_width;
1959 GtkTreeViewColumn *tree_column;
1962 column = &ia->separators.columns[dict_idx];
1964 var = dict_get_var (ia->formats.dict, dict_idx);
1966 escape_underscores (input ? column->name : var_get_name (var), name);
1967 char_cnt = input ? column->width : var_get_print_format (var)->w;
1968 content_width = get_monospace_width (tree_view, ia->asst.fixed_renderer,
1970 header_width = get_string_width (tree_view, ia->asst.prop_renderer,
1973 tree_column = gtk_tree_view_column_new ();
1974 g_object_set_data (G_OBJECT (tree_column), "column-number",
1975 GINT_TO_POINTER (dict_idx));
1976 gtk_tree_view_column_set_title (tree_column, name);
1977 gtk_tree_view_column_pack_start (tree_column, ia->asst.fixed_renderer,
1979 gtk_tree_view_column_set_cell_data_func (
1980 tree_column, ia->asst.fixed_renderer,
1981 input ? render_input_cell : render_output_cell, ia, NULL);
1982 gtk_tree_view_column_set_sizing (tree_column, GTK_TREE_VIEW_COLUMN_FIXED);
1983 gtk_tree_view_column_set_fixed_width (tree_column, MAX (content_width,
1989 static GtkTreeView *
1990 create_data_tree_view (bool input, GtkContainer *parent,
1991 struct import_assistant *ia)
1993 GtkTreeView *tree_view;
1996 make_tree_view (ia, ia->first_line.skip_lines, &tree_view);
1997 gtk_tree_selection_set_mode (gtk_tree_view_get_selection (tree_view),
1998 GTK_SELECTION_NONE);
2000 for (i = 0; i < ia->separators.column_cnt; i++)
2001 gtk_tree_view_append_column (tree_view,
2002 make_data_column (ia, tree_view, input, i));
2004 g_object_set (G_OBJECT (tree_view), "has-tooltip", TRUE, (void *) NULL);
2005 g_signal_connect (tree_view, "query-tooltip",
2006 G_CALLBACK (input ? on_query_input_tooltip
2007 : on_query_output_tooltip), ia);
2008 gtk_tree_view_set_fixed_height_mode (tree_view, true);
2010 gtk_container_add (parent, GTK_WIDGET (tree_view));
2011 gtk_widget_show (GTK_WIDGET (tree_view));
2017 escape_underscores (const char *in, char *out)
2019 for (; *in != '\0'; in++)
2028 /* TextImportModel, a GtkTreeModel implementation used by some
2029 pages of the assistant. */
2031 #define G_TYPE_TEXT_IMPORT_MODEL (text_import_model_get_type ())
2032 #define TEXT_IMPORT_MODEL(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), G_TYPE_TEXT_IMPORT_MODEL, TextImportModel))
2033 #define TEXT_IMPORT_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), G_TYPE_TEXT_IMPORT_MODEL, TextImportModelClass))
2034 #define IS_TEXT_IMPORT_MODEL(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), G_TYPE_TEXT_IMPORT_MODEL))
2035 #define IS_TEXT_IMPORT_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), G_TYPE_TEXT_IMPORT_MODEL))
2036 #define TEXT_IMPORT_MODEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), G_TYPE_TEXT_IMPORT_MODEL, TextImportModelClass))
2038 /* Random number used in 'stamp' member of GtkTreeIter. */
2039 #define TREE_MODEL_STAMP 0x7efd67d3
2041 struct TextImportModel
2044 struct string *lines;
2049 struct TextImportModelClass
2051 GObjectClass parent_class;
2054 GType text_import_model_get_type (void);
2055 static void text_import_model_tree_model_init (gpointer iface, gpointer data);
2058 text_import_model_get_type (void)
2060 static GType object_type = 0;
2064 static const GTypeInfo object_info = {
2065 sizeof (TextImportModelClass),
2066 (GBaseInitFunc) NULL,
2067 (GBaseFinalizeFunc) NULL,
2068 NULL, /* class_init */
2069 NULL, /* class_finalize */
2070 NULL, /* class_data */
2071 sizeof (TextImportModel),
2072 0, /* n_preallocs */
2073 NULL, /* instance_init */
2076 static const GInterfaceInfo tree_model_info = {
2077 text_import_model_tree_model_init,
2082 object_type = g_type_register_static (G_TYPE_OBJECT,
2086 g_type_add_interface_static (object_type, GTK_TYPE_TREE_MODEL,
2096 /* Creates and returns a new TextImportModel that contains the
2097 LINE_CNT lines in LINES. The lines before FIRST_LINE in LINES
2098 are not part of the model, but they are included in the line
2099 numbers in the TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER column.
2101 The caller retains responsibility for freeing LINES and must
2102 ensure that its lifetime and that of the strings that it
2103 contains exceeds that of the TextImportModel. */
2105 text_import_model_new (struct string *lines, size_t line_cnt,
2108 TextImportModel *new_text_import_model
2109 = g_object_new (G_TYPE_TEXT_IMPORT_MODEL, NULL);
2110 new_text_import_model->lines = lines;
2111 new_text_import_model->line_cnt = line_cnt;
2112 new_text_import_model->first_line = first_line;
2113 return new_text_import_model;
2118 tree_model_iter_has_child (GtkTreeModel *tree_model,
2125 tree_model_iter_parent (GtkTreeModel *tree_model,
2132 static GtkTreeModelFlags
2133 tree_model_get_flags (GtkTreeModel *model)
2135 g_return_val_if_fail (IS_TEXT_IMPORT_MODEL (model), (GtkTreeModelFlags) 0);
2137 return GTK_TREE_MODEL_LIST_ONLY | GTK_TREE_MODEL_ITERS_PERSIST;
2142 tree_model_n_columns (GtkTreeModel *model)
2148 tree_model_column_type (GtkTreeModel *model, gint index)
2150 return (index == TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER ? G_TYPE_INT
2151 : index == TEXT_IMPORT_MODEL_COLUMN_LINE ? G_TYPE_STRING
2156 init_iter (TextImportModel *list, gint idx, GtkTreeIter *iter)
2158 if (idx < 0 || idx >= list->line_cnt)
2161 iter->user_data = GINT_TO_POINTER (-1);
2166 iter->stamp = TREE_MODEL_STAMP;
2167 iter->user_data = GINT_TO_POINTER (idx);
2173 tree_model_get_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreePath *path)
2175 gint *indices, depth;
2177 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2179 g_return_val_if_fail (path, FALSE);
2181 indices = gtk_tree_path_get_indices (path);
2182 depth = gtk_tree_path_get_depth (path);
2184 g_return_val_if_fail (depth == 1, FALSE);
2186 return init_iter (list, indices[0], iter);
2191 tree_model_iter_next (GtkTreeModel *model, GtkTreeIter *iter)
2193 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2196 assert (iter->stamp == TREE_MODEL_STAMP);
2198 idx = GPOINTER_TO_INT (iter->user_data);
2199 return init_iter (list, idx == -1 ? -1 : idx + 1, iter);
2202 static GtkTreePath *
2203 tree_model_get_path (GtkTreeModel *model, GtkTreeIter *iter)
2207 g_return_val_if_fail (iter->stamp == TREE_MODEL_STAMP, FALSE);
2209 path = gtk_tree_path_new ();
2210 gtk_tree_path_append_index (path, GPOINTER_TO_INT (iter->user_data));
2216 tree_model_get_value (GtkTreeModel *model, GtkTreeIter *iter,
2217 gint column, GValue *value)
2219 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2222 g_return_if_fail (iter->stamp == TREE_MODEL_STAMP);
2224 idx = GPOINTER_TO_INT (iter->user_data);
2225 assert (idx >= 0 && idx < list->line_cnt);
2229 g_value_init (value, G_TYPE_INT);
2230 g_value_set_int (value, idx + list->first_line + 1);
2234 g_value_init (value, G_TYPE_STRING);
2235 g_value_set_static_string (value, ds_cstr (&list->lines[idx]));
2240 tree_model_iter_children (GtkTreeModel *tree_model,
2242 GtkTreeIter *parent)
2248 tree_model_n_children (GtkTreeModel *model, GtkTreeIter *iter)
2250 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2252 return iter == NULL ? list->line_cnt : 0;
2256 tree_model_nth_child (GtkTreeModel *model, GtkTreeIter *iter,
2257 GtkTreeIter *parent, gint n)
2259 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2260 g_return_val_if_fail (IS_TEXT_IMPORT_MODEL (model), FALSE);
2264 return init_iter (list, n, iter);
2268 text_import_model_tree_model_init (gpointer iface_, gpointer data UNUSED)
2270 GtkTreeModelIface *iface = (GtkTreeModelIface *) iface_;
2272 iface->get_flags = tree_model_get_flags;
2273 iface->get_n_columns = tree_model_n_columns;
2274 iface->get_column_type = tree_model_column_type;
2275 iface->get_iter = tree_model_get_iter;
2276 iface->iter_next = tree_model_iter_next;
2277 iface->get_path = tree_model_get_path;
2278 iface->get_value = tree_model_get_value;
2280 iface->iter_children = tree_model_iter_children;
2281 iface->iter_has_child = tree_model_iter_has_child;
2282 iface->iter_n_children = tree_model_n_children;
2283 iface->iter_nth_child = tree_model_nth_child;
2284 iface->iter_parent = tree_model_iter_parent;
2288 text_import_model_iter_to_row (const GtkTreeIter *iter)
2290 assert (iter->stamp == TREE_MODEL_STAMP);
2291 return GPOINTER_TO_INT (iter->user_data);
2294 /* Increments the "watch cursor" level, setting the cursor for
2295 the assistant window to a watch face to indicate to the user
2296 that the ongoing operation may take some time. */
2298 push_watch_cursor (struct import_assistant *ia)
2300 if (++ia->asst.watch_cursor == 1)
2302 GtkWidget *widget = GTK_WIDGET (ia->asst.assistant);
2303 GdkDisplay *display = gtk_widget_get_display (widget);
2304 GdkCursor *cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
2305 gdk_window_set_cursor (widget->window, cursor);
2306 gdk_cursor_unref (cursor);
2307 gdk_display_flush (display);
2311 /* Decrements the "watch cursor" level. If the level reaches
2312 zero, the cursor is reset to its default shape. */
2314 pop_watch_cursor (struct import_assistant *ia)
2316 if (--ia->asst.watch_cursor == 0)
2318 GtkWidget *widget = GTK_WIDGET (ia->asst.assistant);
2319 gdk_window_set_cursor (widget->window, NULL);