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/>. */
19 #include "ui/gui/text-data-import-dialog.h"
22 #include <gtk-contrib/psppire-sheet.h>
28 #include "data/data-in.h"
29 #include "data/data-out.h"
30 #include "data/format-guesser.h"
31 #include "data/value-labels.h"
32 #include "language/data-io/data-parser.h"
33 #include "language/lexer/lexer.h"
34 #include "libpspp/assertion.h"
35 #include "libpspp/message.h"
36 #include "ui/gui/checkbox-treeview.h"
37 #include "ui/gui/descriptives-dialog.h"
38 #include "ui/gui/dialog-common.h"
39 #include "ui/gui/executor.h"
40 #include "ui/gui/helper.h"
41 #include "ui/gui/psppire-data-window.h"
42 #include "ui/gui/psppire-dialog.h"
43 #include "ui/gui/psppire-var-sheet.h"
44 #include "ui/gui/psppire-var-store.h"
45 #include "ui/gui/widget-io.h"
46 #include "ui/syntax-gen.h"
49 #include "gl/xalloc.h"
52 #define _(msgid) gettext (msgid)
53 #define N_(msgid) msgid
56 /* TextImportModel, a GtkTreeModel used by the text data import
60 TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER, /* 1-based line number in file */
61 TEXT_IMPORT_MODEL_COLUMN_LINE, /* The line from the file. */
63 typedef struct TextImportModel TextImportModel;
64 typedef struct TextImportModelClass TextImportModelClass;
66 TextImportModel *text_import_model_new (struct string *lines, size_t line_cnt,
68 gint text_import_model_iter_to_row (const GtkTreeIter *);
70 struct import_assistant;
72 /* The file to be imported. */
75 char *file_name; /* File name. */
76 unsigned long int total_lines; /* Number of lines in file. */
77 bool total_is_exact; /* Is total_lines exact (or an estimate)? */
79 /* The first several lines of the file. */
83 static bool init_file (struct import_assistant *, GtkWindow *parent);
84 static void destroy_file (struct import_assistant *);
86 /* The main body of the GTK+ assistant and related data. */
90 GtkAssistant *assistant;
92 GtkWidget *paste_button;
93 GtkWidget *reset_button;
97 GtkCellRenderer *prop_renderer;
98 GtkCellRenderer *fixed_renderer;
100 static void init_assistant (struct import_assistant *, GtkWindow *);
101 static void destroy_assistant (struct import_assistant *);
102 static GtkWidget *add_page_to_assistant (struct import_assistant *,
104 GtkAssistantPageType);
106 /* The introduction page of the assistant. */
110 GtkWidget *all_cases_button;
111 GtkWidget *n_cases_button;
112 GtkWidget *n_cases_spin;
113 GtkWidget *percent_button;
114 GtkWidget *percent_spin;
116 static void init_intro_page (struct import_assistant *);
117 static void reset_intro_page (struct import_assistant *);
119 /* Page where the user chooses the first line of data. */
120 struct first_line_page
122 int skip_lines; /* Number of initial lines to skip? */
123 bool variable_names; /* Variable names above first line of data? */
126 GtkTreeView *tree_view;
127 GtkWidget *variable_names_cb;
129 static void init_first_line_page (struct import_assistant *);
130 static void reset_first_line_page (struct import_assistant *);
132 /* Page where the user chooses field separators. */
133 struct separators_page
135 /* How to break lines into columns. */
136 struct string separators; /* Field separators. */
137 struct string quotes; /* Quote characters. */
138 bool escape; /* Doubled quotes yield a quote mark? */
140 /* The columns produced thereby. */
141 struct column *columns; /* Information about each column. */
142 size_t column_cnt; /* Number of columns. */
145 GtkWidget *custom_cb;
146 GtkWidget *custom_entry;
148 GtkWidget *quote_combo;
149 GtkEntry *quote_entry;
150 GtkWidget *escape_cb;
151 GtkTreeView *fields_tree_view;
153 /* The columns that the separators divide the data into. */
156 /* Variable name for this column. This is the variable name
157 used on the separators page; it can be overridden by the
158 user on the formats page. */
161 /* Maximum length of any row in this column. */
164 /* Contents of this column: contents[row] is the contents for
167 A null substring indicates a missing column for that row
168 (because the line contains an insufficient number of
171 contents[] elements may be substrings of the lines[]
172 strings that represent the whole lines of the file, to
173 save memory. Other elements are dynamically allocated
174 with ss_alloc_substring. */
175 struct substring *contents;
177 static void init_separators_page (struct import_assistant *);
178 static void destroy_separators_page (struct import_assistant *);
179 static void prepare_separators_page (struct import_assistant *);
180 static void reset_separators_page (struct import_assistant *);
182 /* Page where the user verifies and adjusts input formats. */
185 struct dictionary *dict;
188 GtkTreeView *data_tree_view;
189 PsppireDict *psppire_dict;
190 struct variable **modified_vars;
191 size_t modified_var_cnt;
193 static void init_formats_page (struct import_assistant *);
194 static void destroy_formats_page (struct import_assistant *);
195 static void prepare_formats_page (struct import_assistant *);
196 static void reset_formats_page (struct import_assistant *);
198 struct import_assistant
201 struct assistant asst;
202 struct intro_page intro;
203 struct first_line_page first_line;
204 struct separators_page separators;
205 struct formats_page formats;
208 static void apply_dict (const struct dictionary *, struct string *);
209 static char *generate_syntax (const struct import_assistant *);
211 static gboolean get_tooltip_location (GtkWidget *widget, gint wx, gint wy,
212 const struct import_assistant *,
213 size_t *row, size_t *column);
214 static void make_tree_view (const struct import_assistant *ia,
216 GtkTreeView **tree_view);
217 static void add_line_number_column (const struct import_assistant *,
219 static gint get_monospace_width (GtkTreeView *, GtkCellRenderer *,
221 static gint get_string_width (GtkTreeView *, GtkCellRenderer *,
223 static GtkTreeViewColumn *make_data_column (struct import_assistant *,
224 GtkTreeView *, bool input,
226 static GtkTreeView *create_data_tree_view (bool input, GtkContainer *parent,
227 struct import_assistant *);
228 static void escape_underscores (const char *in, char *out);
229 static void push_watch_cursor (struct import_assistant *);
230 static void pop_watch_cursor (struct import_assistant *);
232 /* Pops up the Text Data Import assistant. */
234 text_data_import_assistant (GtkWindow *parent_window)
236 struct import_assistant *ia;
238 ia = xzalloc (sizeof *ia);
239 if (!init_file (ia, parent_window))
245 init_assistant (ia, parent_window);
246 init_intro_page (ia);
247 init_first_line_page (ia);
248 init_separators_page (ia);
249 init_formats_page (ia);
251 gtk_widget_show_all (GTK_WIDGET (ia->asst.assistant));
253 ia->asst.main_loop = g_main_loop_new (NULL, false);
254 g_main_loop_run (ia->asst.main_loop);
255 g_main_loop_unref (ia->asst.main_loop);
257 switch (ia->asst.response)
259 case GTK_RESPONSE_APPLY:
260 free (execute_syntax_string (generate_syntax (ia)));
262 case PSPPIRE_RESPONSE_PASTE:
263 free (paste_syntax_to_window (generate_syntax (ia)));
269 destroy_formats_page (ia);
270 destroy_separators_page (ia);
271 destroy_assistant (ia);
276 /* Emits PSPP syntax to S that applies the dictionary attributes
277 (such as missing values and value labels) of the variables in
280 apply_dict (const struct dictionary *dict, struct string *s)
282 size_t var_cnt = dict_get_var_cnt (dict);
285 for (i = 0; i < var_cnt; i++)
287 struct variable *var = dict_get_var (dict, i);
288 const char *name = var_get_name (var);
289 enum val_type type = var_get_type (var);
290 int width = var_get_width (var);
291 enum measure measure = var_get_measure (var);
292 enum alignment alignment = var_get_alignment (var);
293 const struct fmt_spec *format = var_get_print_format (var);
295 if (var_has_missing_values (var))
297 const struct missing_values *mv = var_get_missing_values (var);
300 syntax_gen_pspp (s, "MISSING VALUES %ss (", name);
301 for (j = 0; j < mv_n_values (mv); j++)
304 ds_put_cstr (s, ", ");
305 syntax_gen_value (s, mv_get_value (mv, j), width, format);
308 if (mv_has_range (mv))
311 if (mv_has_value (mv))
312 ds_put_cstr (s, ", ");
313 mv_get_range (mv, &low, &high);
314 syntax_gen_num_range (s, low, high, format);
316 ds_put_cstr (s, ").\n");
318 if (var_has_value_labels (var))
320 const struct val_labs *vls = var_get_value_labels (var);
321 const struct val_lab **labels = val_labs_sorted (vls);
322 size_t n_labels = val_labs_count (vls);
325 syntax_gen_pspp (s, "VALUE LABELS %ss", name);
326 for (i = 0; i < n_labels; i++)
328 const struct val_lab *vl = labels[i];
329 ds_put_cstr (s, "\n ");
330 syntax_gen_value (s, &vl->value, width, format);
331 ds_put_byte (s, ' ');
332 syntax_gen_string (s, ss_cstr (val_lab_get_label (vl)));
335 ds_put_cstr (s, ".\n");
337 if (var_has_label (var))
338 syntax_gen_pspp (s, "VARIABLE LABELS %ss %sq.\n",
339 name, var_get_label (var));
340 if (measure != var_default_measure (type))
341 syntax_gen_pspp (s, "VARIABLE LEVEL %ss (%ss).\n",
343 (measure == MEASURE_NOMINAL ? "NOMINAL"
344 : measure == MEASURE_ORDINAL ? "ORDINAL"
346 if (alignment != var_default_alignment (type))
347 syntax_gen_pspp (s, "VARIABLE ALIGNMENT %ss (%ss).\n",
349 (alignment == ALIGN_LEFT ? "LEFT"
350 : alignment == ALIGN_CENTRE ? "CENTER"
352 if (var_get_display_width (var) != var_default_display_width (width))
353 syntax_gen_pspp (s, "VARIABLE WIDTH %ss (%d).\n",
354 name, var_get_display_width (var));
358 /* Generates and returns PSPP syntax to execute the import
359 operation described by IA. The caller must free the syntax
362 generate_syntax (const struct import_assistant *ia)
364 struct string s = DS_EMPTY_INITIALIZER;
373 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (
374 ia->intro.n_cases_button)))
375 ds_put_format (&s, " /IMPORTCASES=FIRST %d\n",
376 gtk_spin_button_get_value_as_int (
377 GTK_SPIN_BUTTON (ia->intro.n_cases_spin)));
378 else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (
379 ia->intro.percent_button)))
380 ds_put_format (&s, " /IMPORTCASES=PERCENT %d\n",
381 gtk_spin_button_get_value_as_int (
382 GTK_SPIN_BUTTON (ia->intro.percent_spin)));
384 ds_put_cstr (&s, " /IMPORTCASES=ALL\n");
386 " /ARRANGEMENT=DELIMITED\n"
388 if (ia->first_line.skip_lines > 0)
389 ds_put_format (&s, " /FIRSTCASE=%d\n", ia->first_line.skip_lines + 1);
390 ds_put_cstr (&s, " /DELIMITERS=\"");
391 if (ds_find_byte (&ia->separators.separators, '\t') != SIZE_MAX)
392 ds_put_cstr (&s, "\\t");
393 if (ds_find_byte (&ia->separators.separators, '\\') != SIZE_MAX)
394 ds_put_cstr (&s, "\\\\");
395 for (i = 0; i < ds_length (&ia->separators.separators); i++)
397 char c = ds_at (&ia->separators.separators, i);
399 ds_put_cstr (&s, "\"\"");
400 else if (c != '\t' && c != '\\')
403 ds_put_cstr (&s, "\"\n");
404 if (!ds_is_empty (&ia->separators.quotes))
405 syntax_gen_pspp (&s, " /QUALIFIER=%sq\n", ds_cstr (&ia->separators.quotes));
406 if (!ds_is_empty (&ia->separators.quotes) && ia->separators.escape)
407 ds_put_cstr (&s, " /ESCAPE\n");
408 ds_put_cstr (&s, " /VARIABLES=\n");
410 var_cnt = dict_get_var_cnt (ia->formats.dict);
411 for (i = 0; i < var_cnt; i++)
413 struct variable *var = dict_get_var (ia->formats.dict, i);
414 char format_string[FMT_STRING_LEN_MAX + 1];
415 fmt_to_string (var_get_print_format (var), format_string);
416 ds_put_format (&s, " %s %s%s\n",
417 var_get_name (var), format_string,
418 i == var_cnt - 1 ? "." : "");
421 apply_dict (ia->formats.dict, &s);
426 /* Choosing a file and reading it. */
428 static char *choose_file (GtkWindow *parent_window);
430 /* Obtains the file to import from the user and initializes IA's
431 file substructure. PARENT_WINDOW must be the window to use
432 as the file chooser window's parent.
434 Returns true if successful, false if the file name could not
435 be obtained or the file could not be read. */
437 init_file (struct import_assistant *ia, GtkWindow *parent_window)
439 struct file *file = &ia->file;
440 enum { MAX_PREVIEW_LINES = 1000 }; /* Max number of lines to read. */
441 enum { MAX_LINE_LEN = 16384 }; /* Max length of an acceptable line. */
444 file->file_name = choose_file (parent_window);
445 if (file->file_name == NULL)
448 stream = fopen (file->file_name, "r");
451 msg (ME, _("Could not open `%s': %s"),
452 file->file_name, strerror (errno));
456 file->lines = xnmalloc (MAX_PREVIEW_LINES, sizeof *file->lines);
457 for (; file->line_cnt < MAX_PREVIEW_LINES; file->line_cnt++)
459 struct string *line = &file->lines[file->line_cnt];
461 ds_init_empty (line);
462 if (!ds_read_line (line, stream, MAX_LINE_LEN))
466 else if (ferror (stream))
467 msg (ME, _("Error reading `%s': %s"),
468 file->file_name, strerror (errno));
470 msg (ME, _("Failed to read `%s', because it contains a line "
471 "over %d bytes long and therefore appears not to be "
473 file->file_name, MAX_LINE_LEN);
478 ds_chomp (line, '\n');
479 ds_chomp (line, '\r');
482 if (file->line_cnt == 0)
484 msg (ME, _("`%s' is empty."), file->file_name);
490 /* Estimate the number of lines in the file. */
491 if (file->line_cnt < MAX_PREVIEW_LINES)
492 file->total_lines = file->line_cnt;
496 off_t position = ftello (stream);
497 if (fstat (fileno (stream), &s) == 0 && position > 0)
498 file->total_lines = (double) file->line_cnt / position * s.st_size;
500 file->total_lines = 0;
506 /* Frees IA's file substructure. */
508 destroy_file (struct import_assistant *ia)
510 struct file *f = &ia->file;
513 for (i = 0; i < f->line_cnt; i++)
514 ds_destroy (&f->lines[i]);
516 g_free (f->file_name);
519 /* Obtains the file to read from the user and returns the name of
520 the file as a string that must be freed with g_free if
521 successful, otherwise a null pointer. PARENT_WINDOW must be
522 the window to use as the file chooser window's parent. */
524 choose_file (GtkWindow *parent_window)
529 dialog = gtk_file_chooser_dialog_new (_("Import Delimited Text Data"),
531 GTK_FILE_CHOOSER_ACTION_OPEN,
532 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
533 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
536 switch (gtk_dialog_run (GTK_DIALOG (dialog)))
538 case GTK_RESPONSE_ACCEPT:
539 file_name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
545 gtk_widget_destroy (dialog);
552 static void close_assistant (struct import_assistant *, int response);
553 static void on_prepare (GtkAssistant *assistant, GtkWidget *page,
554 struct import_assistant *);
555 static void on_cancel (GtkAssistant *assistant, struct import_assistant *);
556 static void on_close (GtkAssistant *assistant, struct import_assistant *);
557 static void on_paste (GtkButton *button, struct import_assistant *);
558 static void on_reset (GtkButton *button, struct import_assistant *);
559 static void close_assistant (struct import_assistant *, int response);
561 /* Initializes IA's asst substructure. PARENT_WINDOW must be the
562 window to use as the assistant window's parent. */
564 init_assistant (struct import_assistant *ia, GtkWindow *parent_window)
566 struct assistant *a = &ia->asst;
568 a->builder = builder_new ("text-data-import.ui");
569 a->assistant = GTK_ASSISTANT (gtk_assistant_new ());
570 g_signal_connect (a->assistant, "prepare", G_CALLBACK (on_prepare), ia);
571 g_signal_connect (a->assistant, "cancel", G_CALLBACK (on_cancel), ia);
572 g_signal_connect (a->assistant, "close", G_CALLBACK (on_close), ia);
573 a->paste_button = gtk_button_new_from_stock (GTK_STOCK_PASTE);
574 gtk_assistant_add_action_widget (a->assistant, a->paste_button);
575 g_signal_connect (a->paste_button, "clicked", G_CALLBACK (on_paste), ia);
576 a->reset_button = gtk_button_new_from_stock ("pspp-stock-reset");
577 gtk_assistant_add_action_widget (a->assistant, a->reset_button);
578 g_signal_connect (a->reset_button, "clicked", G_CALLBACK (on_reset), ia);
579 gtk_window_set_title (GTK_WINDOW (a->assistant),
580 _("Importing Delimited Text Data"));
581 gtk_window_set_transient_for (GTK_WINDOW (a->assistant), parent_window);
582 gtk_window_set_icon_name (GTK_WINDOW (a->assistant), "psppicon");
584 a->prop_renderer = gtk_cell_renderer_text_new ();
585 g_object_ref_sink (a->prop_renderer);
586 a->fixed_renderer = gtk_cell_renderer_text_new ();
587 g_object_ref_sink (a->fixed_renderer);
588 g_object_set (G_OBJECT (a->fixed_renderer),
589 "family", "Monospace",
593 /* Frees IA's asst substructure. */
595 destroy_assistant (struct import_assistant *ia)
597 struct assistant *a = &ia->asst;
599 g_object_unref (a->prop_renderer);
600 g_object_unref (a->fixed_renderer);
601 g_object_unref (a->builder);
604 /* Appends a page of the given TYPE, with PAGE as its content, to
605 the GtkAssistant encapsulated by IA. Returns the GtkWidget
606 that represents the page. */
608 add_page_to_assistant (struct import_assistant *ia,
609 GtkWidget *page, GtkAssistantPageType type)
615 title = gtk_window_get_title (GTK_WINDOW (page));
616 title_copy = xstrdup (title ? title : "");
618 content = gtk_bin_get_child (GTK_BIN (page));
620 g_object_ref (content);
621 gtk_container_remove (GTK_CONTAINER (page), content);
623 gtk_widget_destroy (page);
625 gtk_assistant_append_page (ia->asst.assistant, content);
626 gtk_assistant_set_page_type (ia->asst.assistant, content, type);
627 gtk_assistant_set_page_title (ia->asst.assistant, content, title_copy);
628 gtk_assistant_set_page_complete (ia->asst.assistant, content, true);
635 /* Called just before PAGE is displayed as the current page of
636 ASSISTANT, this updates IA content according to the new
639 on_prepare (GtkAssistant *assistant, GtkWidget *page,
640 struct import_assistant *ia)
643 if (gtk_assistant_get_page_type (assistant, page)
644 == GTK_ASSISTANT_PAGE_CONFIRM)
645 gtk_widget_grab_focus (assistant->apply);
647 gtk_widget_grab_focus (assistant->forward);
649 if (page == ia->separators.page)
650 prepare_separators_page (ia);
651 else if (page == ia->formats.page)
652 prepare_formats_page (ia);
654 gtk_widget_show (ia->asst.reset_button);
655 if (page == ia->formats.page)
656 gtk_widget_show (ia->asst.paste_button);
658 gtk_widget_hide (ia->asst.paste_button);
661 /* Called when the Cancel button in the assistant is clicked. */
663 on_cancel (GtkAssistant *assistant, struct import_assistant *ia)
665 close_assistant (ia, GTK_RESPONSE_CANCEL);
668 /* Called when the Apply button on the last page of the assistant
671 on_close (GtkAssistant *assistant, struct import_assistant *ia)
673 close_assistant (ia, GTK_RESPONSE_APPLY);
676 /* Called when the Paste button on the last page of the assistant
679 on_paste (GtkButton *button, struct import_assistant *ia)
681 close_assistant (ia, PSPPIRE_RESPONSE_PASTE);
684 /* Called when the Reset button is clicked. */
686 on_reset (GtkButton *button, struct import_assistant *ia)
688 gint page_num = gtk_assistant_get_current_page (ia->asst.assistant);
689 GtkWidget *page = gtk_assistant_get_nth_page (ia->asst.assistant, page_num);
691 if (page == ia->intro.page)
692 reset_intro_page (ia);
693 else if (page == ia->first_line.page)
694 reset_first_line_page (ia);
695 else if (page == ia->separators.page)
696 reset_separators_page (ia);
697 else if (page == ia->formats.page)
698 reset_formats_page (ia);
701 /* Causes the assistant to close, returning RESPONSE for
702 interpretation by text_data_import_assistant. */
704 close_assistant (struct import_assistant *ia, int response)
706 ia->asst.response = response;
707 g_main_loop_quit (ia->asst.main_loop);
708 gtk_widget_hide (GTK_WIDGET (ia->asst.assistant));
711 /* The "intro" page of the assistant. */
713 static void on_intro_amount_changed (struct import_assistant *);
715 /* Initializes IA's intro substructure. */
717 init_intro_page (struct import_assistant *ia)
719 GtkBuilder *builder = ia->asst.builder;
720 struct intro_page *p = &ia->intro;
722 GtkWidget *hbox_n_cases ;
723 GtkWidget *hbox_percent ;
727 p->n_cases_spin = gtk_spin_button_new_with_range (0, INT_MAX, 100);
729 hbox_n_cases = widget_scanf (_("Only the first %4d cases"), &p->n_cases_spin);
731 table = get_widget_assert (builder, "button-table");
733 gtk_table_attach_defaults (GTK_TABLE (table), hbox_n_cases,
737 p->percent_spin = gtk_spin_button_new_with_range (0, 100, 10);
739 hbox_percent = widget_scanf (_("Only the first %3d %% of file (approximately)"), &p->percent_spin);
741 gtk_table_attach_defaults (GTK_TABLE (table), hbox_percent,
745 p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Intro"),
746 GTK_ASSISTANT_PAGE_INTRO);
748 p->all_cases_button = get_widget_assert (builder, "import-all-cases");
750 p->n_cases_button = get_widget_assert (builder, "import-n-cases");
752 p->percent_button = get_widget_assert (builder, "import-percent");
754 g_signal_connect_swapped (p->all_cases_button, "toggled",
755 G_CALLBACK (on_intro_amount_changed), ia);
756 g_signal_connect_swapped (p->n_cases_button, "toggled",
757 G_CALLBACK (on_intro_amount_changed), ia);
758 g_signal_connect_swapped (p->percent_button, "toggled",
759 G_CALLBACK (on_intro_amount_changed), ia);
761 on_intro_amount_changed (ia);
764 ds_put_cstr (&s, _("This assistant will guide you through the process of "
765 "importing data into PSPP from a text file with one line "
766 "per case, in which fields are separated by tabs, "
767 "commas, or other delimiters.\n\n"));
768 if (ia->file.total_is_exact)
770 &s, ngettext ("The selected file contains %zu line of text. ",
771 "The selected file contains %zu lines of text. ",
774 else if (ia->file.total_lines > 0)
778 "The selected file contains approximately %lu line of text. ",
779 "The selected file contains approximately %lu lines of text. ",
780 ia->file.total_lines),
781 ia->file.total_lines);
784 "Only the first %zu line of the file will be shown for "
785 "preview purposes in the following screens. ",
786 "Only the first %zu lines of the file will be shown for "
787 "preview purposes in the following screens. ",
791 ds_put_cstr (&s, _("You may choose below how much of the file should "
792 "actually be imported."));
793 gtk_label_set_text (GTK_LABEL (get_widget_assert (builder, "intro-label")),
798 /* Resets IA's intro page to its initial state. */
800 reset_intro_page (struct import_assistant *ia)
802 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->intro.all_cases_button),
806 /* Called when one of the radio buttons is clicked. */
808 on_intro_amount_changed (struct import_assistant *ia)
810 struct intro_page *p = &ia->intro;
812 gtk_widget_set_sensitive (p->n_cases_spin,
813 gtk_toggle_button_get_active (
814 GTK_TOGGLE_BUTTON (p->n_cases_button)));
816 gtk_widget_set_sensitive (p->percent_spin,
817 gtk_toggle_button_get_active (
818 GTK_TOGGLE_BUTTON (p->percent_button)));
821 /* The "first line" page of the assistant. */
823 static GtkTreeView *create_lines_tree_view (GtkContainer *parent_window,
824 struct import_assistant *);
825 static void on_first_line_change (GtkTreeSelection *,
826 struct import_assistant *);
827 static void on_variable_names_cb_toggle (GtkToggleButton *,
828 struct import_assistant *);
829 static void set_first_line (struct import_assistant *);
830 static void get_first_line (struct import_assistant *);
832 /* Initializes IA's first_line substructure. */
834 init_first_line_page (struct import_assistant *ia)
836 struct first_line_page *p = &ia->first_line;
837 GtkBuilder *builder = ia->asst.builder;
839 p->page = add_page_to_assistant (ia, get_widget_assert (builder, "FirstLine"),
840 GTK_ASSISTANT_PAGE_CONTENT);
841 gtk_widget_destroy (get_widget_assert (builder, "first-line"));
842 p->tree_view = create_lines_tree_view (
843 GTK_CONTAINER (get_widget_assert (builder, "first-line-scroller")), ia);
844 p->variable_names_cb = get_widget_assert (builder, "variable-names");
845 gtk_tree_selection_set_mode (
846 gtk_tree_view_get_selection (GTK_TREE_VIEW (p->tree_view)),
847 GTK_SELECTION_BROWSE);
849 g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (p->tree_view)),
850 "changed", G_CALLBACK (on_first_line_change), ia);
851 g_signal_connect (p->variable_names_cb, "toggled",
852 G_CALLBACK (on_variable_names_cb_toggle), ia);
855 /* Resets the first_line page to its initial content. */
857 reset_first_line_page (struct import_assistant *ia)
859 ia->first_line.skip_lines = 0;
860 ia->first_line.variable_names = false;
864 /* Creates and returns a tree view that contains each of the
865 lines in IA's file as a row. */
867 create_lines_tree_view (GtkContainer *parent, struct import_assistant *ia)
869 GtkTreeView *tree_view;
870 GtkTreeViewColumn *column;
871 size_t max_line_length;
872 gint content_width, header_width;
874 gchar *title = _("Text");
876 make_tree_view (ia, 0, &tree_view);
878 column = gtk_tree_view_column_new_with_attributes
880 title, ia->asst.fixed_renderer,
881 "text", TEXT_IMPORT_MODEL_COLUMN_LINE,
884 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
887 for (i = 0; i < ia->file.line_cnt; i++)
889 size_t w = ds_length (&ia->file.lines[i]);
890 max_line_length = MAX (max_line_length, w);
893 content_width = get_monospace_width (tree_view, ia->asst.fixed_renderer,
895 header_width = get_string_width (tree_view, ia->asst.prop_renderer, title);
896 gtk_tree_view_column_set_fixed_width (column, MAX (content_width,
898 gtk_tree_view_append_column (tree_view, column);
900 gtk_tree_view_set_fixed_height_mode (tree_view, true);
902 gtk_container_add (parent, GTK_WIDGET (tree_view));
903 gtk_widget_show (GTK_WIDGET (tree_view));
908 /* Called when the line selected in the first_line tree view
911 on_first_line_change (GtkTreeSelection *selection UNUSED,
912 struct import_assistant *ia)
917 /* Called when the checkbox that indicates whether variable
918 names are in the row above the first line is toggled. */
920 on_variable_names_cb_toggle (GtkToggleButton *variable_names_cb UNUSED,
921 struct import_assistant *ia)
926 /* Sets the widgets to match IA's first_line substructure. */
928 set_first_line (struct import_assistant *ia)
932 path = gtk_tree_path_new_from_indices (ia->first_line.skip_lines, -1);
933 gtk_tree_view_set_cursor (GTK_TREE_VIEW (ia->first_line.tree_view),
935 gtk_tree_path_free (path);
937 gtk_toggle_button_set_active (
938 GTK_TOGGLE_BUTTON (ia->first_line.variable_names_cb),
939 ia->first_line.variable_names);
940 gtk_widget_set_sensitive (ia->first_line.variable_names_cb,
941 ia->first_line.skip_lines > 0);
944 /* Sets IA's first_line substructure to match the widgets. */
946 get_first_line (struct import_assistant *ia)
948 GtkTreeSelection *selection;
952 selection = gtk_tree_view_get_selection (ia->first_line.tree_view);
953 if (gtk_tree_selection_get_selected (selection, &model, &iter))
955 GtkTreePath *path = gtk_tree_model_get_path (model, &iter);
956 int row = gtk_tree_path_get_indices (path)[0];
957 gtk_tree_path_free (path);
959 ia->first_line.skip_lines = row;
960 ia->first_line.variable_names =
961 (ia->first_line.skip_lines > 0
962 && gtk_toggle_button_get_active (
963 GTK_TOGGLE_BUTTON (ia->first_line.variable_names_cb)));
965 gtk_widget_set_sensitive (ia->first_line.variable_names_cb,
966 ia->first_line.skip_lines > 0);
969 /* The "separators" page of the assistant. */
971 static void revise_fields_preview (struct import_assistant *ia);
972 static void choose_likely_separators (struct import_assistant *ia);
973 static void find_commonest_chars (unsigned long int histogram[UCHAR_MAX + 1],
974 const char *targets, const char *def,
975 struct string *result);
976 static void clear_fields (struct import_assistant *ia);
977 static void revise_fields_preview (struct import_assistant *);
978 static void set_separators (struct import_assistant *);
979 static void get_separators (struct import_assistant *);
980 static void on_separators_custom_entry_notify (GObject *UNUSED,
982 struct import_assistant *);
983 static void on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
984 struct import_assistant *);
985 static void on_quote_combo_change (GtkComboBox *combo,
986 struct import_assistant *);
987 static void on_quote_cb_toggle (GtkToggleButton *quote_cb,
988 struct import_assistant *);
989 static void on_separator_toggle (GtkToggleButton *, struct import_assistant *);
990 static void render_input_cell (GtkTreeViewColumn *tree_column,
991 GtkCellRenderer *cell,
992 GtkTreeModel *model, GtkTreeIter *iter,
994 static gboolean on_query_input_tooltip (GtkWidget *widget, gint wx, gint wy,
995 gboolean keyboard_mode UNUSED,
997 struct import_assistant *);
999 /* A common field separator and its identifying name. */
1002 const char *name; /* Name (for use with get_widget_assert). */
1003 int c; /* Separator character. */
1006 /* All the separators in the dialog box. */
1007 static const struct separator separators[] =
1019 #define SEPARATOR_CNT (sizeof separators / sizeof *separators)
1022 set_quote_list (GtkComboBoxEntry *cb)
1024 GtkListStore *list = gtk_list_store_new (1, G_TYPE_STRING);
1027 const gchar *seperator[3] = {"'\"", "\'", "\""};
1029 for (i = 0; i < 3; i++)
1031 const gchar *s = seperator[i];
1033 /* Add a new row to the model */
1034 gtk_list_store_append (list, &iter);
1035 gtk_list_store_set (list, &iter,
1041 gtk_combo_box_set_model (GTK_COMBO_BOX (cb), GTK_TREE_MODEL (list));
1043 gtk_combo_box_entry_set_text_column (cb, 0);
1046 /* Initializes IA's separators substructure. */
1048 init_separators_page (struct import_assistant *ia)
1050 GtkBuilder *builder = ia->asst.builder;
1051 struct separators_page *p = &ia->separators;
1054 choose_likely_separators (ia);
1056 p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Separators"),
1057 GTK_ASSISTANT_PAGE_CONTENT);
1058 p->custom_cb = get_widget_assert (builder, "custom-cb");
1059 p->custom_entry = get_widget_assert (builder, "custom-entry");
1060 p->quote_combo = get_widget_assert (builder, "quote-combo");
1061 p->quote_entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (p->quote_combo)));
1062 p->quote_cb = get_widget_assert (builder, "quote-cb");
1063 p->escape_cb = get_widget_assert (builder, "escape");
1065 set_separators (ia);
1066 set_quote_list (GTK_COMBO_BOX_ENTRY (p->quote_combo));
1067 p->fields_tree_view = GTK_TREE_VIEW (get_widget_assert (builder, "fields"));
1068 g_signal_connect (p->quote_combo, "changed",
1069 G_CALLBACK (on_quote_combo_change), ia);
1070 g_signal_connect (p->quote_cb, "toggled",
1071 G_CALLBACK (on_quote_cb_toggle), ia);
1072 g_signal_connect (p->custom_entry, "notify::text",
1073 G_CALLBACK (on_separators_custom_entry_notify), ia);
1074 g_signal_connect (p->custom_cb, "toggled",
1075 G_CALLBACK (on_separators_custom_cb_toggle), ia);
1076 for (i = 0; i < SEPARATOR_CNT; i++)
1077 g_signal_connect (get_widget_assert (builder, separators[i].name),
1078 "toggled", G_CALLBACK (on_separator_toggle), ia);
1079 g_signal_connect (p->escape_cb, "toggled",
1080 G_CALLBACK (on_separator_toggle), ia);
1083 /* Frees IA's separators substructure. */
1085 destroy_separators_page (struct import_assistant *ia)
1087 struct separators_page *s = &ia->separators;
1089 ds_destroy (&s->separators);
1090 ds_destroy (&s->quotes);
1094 /* Called just before the separators page becomes visible in the
1097 prepare_separators_page (struct import_assistant *ia)
1099 revise_fields_preview (ia);
1102 /* Called when the Reset button is clicked on the separators
1103 page, resets the separators to the defaults. */
1105 reset_separators_page (struct import_assistant *ia)
1107 choose_likely_separators (ia);
1108 set_separators (ia);
1111 /* Frees and clears the column data in IA's separators
1114 clear_fields (struct import_assistant *ia)
1116 struct separators_page *s = &ia->separators;
1118 if (s->column_cnt > 0)
1123 for (row = 0; row < ia->file.line_cnt; row++)
1125 const struct string *line = &ia->file.lines[row];
1126 const char *line_start = ds_data (line);
1127 const char *line_end = ds_end (line);
1129 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1131 char *s = ss_data (col->contents[row]);
1132 if (!(s >= line_start && s <= line_end))
1133 ss_dealloc (&col->contents[row]);
1137 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1140 free (col->contents);
1149 /* Breaks the file data in IA into columns based on the
1150 separators set in IA's separators substructure. */
1152 split_fields (struct import_assistant *ia)
1154 struct separators_page *s = &ia->separators;
1155 size_t columns_allocated;
1161 /* Is space in the set of separators? */
1162 space_sep = ss_find_byte (ds_ss (&s->separators), ' ') != SIZE_MAX;
1164 /* Split all the lines, not just those from
1165 ia->first_line.skip_lines on, so that we split the line that
1166 contains variables names if ia->first_line.variable_names is
1168 columns_allocated = 0;
1169 for (row = 0; row < ia->file.line_cnt; row++)
1171 struct string *line = &ia->file.lines[row];
1172 struct substring text = ds_ss (line);
1175 for (column_idx = 0; ; column_idx++)
1177 struct substring field;
1178 struct column *column;
1181 ss_ltrim (&text, ss_cstr (" "));
1182 if (ss_is_empty (text))
1184 if (column_idx != 0)
1188 else if (!ds_is_empty (&s->quotes)
1189 && ds_find_byte (&s->quotes, text.string[0]) != SIZE_MAX)
1191 int quote = ss_get_byte (&text);
1193 ss_get_until (&text, quote, &field);
1200 while ((c = ss_get_byte (&text)) != EOF)
1202 ds_put_byte (&s, c);
1203 else if (ss_match_byte (&text, quote))
1204 ds_put_byte (&s, quote);
1211 ss_get_bytes (&text, ss_cspan (text, ds_ss (&s->separators)),
1214 if (column_idx >= s->column_cnt)
1216 struct column *column;
1218 if (s->column_cnt >= columns_allocated)
1219 s->columns = x2nrealloc (s->columns, &columns_allocated,
1220 sizeof *s->columns);
1221 column = &s->columns[s->column_cnt++];
1222 column->name = NULL;
1224 column->contents = xcalloc (ia->file.line_cnt,
1225 sizeof *column->contents);
1227 column = &s->columns[column_idx];
1228 column->contents[row] = field;
1229 if (ss_length (field) > column->width)
1230 column->width = ss_length (field);
1233 ss_ltrim (&text, ss_cstr (" "));
1234 if (ss_is_empty (text))
1236 if (ss_find_byte (ds_ss (&s->separators), ss_first (text))
1238 ss_advance (&text, 1);
1243 /* Chooses a name for each column on the separators page */
1245 choose_column_names (struct import_assistant *ia)
1247 const struct first_line_page *f = &ia->first_line;
1248 struct separators_page *s = &ia->separators;
1249 struct dictionary *dict;
1250 unsigned long int generated_name_count = 0;
1254 dict = dict_create ();
1255 name_row = f->variable_names && f->skip_lines ? f->skip_lines : 0;
1256 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1258 char name[VAR_NAME_LEN + 1];
1261 hint = name_row ? ss_xstrdup (col->contents[name_row - 1]) : NULL;
1262 if (!dict_make_unique_var_name (dict, hint, &generated_name_count, name))
1266 col->name = xstrdup (name);
1267 dict_create_var_assert (dict, name, 0);
1269 dict_destroy (dict);
1272 /* Picks the most likely separator and quote characters based on
1275 choose_likely_separators (struct import_assistant *ia)
1277 unsigned long int histogram[UCHAR_MAX + 1] = { 0 };
1280 /* Construct a histogram of all the characters used in the
1282 for (row = 0; row < ia->file.line_cnt; row++)
1284 struct substring line = ds_ss (&ia->file.lines[row]);
1285 size_t length = ss_length (line);
1287 for (i = 0; i < length; i++)
1288 histogram[(unsigned char) line.string[i]]++;
1291 find_commonest_chars (histogram, "\"'", "", &ia->separators.quotes);
1292 find_commonest_chars (histogram, ",;:/|!\t-", ",",
1293 &ia->separators.separators);
1294 ia->separators.escape = true;
1297 /* Chooses the most common character among those in TARGETS,
1298 based on the frequency data in HISTOGRAM, and stores it in
1299 RESULT. If there is a tie for the most common character among
1300 those in TARGETS, the earliest character is chosen. If none
1301 of the TARGETS appear at all, then DEF is used as a
1304 find_commonest_chars (unsigned long int histogram[UCHAR_MAX + 1],
1305 const char *targets, const char *def,
1306 struct string *result)
1308 unsigned char max = 0;
1309 unsigned long int max_count = 0;
1311 for (; *targets != '\0'; targets++)
1313 unsigned char c = *targets;
1314 unsigned long int count = histogram[c];
1315 if (count > max_count)
1324 ds_put_byte (result, max);
1327 ds_assign_cstr (result, def);
1330 /* Revises the contents of the fields tree view based on the
1331 currently chosen set of separators. */
1333 revise_fields_preview (struct import_assistant *ia)
1337 push_watch_cursor (ia);
1339 w = GTK_WIDGET (ia->separators.fields_tree_view);
1340 gtk_widget_destroy (w);
1341 get_separators (ia);
1343 choose_column_names (ia);
1344 ia->separators.fields_tree_view = create_data_tree_view (
1346 GTK_CONTAINER (get_widget_assert (ia->asst.builder, "fields-scroller")),
1349 pop_watch_cursor (ia);
1352 /* Sets the widgets to match IA's separators substructure. */
1354 set_separators (struct import_assistant *ia)
1356 struct separators_page *s = &ia->separators;
1358 struct string custom;
1363 ds_init_empty (&custom);
1365 for (i = 0; i < ds_length (&s->separators); i++)
1367 unsigned char c = ds_at (&s->separators, i);
1370 for (j = 0; j < SEPARATOR_CNT; j++)
1372 const struct separator *s = &separators[j];
1380 ds_put_byte (&custom, c);
1384 for (i = 0; i < SEPARATOR_CNT; i++)
1386 const struct separator *s = &separators[i];
1387 GtkWidget *button = get_widget_assert (ia->asst.builder, s->name);
1388 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
1389 (seps & (1u << i)) != 0);
1391 any_custom = !ds_is_empty (&custom);
1392 gtk_entry_set_text (GTK_ENTRY (s->custom_entry), ds_cstr (&custom));
1393 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->custom_cb),
1395 gtk_widget_set_sensitive (s->custom_entry, any_custom);
1396 ds_destroy (&custom);
1398 any_quotes = !ds_is_empty (&s->quotes);
1400 gtk_entry_set_text (s->quote_entry,
1401 any_quotes ? ds_cstr (&s->quotes) : "\"");
1402 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->quote_cb),
1404 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->escape_cb),
1406 gtk_widget_set_sensitive (s->quote_combo, any_quotes);
1407 gtk_widget_set_sensitive (s->escape_cb, any_quotes);
1410 /* Sets IA's separators substructure to match the widgets. */
1412 get_separators (struct import_assistant *ia)
1414 struct separators_page *s = &ia->separators;
1417 ds_clear (&s->separators);
1418 for (i = 0; i < SEPARATOR_CNT; i++)
1420 const struct separator *sep = &separators[i];
1421 GtkWidget *button = get_widget_assert (ia->asst.builder, sep->name);
1422 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
1423 ds_put_byte (&s->separators, sep->c);
1426 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->custom_cb)))
1427 ds_put_cstr (&s->separators,
1428 gtk_entry_get_text (GTK_ENTRY (s->custom_entry)));
1430 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->quote_cb)))
1432 gchar *text = gtk_combo_box_get_active_text (
1433 GTK_COMBO_BOX (s->quote_combo));
1434 ds_assign_cstr (&s->quotes, text);
1438 ds_clear (&s->quotes);
1439 s->escape = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->escape_cb));
1442 /* Called when the user changes the entry field for custom
1445 on_separators_custom_entry_notify (GObject *gobject UNUSED,
1446 GParamSpec *arg1 UNUSED,
1447 struct import_assistant *ia)
1449 revise_fields_preview (ia);
1452 /* Called when the user toggles the checkbox that enables custom
1455 on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
1456 struct import_assistant *ia)
1458 bool is_active = gtk_toggle_button_get_active (custom_cb);
1459 gtk_widget_set_sensitive (ia->separators.custom_entry, is_active);
1460 revise_fields_preview (ia);
1463 /* Called when the user changes the selection in the combo box
1464 that selects a quote character. */
1466 on_quote_combo_change (GtkComboBox *combo, struct import_assistant *ia)
1468 revise_fields_preview (ia);
1471 /* Called when the user toggles the checkbox that enables
1474 on_quote_cb_toggle (GtkToggleButton *quote_cb, struct import_assistant *ia)
1476 bool is_active = gtk_toggle_button_get_active (quote_cb);
1477 gtk_widget_set_sensitive (ia->separators.quote_combo, is_active);
1478 gtk_widget_set_sensitive (ia->separators.escape_cb, is_active);
1479 revise_fields_preview (ia);
1482 /* Called when the user toggles one of the separators
1485 on_separator_toggle (GtkToggleButton *toggle UNUSED,
1486 struct import_assistant *ia)
1488 revise_fields_preview (ia);
1491 /* Called to render one of the cells in the fields preview tree
1494 render_input_cell (GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1495 GtkTreeModel *model, GtkTreeIter *iter,
1498 struct import_assistant *ia = ia_;
1499 struct substring field;
1503 column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1505 row = text_import_model_iter_to_row (iter) + ia->first_line.skip_lines;
1506 field = ia->separators.columns[column].contents[row];
1507 if (field.string != NULL)
1509 GValue text = {0, };
1510 g_value_init (&text, G_TYPE_STRING);
1511 g_value_take_string (&text, ss_xstrdup (field));
1512 g_object_set_property (G_OBJECT (cell), "text", &text);
1513 g_value_unset (&text);
1514 g_object_set (cell, "background-set", FALSE, (void *) NULL);
1519 "background", "red",
1520 "background-set", TRUE,
1524 /* Called to render a tooltip on one of the cells in the fields
1525 preview tree view. */
1527 on_query_input_tooltip (GtkWidget *widget, gint wx, gint wy,
1528 gboolean keyboard_mode UNUSED,
1529 GtkTooltip *tooltip, struct import_assistant *ia)
1533 if (!get_tooltip_location (widget, wx, wy, ia, &row, &column))
1536 if (ia->separators.columns[column].contents[row].string != NULL)
1539 gtk_tooltip_set_text (tooltip,
1540 _("This input line has too few separators "
1541 "to fill in this field."));
1545 /* The "formats" page of the assistant. */
1547 static void on_variable_change (PsppireDict *dict, int idx,
1548 struct import_assistant *);
1549 static void clear_modified_vars (struct import_assistant *);
1551 /* Initializes IA's formats substructure. */
1553 init_formats_page (struct import_assistant *ia)
1555 GtkBuilder *builder = ia->asst.builder;
1556 struct formats_page *p = &ia->formats;
1558 p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Formats"),
1559 GTK_ASSISTANT_PAGE_CONFIRM);
1560 p->data_tree_view = GTK_TREE_VIEW (get_widget_assert (builder, "data"));
1561 p->modified_vars = NULL;
1562 p->modified_var_cnt = 0;
1566 /* Frees IA's formats substructure. */
1568 destroy_formats_page (struct import_assistant *ia)
1570 struct formats_page *p = &ia->formats;
1572 if (p->psppire_dict != NULL)
1574 /* This destroys p->dict also. */
1575 g_object_unref (p->psppire_dict);
1577 clear_modified_vars (ia);
1580 /* Called just before the formats page of the assistant is
1583 prepare_formats_page (struct import_assistant *ia)
1585 struct dictionary *dict;
1586 PsppireDict *psppire_dict;
1587 PsppireVarStore *var_store;
1588 GtkBin *vars_scroller;
1589 GtkWidget *old_var_sheet;
1590 PsppireVarSheet *var_sheet;
1591 struct separators_page *s = &ia->separators;
1592 struct formats_page *p = &ia->formats;
1593 struct fmt_guesser *fg;
1594 unsigned long int number = 0;
1597 push_watch_cursor (ia);
1599 dict = dict_create ();
1600 fg = fmt_guesser_create ();
1601 for (column_idx = 0; column_idx < s->column_cnt; column_idx++)
1603 struct variable *modified_var;
1604 char name[VAR_NAME_LEN + 1];
1606 modified_var = (column_idx < p->modified_var_cnt
1607 ? p->modified_vars[column_idx] : NULL);
1608 if (modified_var == NULL)
1610 struct column *column = &s->columns[column_idx];
1611 struct variable *var;
1612 struct fmt_spec format;
1615 /* Choose variable name. */
1616 if (!dict_make_unique_var_name (dict, column->name, &number, name))
1619 /* Choose variable format. */
1620 fmt_guesser_clear (fg);
1621 for (row = ia->first_line.skip_lines; row < ia->file.line_cnt; row++)
1622 fmt_guesser_add (fg, column->contents[row]);
1623 fmt_guesser_guess (fg, &format);
1624 fmt_fix_input (&format);
1626 /* Create variable. */
1627 var = dict_create_var_assert (dict, name, fmt_var_width (&format));
1628 var_set_both_formats (var, &format);
1632 if (!dict_make_unique_var_name (dict, var_get_name (modified_var),
1635 dict_clone_var_as_assert (dict, modified_var, name);
1638 fmt_guesser_destroy (fg);
1640 psppire_dict = psppire_dict_new_from_dict (dict);
1641 g_signal_connect (psppire_dict, "variable_changed",
1642 G_CALLBACK (on_variable_change), ia);
1643 ia->formats.dict = dict;
1644 ia->formats.psppire_dict = psppire_dict;
1646 /* XXX: PsppireVarStore doesn't hold a reference to
1647 psppire_dict for now, but it should. After it does, we
1648 should g_object_ref the psppire_dict here, since we also
1649 hold a reference via ia->formats.dict. */
1650 var_store = psppire_var_store_new (psppire_dict);
1651 g_object_set (var_store,
1652 "format-type", PSPPIRE_VAR_STORE_INPUT_FORMATS,
1654 var_sheet = PSPPIRE_VAR_SHEET (psppire_var_sheet_new ());
1655 g_object_set (var_sheet,
1657 "may-create-vars", FALSE,
1660 vars_scroller = GTK_BIN (get_widget_assert (ia->asst.builder, "vars-scroller"));
1661 old_var_sheet = gtk_bin_get_child (vars_scroller);
1662 if (old_var_sheet != NULL)
1663 gtk_widget_destroy (old_var_sheet);
1664 gtk_container_add (GTK_CONTAINER (vars_scroller), GTK_WIDGET (var_sheet));
1665 gtk_widget_show (GTK_WIDGET (var_sheet));
1667 gtk_widget_destroy (GTK_WIDGET (ia->formats.data_tree_view));
1668 ia->formats.data_tree_view = create_data_tree_view (
1670 GTK_CONTAINER (get_widget_assert (ia->asst.builder, "data-scroller")),
1673 pop_watch_cursor (ia);
1676 /* Clears the set of user-modified variables from IA's formats
1677 substructure. This discards user modifications to variable
1678 formats, thereby causing formats to revert to their
1681 clear_modified_vars (struct import_assistant *ia)
1683 struct formats_page *p = &ia->formats;
1686 for (i = 0; i < p->modified_var_cnt; i++)
1687 var_destroy (p->modified_vars[i]);
1688 free (p->modified_vars);
1689 p->modified_vars = NULL;
1690 p->modified_var_cnt = 0;
1693 /* Resets the formats page to its defaults, discarding user
1696 reset_formats_page (struct import_assistant *ia)
1698 clear_modified_vars (ia);
1699 prepare_formats_page (ia);
1702 /* Called when the user changes one of the variables in the
1705 on_variable_change (PsppireDict *dict, int dict_idx,
1706 struct import_assistant *ia)
1708 struct formats_page *p = &ia->formats;
1709 GtkTreeView *tv = ia->formats.data_tree_view;
1710 gint column_idx = dict_idx + 1;
1712 push_watch_cursor (ia);
1714 /* Remove previous column and replace with new column. */
1715 gtk_tree_view_remove_column (tv, gtk_tree_view_get_column (tv, column_idx));
1716 gtk_tree_view_insert_column (tv, make_data_column (ia, tv, false, dict_idx),
1719 /* Save a copy of the modified variable in modified_vars, so
1720 that its attributes will be preserved if we back up to the
1721 previous page with the Prev button and then come back
1723 if (dict_idx >= p->modified_var_cnt)
1726 p->modified_vars = xnrealloc (p->modified_vars, dict_idx + 1,
1727 sizeof *p->modified_vars);
1728 for (i = 0; i <= dict_idx; i++)
1729 p->modified_vars[i] = NULL;
1730 p->modified_var_cnt = dict_idx + 1;
1732 if (p->modified_vars[dict_idx])
1733 var_destroy (p->modified_vars[dict_idx]);
1734 p->modified_vars[dict_idx]
1735 = var_clone (psppire_dict_get_variable (dict, dict_idx));
1737 pop_watch_cursor (ia);
1740 /* Parses the contents of the field at (ROW,COLUMN) according to
1741 its variable format. If OUTPUTP is non-null, then *OUTPUTP
1742 receives the formatted output for that field (which must be
1743 freed with free). If TOOLTIPP is non-null, then *TOOLTIPP
1744 receives a message suitable for use in a tooltip, if one is
1745 needed, or a null pointer otherwise. Returns true if a
1746 tooltip message is needed, otherwise false. */
1748 parse_field (struct import_assistant *ia,
1749 size_t row, size_t column,
1750 char **outputp, char **tooltipp)
1752 struct substring field;
1754 struct variable *var;
1755 const struct fmt_spec *in;
1756 struct fmt_spec out;
1760 field = ia->separators.columns[column].contents[row];
1761 var = dict_get_var (ia->formats.dict, column);
1762 value_init (&val, var_get_width (var));
1763 in = var_get_print_format (var);
1764 out = fmt_for_output_from_input (in);
1766 if (field.string != NULL)
1770 error = data_in (field, LEGACY_NATIVE, in->type, &val,
1771 var_get_width (var),
1772 dict_get_encoding (ia->formats.dict));
1775 tooltip = xasprintf (_("Cannot parse field content `%.*s' as "
1777 (int) field.length, field.string,
1778 fmt_name (in->type), error);
1784 tooltip = xstrdup (_("This input line has too few separators "
1785 "to fill in this field."));
1786 value_set_missing (&val, var_get_width (var));
1788 if (outputp != NULL)
1790 *outputp = data_out (&val, dict_get_encoding (ia->formats.dict), &out);
1792 value_destroy (&val, var_get_width (var));
1794 ok = tooltip == NULL;
1795 if (tooltipp != NULL)
1796 *tooltipp = tooltip;
1802 /* Called to render one of the cells in the data preview tree
1805 render_output_cell (GtkTreeViewColumn *tree_column,
1806 GtkCellRenderer *cell,
1807 GtkTreeModel *model,
1811 struct import_assistant *ia = ia_;
1813 GValue gvalue = { 0, };
1816 ok = parse_field (ia,
1817 (text_import_model_iter_to_row (iter)
1818 + ia->first_line.skip_lines),
1819 GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1823 g_value_init (&gvalue, G_TYPE_STRING);
1824 g_value_take_string (&gvalue, output);
1825 g_object_set_property (G_OBJECT (cell), "text", &gvalue);
1826 g_value_unset (&gvalue);
1829 g_object_set (cell, "background-set", FALSE, (void *) NULL);
1832 "background", "red",
1833 "background-set", TRUE,
1837 /* Called to render a tooltip for one of the cells in the data
1838 preview tree view. */
1840 on_query_output_tooltip (GtkWidget *widget, gint wx, gint wy,
1841 gboolean keyboard_mode UNUSED,
1842 GtkTooltip *tooltip, struct import_assistant *ia)
1847 if (!get_tooltip_location (widget, wx, wy, ia, &row, &column))
1850 if (parse_field (ia, row, column, NULL, &text))
1853 gtk_tooltip_set_text (tooltip, text);
1858 /* Utility functions used by multiple pages of the assistant. */
1861 get_tooltip_location (GtkWidget *widget, gint wx, gint wy,
1862 const struct import_assistant *ia,
1863 size_t *row, size_t *column)
1865 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
1869 GtkTreeViewColumn *tree_column;
1870 GtkTreeModel *tree_model;
1873 /* Check that WIDGET is really visible on the screen before we
1874 do anything else. This is a bug fix for a sticky situation:
1875 when text_data_import_assistant() returns, it frees the data
1876 necessary to compose the tool tip message, but there may be
1877 a tool tip under preparation at that point (even if there is
1878 no visible tool tip) that will call back into us a little
1879 bit later. Perhaps the correct solution to this problem is
1880 to make the data related to the tool tips part of a GObject
1881 that only gets destroyed when all references are released,
1882 but this solution appears to be effective too. */
1883 if (!GTK_WIDGET_MAPPED (widget))
1886 gtk_tree_view_convert_widget_to_bin_window_coords (tree_view,
1888 if (!gtk_tree_view_get_path_at_pos (tree_view, bx, by,
1889 &path, &tree_column, NULL, NULL))
1892 *column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1895 tree_model = gtk_tree_view_get_model (tree_view);
1896 ok = gtk_tree_model_get_iter (tree_model, &iter, path);
1897 gtk_tree_path_free (path);
1901 *row = text_import_model_iter_to_row (&iter) + ia->first_line.skip_lines;
1906 make_tree_view (const struct import_assistant *ia,
1908 GtkTreeView **tree_view)
1910 GtkTreeModel *model;
1912 *tree_view = GTK_TREE_VIEW (gtk_tree_view_new ());
1913 model = GTK_TREE_MODEL (text_import_model_new (
1914 ia->file.lines + first_line,
1915 ia->file.line_cnt - first_line, first_line));
1916 gtk_tree_view_set_model (*tree_view, model);
1918 add_line_number_column (ia, *tree_view);
1922 add_line_number_column (const struct import_assistant *ia,
1923 GtkTreeView *treeview)
1925 GtkTreeViewColumn *column;
1927 column = gtk_tree_view_column_new_with_attributes (
1928 _("Line"), ia->asst.prop_renderer,
1929 "text", TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER,
1931 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
1932 gtk_tree_view_column_set_fixed_width (
1933 column, get_monospace_width (treeview, ia->asst.prop_renderer, 5));
1934 gtk_tree_view_append_column (treeview, column);
1938 get_monospace_width (GtkTreeView *treeview, GtkCellRenderer *renderer,
1945 ds_put_byte_multiple (&s, '0', char_cnt);
1946 ds_put_byte (&s, ' ');
1947 width = get_string_width (treeview, renderer, ds_cstr (&s));
1954 get_string_width (GtkTreeView *treeview, GtkCellRenderer *renderer,
1958 g_object_set (G_OBJECT (renderer), "text", string, (void *) NULL);
1959 gtk_cell_renderer_get_size (renderer, GTK_WIDGET (treeview),
1960 NULL, NULL, NULL, &width, NULL);
1964 static GtkTreeViewColumn *
1965 make_data_column (struct import_assistant *ia, GtkTreeView *tree_view,
1966 bool input, gint dict_idx)
1968 struct variable *var = NULL;
1969 struct column *column = NULL;
1970 char name[(VAR_NAME_LEN * 2) + 1];
1972 gint content_width, header_width;
1973 GtkTreeViewColumn *tree_column;
1976 column = &ia->separators.columns[dict_idx];
1978 var = dict_get_var (ia->formats.dict, dict_idx);
1980 escape_underscores (input ? column->name : var_get_name (var), name);
1981 char_cnt = input ? column->width : var_get_print_format (var)->w;
1982 content_width = get_monospace_width (tree_view, ia->asst.fixed_renderer,
1984 header_width = get_string_width (tree_view, ia->asst.prop_renderer,
1987 tree_column = gtk_tree_view_column_new ();
1988 g_object_set_data (G_OBJECT (tree_column), "column-number",
1989 GINT_TO_POINTER (dict_idx));
1990 gtk_tree_view_column_set_title (tree_column, name);
1991 gtk_tree_view_column_pack_start (tree_column, ia->asst.fixed_renderer,
1993 gtk_tree_view_column_set_cell_data_func (
1994 tree_column, ia->asst.fixed_renderer,
1995 input ? render_input_cell : render_output_cell, ia, NULL);
1996 gtk_tree_view_column_set_sizing (tree_column, GTK_TREE_VIEW_COLUMN_FIXED);
1997 gtk_tree_view_column_set_fixed_width (tree_column, MAX (content_width,
2003 static GtkTreeView *
2004 create_data_tree_view (bool input, GtkContainer *parent,
2005 struct import_assistant *ia)
2007 GtkTreeView *tree_view;
2010 make_tree_view (ia, ia->first_line.skip_lines, &tree_view);
2011 gtk_tree_selection_set_mode (gtk_tree_view_get_selection (tree_view),
2012 GTK_SELECTION_NONE);
2014 for (i = 0; i < ia->separators.column_cnt; i++)
2015 gtk_tree_view_append_column (tree_view,
2016 make_data_column (ia, tree_view, input, i));
2018 g_object_set (G_OBJECT (tree_view), "has-tooltip", TRUE, (void *) NULL);
2019 g_signal_connect (tree_view, "query-tooltip",
2020 G_CALLBACK (input ? on_query_input_tooltip
2021 : on_query_output_tooltip), ia);
2022 gtk_tree_view_set_fixed_height_mode (tree_view, true);
2024 gtk_container_add (parent, GTK_WIDGET (tree_view));
2025 gtk_widget_show (GTK_WIDGET (tree_view));
2031 escape_underscores (const char *in, char *out)
2033 for (; *in != '\0'; in++)
2042 /* TextImportModel, a GtkTreeModel implementation used by some
2043 pages of the assistant. */
2045 #define G_TYPE_TEXT_IMPORT_MODEL (text_import_model_get_type ())
2046 #define TEXT_IMPORT_MODEL(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), G_TYPE_TEXT_IMPORT_MODEL, TextImportModel))
2047 #define TEXT_IMPORT_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), G_TYPE_TEXT_IMPORT_MODEL, TextImportModelClass))
2048 #define IS_TEXT_IMPORT_MODEL(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), G_TYPE_TEXT_IMPORT_MODEL))
2049 #define IS_TEXT_IMPORT_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), G_TYPE_TEXT_IMPORT_MODEL))
2050 #define TEXT_IMPORT_MODEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), G_TYPE_TEXT_IMPORT_MODEL, TextImportModelClass))
2052 /* Random number used in 'stamp' member of GtkTreeIter. */
2053 #define TREE_MODEL_STAMP 0x7efd67d3
2055 struct TextImportModel
2058 struct string *lines;
2063 struct TextImportModelClass
2065 GObjectClass parent_class;
2068 GType text_import_model_get_type (void);
2069 static void text_import_model_tree_model_init (gpointer iface, gpointer data);
2072 text_import_model_get_type (void)
2074 static GType object_type = 0;
2078 static const GTypeInfo object_info = {
2079 sizeof (TextImportModelClass),
2080 (GBaseInitFunc) NULL,
2081 (GBaseFinalizeFunc) NULL,
2082 NULL, /* class_init */
2083 NULL, /* class_finalize */
2084 NULL, /* class_data */
2085 sizeof (TextImportModel),
2086 0, /* n_preallocs */
2087 NULL, /* instance_init */
2090 static const GInterfaceInfo tree_model_info = {
2091 text_import_model_tree_model_init,
2096 object_type = g_type_register_static (G_TYPE_OBJECT,
2100 g_type_add_interface_static (object_type, GTK_TYPE_TREE_MODEL,
2110 /* Creates and returns a new TextImportModel that contains the
2111 LINE_CNT lines in LINES. The lines before FIRST_LINE in LINES
2112 are not part of the model, but they are included in the line
2113 numbers in the TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER column.
2115 The caller retains responsibility for freeing LINES and must
2116 ensure that its lifetime and that of the strings that it
2117 contains exceeds that of the TextImportModel. */
2119 text_import_model_new (struct string *lines, size_t line_cnt,
2122 TextImportModel *new_text_import_model
2123 = g_object_new (G_TYPE_TEXT_IMPORT_MODEL, NULL);
2124 new_text_import_model->lines = lines;
2125 new_text_import_model->line_cnt = line_cnt;
2126 new_text_import_model->first_line = first_line;
2127 return new_text_import_model;
2132 tree_model_iter_has_child (GtkTreeModel *tree_model,
2139 tree_model_iter_parent (GtkTreeModel *tree_model,
2146 static GtkTreeModelFlags
2147 tree_model_get_flags (GtkTreeModel *model)
2149 g_return_val_if_fail (IS_TEXT_IMPORT_MODEL (model), (GtkTreeModelFlags) 0);
2151 return GTK_TREE_MODEL_LIST_ONLY | GTK_TREE_MODEL_ITERS_PERSIST;
2156 tree_model_n_columns (GtkTreeModel *model)
2162 tree_model_column_type (GtkTreeModel *model, gint index)
2164 return (index == TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER ? G_TYPE_INT
2165 : index == TEXT_IMPORT_MODEL_COLUMN_LINE ? G_TYPE_STRING
2170 init_iter (TextImportModel *list, gint idx, GtkTreeIter *iter)
2172 if (idx < 0 || idx >= list->line_cnt)
2175 iter->user_data = GINT_TO_POINTER (-1);
2180 iter->stamp = TREE_MODEL_STAMP;
2181 iter->user_data = GINT_TO_POINTER (idx);
2187 tree_model_get_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreePath *path)
2189 gint *indices, depth;
2191 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2193 g_return_val_if_fail (path, FALSE);
2195 indices = gtk_tree_path_get_indices (path);
2196 depth = gtk_tree_path_get_depth (path);
2198 g_return_val_if_fail (depth == 1, FALSE);
2200 return init_iter (list, indices[0], iter);
2205 tree_model_iter_next (GtkTreeModel *model, GtkTreeIter *iter)
2207 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2210 assert (iter->stamp == TREE_MODEL_STAMP);
2212 idx = GPOINTER_TO_INT (iter->user_data);
2213 return init_iter (list, idx == -1 ? -1 : idx + 1, iter);
2216 static GtkTreePath *
2217 tree_model_get_path (GtkTreeModel *model, GtkTreeIter *iter)
2221 g_return_val_if_fail (iter->stamp == TREE_MODEL_STAMP, FALSE);
2223 path = gtk_tree_path_new ();
2224 gtk_tree_path_append_index (path, GPOINTER_TO_INT (iter->user_data));
2230 tree_model_get_value (GtkTreeModel *model, GtkTreeIter *iter,
2231 gint column, GValue *value)
2233 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2236 g_return_if_fail (iter->stamp == TREE_MODEL_STAMP);
2238 idx = GPOINTER_TO_INT (iter->user_data);
2239 assert (idx >= 0 && idx < list->line_cnt);
2243 g_value_init (value, G_TYPE_INT);
2244 g_value_set_int (value, idx + list->first_line + 1);
2248 g_value_init (value, G_TYPE_STRING);
2249 g_value_set_static_string (value, ds_cstr (&list->lines[idx]));
2254 tree_model_iter_children (GtkTreeModel *tree_model,
2256 GtkTreeIter *parent)
2262 tree_model_n_children (GtkTreeModel *model, GtkTreeIter *iter)
2264 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2266 return iter == NULL ? list->line_cnt : 0;
2270 tree_model_nth_child (GtkTreeModel *model, GtkTreeIter *iter,
2271 GtkTreeIter *parent, gint n)
2273 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2274 g_return_val_if_fail (IS_TEXT_IMPORT_MODEL (model), FALSE);
2278 return init_iter (list, n, iter);
2282 text_import_model_tree_model_init (gpointer iface_, gpointer data UNUSED)
2284 GtkTreeModelIface *iface = (GtkTreeModelIface *) iface_;
2286 iface->get_flags = tree_model_get_flags;
2287 iface->get_n_columns = tree_model_n_columns;
2288 iface->get_column_type = tree_model_column_type;
2289 iface->get_iter = tree_model_get_iter;
2290 iface->iter_next = tree_model_iter_next;
2291 iface->get_path = tree_model_get_path;
2292 iface->get_value = tree_model_get_value;
2294 iface->iter_children = tree_model_iter_children;
2295 iface->iter_has_child = tree_model_iter_has_child;
2296 iface->iter_n_children = tree_model_n_children;
2297 iface->iter_nth_child = tree_model_nth_child;
2298 iface->iter_parent = tree_model_iter_parent;
2302 text_import_model_iter_to_row (const GtkTreeIter *iter)
2304 assert (iter->stamp == TREE_MODEL_STAMP);
2305 return GPOINTER_TO_INT (iter->user_data);
2308 /* Increments the "watch cursor" level, setting the cursor for
2309 the assistant window to a watch face to indicate to the user
2310 that the ongoing operation may take some time. */
2312 push_watch_cursor (struct import_assistant *ia)
2314 if (++ia->asst.watch_cursor == 1)
2316 GtkWidget *widget = GTK_WIDGET (ia->asst.assistant);
2317 GdkDisplay *display = gtk_widget_get_display (widget);
2318 GdkCursor *cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
2319 gdk_window_set_cursor (widget->window, cursor);
2320 gdk_cursor_unref (cursor);
2321 gdk_display_flush (display);
2325 /* Decrements the "watch cursor" level. If the level reaches
2326 zero, the cursor is reset to its default shape. */
2328 pop_watch_cursor (struct import_assistant *ia)
2330 if (--ia->asst.watch_cursor == 0)
2332 GtkWidget *widget = GTK_WIDGET (ia->asst.assistant);
2333 gdk_window_set_cursor (widget->window, NULL);