1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2008 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 "checkbox-treeview.h"
20 #include "descriptives-dialog.h"
24 #include <gtksheet/gtksheet.h>
29 #include <data/data-in.h>
30 #include <data/data-out.h>
31 #include <data/format-guesser.h>
32 #include <data/value-labels.h>
33 #include <language/data-io/data-parser.h>
34 #include <language/syntax-string-source.h>
35 #include <libpspp/assertion.h>
36 #include <libpspp/message.h>
37 #include <ui/syntax-gen.h>
38 #include <ui/gui/data-editor.h>
39 #include <ui/gui/dialog-common.h>
40 #include <ui/gui/helper.h>
41 #include <ui/gui/psppire-dialog.h>
42 #include <ui/gui/psppire-var-sheet.h>
43 #include <ui/gui/psppire-var-store.h>
44 #include <ui/gui/syntax-editor.h>
50 #define _(msgid) gettext (msgid)
51 #define N_(msgid) msgid
53 /* TextImportModel, a GtkTreeModel used by the text data import
57 TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER, /* 1-based line number in file */
58 TEXT_IMPORT_MODEL_COLUMN_LINE, /* The line from the file. */
60 typedef struct TextImportModel TextImportModel;
61 typedef struct TextImportModelClass TextImportModelClass;
63 TextImportModel *text_import_model_new (struct string *lines, size_t line_cnt,
65 gint text_import_model_iter_to_row (const GtkTreeIter *);
67 struct import_assistant;
69 /* The file to be imported. */
72 char *file_name; /* File name. */
73 unsigned long int total_lines; /* Number of lines in file. */
74 bool total_is_exact; /* Is total_lines exact (or an estimate)? */
76 /* The first several lines of the file. */
80 static bool init_file (struct import_assistant *, GtkWindow *parent);
81 static void destroy_file (struct import_assistant *);
83 /* The main body of the GTK+ assistant and related data. */
87 GtkAssistant *assistant;
89 GtkWidget *paste_button;
90 GtkWidget *reset_button;
94 GtkCellRenderer *prop_renderer;
95 GtkCellRenderer *fixed_renderer;
97 static void init_assistant (struct import_assistant *, GtkWindow *);
98 static void destroy_assistant (struct import_assistant *);
99 static GtkWidget *add_page_to_assistant (struct import_assistant *,
101 GtkAssistantPageType);
103 /* The introduction page of the assistant. */
107 GtkWidget *all_cases_button;
108 GtkWidget *n_cases_button;
109 GtkWidget *n_cases_spin;
110 GtkWidget *percent_button;
111 GtkWidget *percent_spin;
113 static void init_intro_page (struct import_assistant *);
114 static void reset_intro_page (struct import_assistant *);
116 /* Page where the user chooses the first line of data. */
117 struct first_line_page
119 int skip_lines; /* Number of initial lines to skip? */
120 bool variable_names; /* Variable names above first line of data? */
123 GtkTreeView *tree_view;
124 GtkWidget *variable_names_cb;
126 static void init_first_line_page (struct import_assistant *);
127 static void reset_first_line_page (struct import_assistant *);
129 /* Page where the user chooses field separators. */
130 struct separators_page
132 /* How to break lines into columns. */
133 struct string separators; /* Field separators. */
134 struct string quotes; /* Quote characters. */
135 bool escape; /* Doubled quotes yield a quote mark? */
137 /* The columns produced thereby. */
138 struct column *columns; /* Information about each column. */
139 size_t column_cnt; /* Number of columns. */
142 GtkWidget *custom_cb;
143 GtkWidget *custom_entry;
145 GtkWidget *quote_combo;
146 GtkEntry *quote_entry;
147 GtkWidget *escape_cb;
148 GtkTreeView *fields_tree_view;
150 /* The columns that the separators divide the data into. */
153 /* Variable name for this column. This is the variable name
154 used on the separators page; it can be overridden by the
155 user on the formats page. */
158 /* Maximum length of any row in this column. */
161 /* Contents of this column: contents[row] is the contents for
164 A null substring indicates a missing column for that row
165 (because the line contains an insufficient number of
168 contents[] elements may be substrings of the lines[]
169 strings that represent the whole lines of the file, to
170 save memory. Other elements are dynamically allocated
171 with ss_alloc_substring. */
172 struct substring *contents;
174 static void init_separators_page (struct import_assistant *);
175 static void destroy_separators_page (struct import_assistant *);
176 static void prepare_separators_page (struct import_assistant *);
177 static void reset_separators_page (struct import_assistant *);
179 /* Page where the user verifies and adjusts input formats. */
182 struct dictionary *dict;
185 GtkTreeView *data_tree_view;
186 PsppireDict *psppire_dict;
187 struct variable **modified_vars;
188 size_t modified_var_cnt;
190 static void init_formats_page (struct import_assistant *);
191 static void destroy_formats_page (struct import_assistant *);
192 static void prepare_formats_page (struct import_assistant *);
193 static void reset_formats_page (struct import_assistant *);
195 struct import_assistant
198 struct assistant asst;
199 struct intro_page intro;
200 struct first_line_page first_line;
201 struct separators_page separators;
202 struct formats_page formats;
205 static void apply_dict (const struct dictionary *, struct string *);
206 static char *generate_syntax (const struct import_assistant *);
208 static gboolean get_tooltip_location (GtkWidget *widget, gint wx, gint wy,
209 const struct import_assistant *,
210 size_t *row, size_t *column);
211 static void make_tree_view (const struct import_assistant *ia,
213 GtkTreeView **tree_view);
214 static void add_line_number_column (const struct import_assistant *,
216 static gint get_monospace_width (GtkTreeView *, GtkCellRenderer *,
218 static gint get_string_width (GtkTreeView *, GtkCellRenderer *,
220 static GtkTreeViewColumn *make_data_column (struct import_assistant *,
221 GtkTreeView *, bool input,
223 static GtkTreeView *create_data_tree_view (bool input, GtkContainer *parent,
224 struct import_assistant *);
225 static void escape_underscores (const char *in, char *out);
226 static void push_watch_cursor (struct import_assistant *);
227 static void pop_watch_cursor (struct import_assistant *);
229 /* Pops up the Text Data Import assistant. */
231 text_data_import_assistant (GObject *o, gpointer de_)
233 struct data_editor *de = de_;
234 GtkWindow *parent_window = de->parent.window;
235 struct import_assistant *ia;
237 ia = xzalloc (sizeof *ia);
238 if (!init_file (ia, parent_window))
244 init_assistant (ia, parent_window);
245 init_intro_page (ia);
246 init_first_line_page (ia);
247 init_separators_page (ia);
248 init_formats_page (ia);
250 gtk_widget_show_all (GTK_WIDGET (ia->asst.assistant));
252 ia->asst.main_loop = g_main_loop_new (NULL, false);
253 g_main_loop_run (ia->asst.main_loop);
254 g_main_loop_unref (ia->asst.main_loop);
256 switch (ia->asst.response)
258 case GTK_RESPONSE_APPLY:
260 char *syntax = generate_syntax (ia);
261 execute_syntax (create_syntax_string_source (syntax));
265 case PSPPIRE_RESPONSE_PASTE:
267 char *syntax = generate_syntax (ia);
268 struct syntax_editor *se =
269 (struct syntax_editor *) window_create (WINDOW_SYNTAX, NULL);
270 gtk_text_buffer_insert_at_cursor (se->buffer, syntax, -1);
278 destroy_formats_page (ia);
279 destroy_separators_page (ia);
280 destroy_assistant (ia);
285 /* Emits PSPP syntax to S that applies the dictionary attributes
286 (such as missing values and value labels) of the variables in
289 apply_dict (const struct dictionary *dict, struct string *s)
291 size_t var_cnt = dict_get_var_cnt (dict);
294 for (i = 0; i < var_cnt; i++)
296 struct variable *var = dict_get_var (dict, i);
297 const char *name = var_get_name (var);
298 enum val_type type = var_get_type (var);
299 int width = var_get_width (var);
300 enum measure measure = var_get_measure (var);
301 enum alignment alignment = var_get_alignment (var);
302 const struct fmt_spec *format = var_get_print_format (var);
304 if (var_has_missing_values (var))
306 const struct missing_values *mv = var_get_missing_values (var);
309 syntax_gen_pspp (s, "MISSING VALUES %ss (", name);
310 for (j = 0; j < mv_n_values (mv); j++)
314 ds_put_cstr (s, ", ");
315 mv_get_value (mv, &value, j);
316 syntax_gen_value (s, &value, width, format);
319 if (mv_has_range (mv))
322 if (mv_has_value (mv))
323 ds_put_cstr (s, ", ");
324 mv_get_range (mv, &low, &high);
325 syntax_gen_num_range (s, low, high, format);
327 ds_put_cstr (s, ").\n");
329 if (var_has_value_labels (var))
331 const struct val_labs *vls = var_get_value_labels (var);
332 struct val_labs_iterator *iter;
335 syntax_gen_pspp (s, "VALUE LABELS %ss", name);
336 for (vl = val_labs_first_sorted (vls, &iter); vl != NULL;
337 vl = val_labs_next (vls, &iter))
339 ds_put_cstr (s, "\n ");
340 syntax_gen_value (s, &vl->value, width, format);
341 ds_put_char (s, ' ');
342 syntax_gen_string (s, ss_cstr (vl->label));
344 ds_put_cstr (s, ".\n");
346 if (var_has_label (var))
347 syntax_gen_pspp (s, "VARIABLE LABELS %ss %sq.\n",
348 name, var_get_label (var));
349 if (measure != var_default_measure (type))
350 syntax_gen_pspp (s, "VARIABLE LEVEL %ss (%ss).\n",
352 (measure == MEASURE_NOMINAL ? "NOMINAL"
353 : measure == MEASURE_ORDINAL ? "ORDINAL"
355 if (alignment != var_default_alignment (type))
356 syntax_gen_pspp (s, "VARIABLE ALIGNMENT %ss (%ss).\n",
358 (alignment == ALIGN_LEFT ? "LEFT"
359 : alignment == ALIGN_CENTRE ? "CENTER"
361 if (var_get_display_width (var) != var_default_display_width (width))
362 syntax_gen_pspp (s, "VARIABLE WIDTH %ss (%d).\n",
363 name, var_get_display_width (var));
367 /* Generates and returns PSPP syntax to execute the import
368 operation described by IA. The caller must free the syntax
371 generate_syntax (const struct import_assistant *ia)
373 struct string s = DS_EMPTY_INITIALIZER;
382 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (
383 ia->intro.n_cases_button)))
384 ds_put_format (&s, " /IMPORTCASES=FIRST %d\n",
385 gtk_spin_button_get_value_as_int (
386 GTK_SPIN_BUTTON (ia->intro.n_cases_spin)));
387 else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (
388 ia->intro.percent_button)))
389 ds_put_format (&s, " /IMPORTCASES=PERCENT %d\n",
390 gtk_spin_button_get_value_as_int (
391 GTK_SPIN_BUTTON (ia->intro.percent_spin)));
393 ds_put_cstr (&s, " /IMPORTCASES=ALL\n");
395 " /ARRANGEMENT=DELIMITED\n"
397 if (ia->first_line.skip_lines > 0)
398 ds_put_format (&s, " /FIRSTCASE=%d\n", ia->first_line.skip_lines + 1);
399 ds_put_cstr (&s, " /DELIMITERS=\"");
400 if (ds_find_char (&ia->separators.separators, '\t') != SIZE_MAX)
401 ds_put_cstr (&s, "\\t");
402 if (ds_find_char (&ia->separators.separators, '\\') != SIZE_MAX)
403 ds_put_cstr (&s, "\\\\");
404 for (i = 0; i < ds_length (&ia->separators.separators); i++)
406 char c = ds_at (&ia->separators.separators, i);
408 ds_put_cstr (&s, "\"\"");
409 else if (c != '\t' && c != '\\')
412 ds_put_cstr (&s, "\"\n");
413 if (!ds_is_empty (&ia->separators.quotes))
414 syntax_gen_pspp (&s, " /QUALIFIER=%sq\n", ds_cstr (&ia->separators.quotes));
415 if (!ds_is_empty (&ia->separators.quotes) && ia->separators.escape)
416 ds_put_cstr (&s, " /ESCAPE\n");
417 ds_put_cstr (&s, " /VARIABLES=\n");
419 var_cnt = dict_get_var_cnt (ia->formats.dict);
420 for (i = 0; i < var_cnt; i++)
422 struct variable *var = dict_get_var (ia->formats.dict, i);
423 char format_string[FMT_STRING_LEN_MAX + 1];
424 fmt_to_string (var_get_print_format (var), format_string);
425 ds_put_format (&s, " %s %s%s\n",
426 var_get_name (var), format_string,
427 i == var_cnt - 1 ? "." : "");
430 apply_dict (ia->formats.dict, &s);
435 /* Choosing a file and reading it. */
437 static char *choose_file (GtkWindow *parent_window);
439 /* Obtains the file to import from the user and initializes IA's
440 file substructure. PARENT_WINDOW must be the window to use
441 as the file chooser window's parent.
443 Returns true if successful, false if the file name could not
444 be obtained or the file could not be read. */
446 init_file (struct import_assistant *ia, GtkWindow *parent_window)
448 struct file *file = &ia->file;
449 enum { MAX_PREVIEW_LINES = 1000 }; /* Max number of lines to read. */
450 enum { MAX_LINE_LEN = 16384 }; /* Max length of an acceptable line. */
453 file->file_name = choose_file (parent_window);
454 if (file->file_name == NULL)
457 stream = fopen (file->file_name, "r");
460 msg (ME, _("Could not open \"%s\": %s"),
461 file->file_name, strerror (errno));
465 file->lines = xnmalloc (MAX_PREVIEW_LINES, sizeof *file->lines);
466 for (; file->line_cnt < MAX_PREVIEW_LINES; file->line_cnt++)
468 struct string *line = &file->lines[file->line_cnt];
470 ds_init_empty (line);
471 if (!ds_read_line (line, stream, MAX_LINE_LEN))
475 else if (ferror (stream))
476 msg (ME, _("Error reading \"%s\": %s"),
477 file->file_name, strerror (errno));
479 msg (ME, _("Failed to read \"%s\", because it contains a line "
480 "over %d bytes long and therefore appears not to be "
482 file->file_name, MAX_LINE_LEN);
487 ds_chomp (line, '\n');
488 ds_chomp (line, '\r');
491 if (file->line_cnt == 0)
493 msg (ME, _("\"%s\" is empty."), file->file_name);
499 /* Estimate the number of lines in the file. */
500 if (file->line_cnt < MAX_PREVIEW_LINES)
501 file->total_lines = file->line_cnt;
505 off_t position = ftello (stream);
506 if (fstat (fileno (stream), &s) == 0 && position > 0)
507 file->total_lines = (double) file->line_cnt / position * s.st_size;
509 file->total_lines = 0;
515 /* Frees IA's file substructure. */
517 destroy_file (struct import_assistant *ia)
519 struct file *f = &ia->file;
522 for (i = 0; i < f->line_cnt; i++)
523 ds_destroy (&f->lines[i]);
525 g_free (f->file_name);
528 /* Obtains the file to read from the user and returns the name of
529 the file as a string that must be freed with g_free if
530 successful, otherwise a null pointer. PARENT_WINDOW must be
531 the window to use as the file chooser window's parent. */
533 choose_file (GtkWindow *parent_window)
538 dialog = gtk_file_chooser_dialog_new (_("Import Delimited Text Data"),
540 GTK_FILE_CHOOSER_ACTION_OPEN,
541 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
542 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
545 switch (gtk_dialog_run (GTK_DIALOG (dialog)))
547 case GTK_RESPONSE_ACCEPT:
548 file_name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
554 gtk_widget_destroy (dialog);
561 static void close_assistant (struct import_assistant *, int response);
562 static void on_prepare (GtkAssistant *assistant, GtkWidget *page,
563 struct import_assistant *);
564 static void on_cancel (GtkAssistant *assistant, struct import_assistant *);
565 static void on_close (GtkAssistant *assistant, struct import_assistant *);
566 static void on_paste (GtkButton *button, struct import_assistant *);
567 static void on_reset (GtkButton *button, struct import_assistant *);
568 static void close_assistant (struct import_assistant *, int response);
570 /* Initializes IA's asst substructure. PARENT_WINDOW must be the
571 window to use as the assistant window's parent. */
573 init_assistant (struct import_assistant *ia, GtkWindow *parent_window)
575 struct assistant *a = &ia->asst;
577 a->xml = XML_NEW ("text-data-import.glade");
578 a->assistant = GTK_ASSISTANT (gtk_assistant_new ());
579 g_signal_connect (a->assistant, "prepare", G_CALLBACK (on_prepare), ia);
580 g_signal_connect (a->assistant, "cancel", G_CALLBACK (on_cancel), ia);
581 g_signal_connect (a->assistant, "close", G_CALLBACK (on_close), ia);
582 a->paste_button = gtk_button_new_from_stock (GTK_STOCK_PASTE);
583 gtk_assistant_add_action_widget (a->assistant, a->paste_button);
584 g_signal_connect (a->paste_button, "clicked", G_CALLBACK (on_paste), ia);
585 a->reset_button = gtk_button_new_from_stock ("pspp-stock-reset");
586 gtk_assistant_add_action_widget (a->assistant, a->reset_button);
587 g_signal_connect (a->reset_button, "clicked", G_CALLBACK (on_reset), ia);
588 gtk_window_set_title (GTK_WINDOW (a->assistant),
589 _("Importing Delimited Text Data"));
590 gtk_window_set_transient_for (GTK_WINDOW (a->assistant), parent_window);
592 a->prop_renderer = gtk_cell_renderer_text_new ();
593 g_object_ref_sink (a->prop_renderer);
594 a->fixed_renderer = gtk_cell_renderer_text_new ();
595 g_object_ref_sink (a->fixed_renderer);
596 g_object_set (G_OBJECT (a->fixed_renderer),
597 "family", "Monospace",
601 /* Frees IA's asst substructure. */
603 destroy_assistant (struct import_assistant *ia)
605 struct assistant *a = &ia->asst;
607 g_object_unref (a->prop_renderer);
608 g_object_unref (a->fixed_renderer);
609 g_object_unref (a->xml);
612 /* Appends a page of the given TYPE, with PAGE as its content, to
613 the GtkAssistant encapsulated by IA. Returns the GtkWidget
614 that represents the page. */
616 add_page_to_assistant (struct import_assistant *ia,
617 GtkWidget *page, GtkAssistantPageType type)
623 title = gtk_window_get_title (GTK_WINDOW (page));
624 title_copy = xstrdup (title ? title : "");
626 content = gtk_bin_get_child (GTK_BIN (page));
628 g_object_ref (content);
629 gtk_container_remove (GTK_CONTAINER (page), content);
631 gtk_widget_destroy (page);
633 gtk_assistant_append_page (ia->asst.assistant, content);
634 gtk_assistant_set_page_type (ia->asst.assistant, content, type);
635 gtk_assistant_set_page_title (ia->asst.assistant, content, title_copy);
636 gtk_assistant_set_page_complete (ia->asst.assistant, content, true);
643 /* Called just before PAGE is displayed as the current page of
644 ASSISTANT, this updates IA content according to the new
647 on_prepare (GtkAssistant *assistant, GtkWidget *page,
648 struct import_assistant *ia)
650 if (page == ia->separators.page)
651 prepare_separators_page (ia);
652 else if (page == ia->formats.page)
653 prepare_formats_page (ia);
655 gtk_widget_show (ia->asst.reset_button);
656 if (page == ia->formats.page)
657 gtk_widget_show (ia->asst.paste_button);
659 gtk_widget_hide (ia->asst.paste_button);
662 /* Called when the Cancel button in the assistant is clicked. */
664 on_cancel (GtkAssistant *assistant, struct import_assistant *ia)
666 close_assistant (ia, GTK_RESPONSE_CANCEL);
669 /* Called when the Apply button on the last page of the assistant
672 on_close (GtkAssistant *assistant, struct import_assistant *ia)
674 close_assistant (ia, GTK_RESPONSE_APPLY);
677 /* Called when the Paste button on the last page of the assistant
680 on_paste (GtkButton *button, struct import_assistant *ia)
682 close_assistant (ia, PSPPIRE_RESPONSE_PASTE);
685 /* Called when the Reset button is clicked. */
687 on_reset (GtkButton *button, struct import_assistant *ia)
689 gint page_num = gtk_assistant_get_current_page (ia->asst.assistant);
690 GtkWidget *page = gtk_assistant_get_nth_page (ia->asst.assistant, page_num);
692 if (page == ia->intro.page)
693 reset_intro_page (ia);
694 else if (page == ia->first_line.page)
695 reset_first_line_page (ia);
696 else if (page == ia->separators.page)
697 reset_separators_page (ia);
698 else if (page == ia->formats.page)
699 reset_formats_page (ia);
702 /* Causes the assistant to close, returning RESPONSE for
703 interpretation by text_data_import_assistant. */
705 close_assistant (struct import_assistant *ia, int response)
707 ia->asst.response = response;
708 g_main_loop_quit (ia->asst.main_loop);
709 gtk_widget_hide (GTK_WIDGET (ia->asst.assistant));
712 /* The "intro" page of the assistant. */
714 static void on_intro_amount_changed (GtkToggleButton *button,
715 struct import_assistant *);
717 /* Initializes IA's intro substructure. */
719 init_intro_page (struct import_assistant *ia)
721 GladeXML *xml = ia->asst.xml;
722 struct intro_page *p = &ia->intro;
725 p->page = add_page_to_assistant (ia, get_widget_assert (xml, "Intro"),
726 GTK_ASSISTANT_PAGE_INTRO);
727 p->all_cases_button = get_widget_assert (xml, "import-all-cases");
728 p->n_cases_button = get_widget_assert (xml, "import-n-cases");
729 p->n_cases_spin = get_widget_assert (xml, "n-cases-spin");
730 p->percent_button = get_widget_assert (xml, "import-percent");
731 p->percent_spin = get_widget_assert (xml, "percent-spin");
732 g_signal_connect (p->all_cases_button, "toggled",
733 G_CALLBACK (on_intro_amount_changed), ia);
734 g_signal_connect (p->n_cases_button, "toggled",
735 G_CALLBACK (on_intro_amount_changed), ia);
736 g_signal_connect (p->percent_button, "toggled",
737 G_CALLBACK (on_intro_amount_changed), ia);
740 ds_put_cstr (&s, _("This assistant will guide you through the process of "
741 "importing data into PSPP from a text file with one line "
742 "per case, in which fields are separated by tabs, "
743 "commas, or other delimiters.\n\n"));
744 if (ia->file.total_is_exact)
746 &s, ngettext ("The selected file contains %zu line of text. ",
747 "The selected file contains %zu lines of text. ",
750 else if (ia->file.total_lines > 0)
754 "The selected file contains approximately %lu line of text. ",
755 "The selected file contains approximately %lu lines of text. ",
756 ia->file.total_lines),
757 ia->file.total_lines);
760 "Only the first %zu line of the file will be shown for "
761 "preview purposes in the following screens. ",
762 "Only the first %zu lines of the file will be shown for "
763 "preview purposes in the following screens. ",
767 ds_put_cstr (&s, _("You may choose below how much of the file should "
768 "actually be imported."));
769 gtk_label_set_text (GTK_LABEL (get_widget_assert (xml, "intro-label")),
774 /* Resets IA's intro page to its initial state. */
776 reset_intro_page (struct import_assistant *ia)
778 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->intro.all_cases_button),
782 /* Called when one of the radio buttons is clicked. */
784 on_intro_amount_changed (GtkToggleButton *button UNUSED,
785 struct import_assistant *ia)
787 struct intro_page *p = &ia->intro;
789 gtk_widget_set_sensitive (p->n_cases_spin,
790 gtk_toggle_button_get_active (
791 GTK_TOGGLE_BUTTON (p->n_cases_button)));
793 gtk_widget_set_sensitive (ia->intro.percent_spin,
794 gtk_toggle_button_get_active (
795 GTK_TOGGLE_BUTTON (p->percent_button)));
798 /* The "first line" page of the assistant. */
800 static GtkTreeView *create_lines_tree_view (GtkContainer *parent_window,
801 struct import_assistant *);
802 static void on_first_line_change (GtkTreeSelection *,
803 struct import_assistant *);
804 static void on_variable_names_cb_toggle (GtkToggleButton *,
805 struct import_assistant *);
806 static void set_first_line (struct import_assistant *);
807 static void get_first_line (struct import_assistant *);
809 /* Initializes IA's first_line substructure. */
811 init_first_line_page (struct import_assistant *ia)
813 struct first_line_page *p = &ia->first_line;
814 GladeXML *xml = ia->asst.xml;
816 p->page = add_page_to_assistant (ia, get_widget_assert (xml, "FirstLine"),
817 GTK_ASSISTANT_PAGE_CONTENT);
818 gtk_widget_destroy (get_widget_assert (xml, "first-line"));
819 p->tree_view = create_lines_tree_view (
820 GTK_CONTAINER (get_widget_assert (xml, "first-line-scroller")), ia);
821 p->variable_names_cb = get_widget_assert (xml, "variable-names");
822 gtk_tree_selection_set_mode (
823 gtk_tree_view_get_selection (GTK_TREE_VIEW (p->tree_view)),
824 GTK_SELECTION_BROWSE);
826 g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (p->tree_view)),
827 "changed", G_CALLBACK (on_first_line_change), ia);
828 g_signal_connect (p->variable_names_cb, "toggled",
829 G_CALLBACK (on_variable_names_cb_toggle), ia);
832 /* Resets the first_line page to its initial content. */
834 reset_first_line_page (struct import_assistant *ia)
836 ia->first_line.skip_lines = 0;
837 ia->first_line.variable_names = false;
841 /* Creates and returns a tree view that contains each of the
842 lines in IA's file as a row. */
844 create_lines_tree_view (GtkContainer *parent, struct import_assistant *ia)
846 GtkTreeView *tree_view;
847 GtkTreeViewColumn *column;
848 size_t max_line_length;
849 gint content_width, header_width;
852 make_tree_view (ia, 0, &tree_view);
854 column = gtk_tree_view_column_new_with_attributes (
855 "Text", ia->asst.fixed_renderer,
856 "text", TEXT_IMPORT_MODEL_COLUMN_LINE,
858 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
861 for (i = 0; i < ia->file.line_cnt; i++)
863 size_t w = ds_length (&ia->file.lines[i]);
864 max_line_length = MAX (max_line_length, w);
867 content_width = get_monospace_width (tree_view, ia->asst.fixed_renderer,
869 header_width = get_string_width (tree_view, ia->asst.prop_renderer, "Text");
870 gtk_tree_view_column_set_fixed_width (column, MAX (content_width,
872 gtk_tree_view_append_column (tree_view, column);
874 gtk_tree_view_set_fixed_height_mode (tree_view, true);
876 gtk_container_add (parent, GTK_WIDGET (tree_view));
877 gtk_widget_show (GTK_WIDGET (tree_view));
882 /* Called when the line selected in the first_line tree view
885 on_first_line_change (GtkTreeSelection *selection UNUSED,
886 struct import_assistant *ia)
891 /* Called when the checkbox that indicates whether variable
892 names are in the row above the first line is toggled. */
894 on_variable_names_cb_toggle (GtkToggleButton *variable_names_cb UNUSED,
895 struct import_assistant *ia)
900 /* Sets the widgets to match IA's first_line substructure. */
902 set_first_line (struct import_assistant *ia)
906 path = gtk_tree_path_new_from_indices (ia->first_line.skip_lines, -1);
907 gtk_tree_view_set_cursor (GTK_TREE_VIEW (ia->first_line.tree_view),
909 gtk_tree_path_free (path);
911 gtk_toggle_button_set_active (
912 GTK_TOGGLE_BUTTON (ia->first_line.variable_names_cb),
913 ia->first_line.variable_names);
914 gtk_widget_set_sensitive (ia->first_line.variable_names_cb,
915 ia->first_line.skip_lines > 0);
918 /* Sets IA's first_line substructure to match the widgets. */
920 get_first_line (struct import_assistant *ia)
922 GtkTreeSelection *selection;
926 selection = gtk_tree_view_get_selection (ia->first_line.tree_view);
927 if (gtk_tree_selection_get_selected (selection, &model, &iter))
929 GtkTreePath *path = gtk_tree_model_get_path (model, &iter);
930 int row = gtk_tree_path_get_indices (path)[0];
931 gtk_tree_path_free (path);
933 ia->first_line.skip_lines = row;
934 ia->first_line.variable_names =
935 (ia->first_line.skip_lines > 0
936 && gtk_toggle_button_get_active (
937 GTK_TOGGLE_BUTTON (ia->first_line.variable_names_cb)));
939 gtk_widget_set_sensitive (ia->first_line.variable_names_cb,
940 ia->first_line.skip_lines > 0);
943 /* The "separators" page of the assistant. */
945 static void revise_fields_preview (struct import_assistant *ia);
946 static void choose_likely_separators (struct import_assistant *ia);
947 static void find_commonest_chars (unsigned long int histogram[UCHAR_MAX + 1],
948 const char *targets, const char *def,
949 struct string *result);
950 static void clear_fields (struct import_assistant *ia);
951 static void revise_fields_preview (struct import_assistant *);
952 static void set_separators (struct import_assistant *);
953 static void get_separators (struct import_assistant *);
954 static void on_separators_custom_entry_notify (GObject *UNUSED,
956 struct import_assistant *);
957 static void on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
958 struct import_assistant *);
959 static void on_quote_combo_change (GtkComboBox *combo,
960 struct import_assistant *);
961 static void on_quote_cb_toggle (GtkToggleButton *quote_cb,
962 struct import_assistant *);
963 static void on_separator_toggle (GtkToggleButton *, struct import_assistant *);
964 static void render_input_cell (GtkTreeViewColumn *tree_column,
965 GtkCellRenderer *cell,
966 GtkTreeModel *model, GtkTreeIter *iter,
968 static gboolean on_query_input_tooltip (GtkWidget *widget, gint wx, gint wy,
969 gboolean keyboard_mode UNUSED,
971 struct import_assistant *);
973 /* A common field separator and its identifying name. */
976 const char *name; /* Name (for use with get_widget_assert). */
977 int c; /* Separator character. */
980 /* All the separators in the dialog box. */
981 static const struct separator separators[] =
993 #define SEPARATOR_CNT (sizeof separators / sizeof *separators)
995 /* Initializes IA's separators substructure. */
997 init_separators_page (struct import_assistant *ia)
999 GladeXML *xml = ia->asst.xml;
1000 struct separators_page *p = &ia->separators;
1003 choose_likely_separators (ia);
1005 p->page = add_page_to_assistant (ia, get_widget_assert (xml, "Separators"),
1006 GTK_ASSISTANT_PAGE_CONTENT);
1007 p->custom_cb = get_widget_assert (xml, "custom-cb");
1008 p->custom_entry = get_widget_assert (xml, "custom-entry");
1009 p->quote_combo = get_widget_assert (xml, "quote-combo");
1010 p->quote_entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (p->quote_combo)));
1011 p->quote_cb = get_widget_assert (xml, "quote-cb");
1012 p->escape_cb = get_widget_assert (xml, "escape");
1014 set_separators (ia);
1015 p->fields_tree_view = GTK_TREE_VIEW (get_widget_assert (xml, "fields"));
1016 g_signal_connect (GTK_COMBO_BOX (p->quote_combo), "changed",
1017 G_CALLBACK (on_quote_combo_change), ia);
1018 g_signal_connect (p->quote_cb, "toggled",
1019 G_CALLBACK (on_quote_cb_toggle), ia);
1020 g_signal_connect (GTK_ENTRY (p->custom_entry), "notify::text",
1021 G_CALLBACK (on_separators_custom_entry_notify), ia);
1022 g_signal_connect (p->custom_cb, "toggled",
1023 G_CALLBACK (on_separators_custom_cb_toggle), ia);
1024 for (i = 0; i < SEPARATOR_CNT; i++)
1025 g_signal_connect (get_widget_assert (xml, separators[i].name),
1026 "toggled", G_CALLBACK (on_separator_toggle), ia);
1027 g_signal_connect (p->escape_cb, "toggled",
1028 G_CALLBACK (on_separator_toggle), ia);
1031 /* Frees IA's separators substructure. */
1033 destroy_separators_page (struct import_assistant *ia)
1035 struct separators_page *s = &ia->separators;
1037 ds_destroy (&s->separators);
1038 ds_destroy (&s->quotes);
1042 /* Called just before the separators page becomes visible in the
1045 prepare_separators_page (struct import_assistant *ia)
1047 revise_fields_preview (ia);
1050 /* Called when the Reset button is clicked on the separators
1051 page, resets the separators to the defaults. */
1053 reset_separators_page (struct import_assistant *ia)
1055 choose_likely_separators (ia);
1056 set_separators (ia);
1059 /* Frees and clears the column data in IA's separators
1062 clear_fields (struct import_assistant *ia)
1064 struct separators_page *s = &ia->separators;
1066 if (s->column_cnt > 0)
1071 for (row = 0; row < ia->file.line_cnt; row++)
1073 const struct string *line = &ia->file.lines[row];
1074 const char *line_start = ds_data (line);
1075 const char *line_end = ds_end (line);
1077 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1079 char *s = ss_data (col->contents[row]);
1080 if (!(s >= line_start && s <= line_end))
1081 ss_dealloc (&col->contents[row]);
1085 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1088 free (col->contents);
1097 /* Breaks the file data in IA into columns based on the
1098 separators set in IA's separators substructure. */
1100 split_fields (struct import_assistant *ia)
1102 struct separators_page *s = &ia->separators;
1103 size_t columns_allocated;
1109 /* Is space in the set of separators? */
1110 space_sep = ss_find_char (ds_ss (&s->separators), ' ') != SIZE_MAX;
1112 /* Split all the lines, not just those from
1113 ia->first_line.skip_lines on, so that we split the line that
1114 contains variables names if ia->first_line.variable_names is
1116 columns_allocated = 0;
1117 for (row = 0; row < ia->file.line_cnt; row++)
1119 struct string *line = &ia->file.lines[row];
1120 struct substring text = ds_ss (line);
1123 for (column_idx = 0; ; column_idx++)
1125 struct substring field;
1126 struct column *column;
1129 ss_ltrim (&text, ss_cstr (" "));
1130 if (ss_is_empty (text))
1132 if (column_idx != 0)
1136 else if (!ds_is_empty (&s->quotes)
1137 && ds_find_char (&s->quotes, text.string[0]) != SIZE_MAX)
1139 int quote = ss_get_char (&text);
1141 ss_get_until (&text, quote, &field);
1148 while ((c = ss_get_char (&text)) != EOF)
1150 ds_put_char (&s, c);
1151 else if (ss_match_char (&text, quote))
1152 ds_put_char (&s, quote);
1159 ss_get_chars (&text, ss_cspan (text, ds_ss (&s->separators)),
1162 if (column_idx >= s->column_cnt)
1164 struct column *column;
1166 if (s->column_cnt >= columns_allocated)
1167 s->columns = x2nrealloc (s->columns, &columns_allocated,
1168 sizeof *s->columns);
1169 column = &s->columns[s->column_cnt++];
1170 column->name = NULL;
1172 column->contents = xcalloc (ia->file.line_cnt,
1173 sizeof *column->contents);
1175 column = &s->columns[column_idx];
1176 column->contents[row] = field;
1177 if (ss_length (field) > column->width)
1178 column->width = ss_length (field);
1181 ss_ltrim (&text, ss_cstr (" "));
1182 if (ss_is_empty (text))
1184 if (ss_find_char (ds_ss (&s->separators), ss_first (text))
1186 ss_advance (&text, 1);
1191 /* Chooses a name for each column on the separators page */
1193 choose_column_names (struct import_assistant *ia)
1195 const struct first_line_page *f = &ia->first_line;
1196 struct separators_page *s = &ia->separators;
1197 struct dictionary *dict;
1198 unsigned long int generated_name_count = 0;
1202 dict = dict_create ();
1203 name_row = f->variable_names && f->skip_lines ? f->skip_lines : 0;
1204 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1206 char name[VAR_NAME_LEN + 1];
1209 hint = name_row ? ss_xstrdup (col->contents[name_row - 1]) : NULL;
1210 if (!dict_make_unique_var_name (dict, hint, &generated_name_count, name))
1214 col->name = xstrdup (name);
1215 dict_create_var_assert (dict, name, 0);
1217 dict_destroy (dict);
1220 /* Picks the most likely separator and quote characters based on
1223 choose_likely_separators (struct import_assistant *ia)
1225 unsigned long int histogram[UCHAR_MAX + 1] = { 0 };
1228 /* Construct a histogram of all the characters used in the
1230 for (row = 0; row < ia->file.line_cnt; row++)
1232 struct substring line = ds_ss (&ia->file.lines[row]);
1233 size_t length = ss_length (line);
1235 for (i = 0; i < length; i++)
1236 histogram[(unsigned char) line.string[i]]++;
1239 find_commonest_chars (histogram, "\"'", "", &ia->separators.quotes);
1240 find_commonest_chars (histogram, ",;:/|!\t-", ",",
1241 &ia->separators.separators);
1242 ia->separators.escape = true;
1245 /* Chooses the most common character among those in TARGETS,
1246 based on the frequency data in HISTOGRAM, and stores it in
1247 RESULT. If there is a tie for the most common character among
1248 those in TARGETS, the earliest character is chosen. If none
1249 of the TARGETS appear at all, then DEF is used as a
1252 find_commonest_chars (unsigned long int histogram[UCHAR_MAX + 1],
1253 const char *targets, const char *def,
1254 struct string *result)
1256 unsigned char max = 0;
1257 unsigned long int max_count = 0;
1259 for (; *targets != '\0'; targets++)
1261 unsigned char c = *targets;
1262 unsigned long int count = histogram[c];
1263 if (count > max_count)
1272 ds_put_char (result, max);
1275 ds_assign_cstr (result, def);
1278 /* Revises the contents of the fields tree view based on the
1279 currently chosen set of separators. */
1281 revise_fields_preview (struct import_assistant *ia)
1285 push_watch_cursor (ia);
1287 w = GTK_WIDGET (ia->separators.fields_tree_view);
1288 gtk_widget_destroy (w);
1289 get_separators (ia);
1291 choose_column_names (ia);
1292 ia->separators.fields_tree_view = create_data_tree_view (
1294 GTK_CONTAINER (get_widget_assert (ia->asst.xml, "fields-scroller")),
1297 pop_watch_cursor (ia);
1300 /* Sets the widgets to match IA's separators substructure. */
1302 set_separators (struct import_assistant *ia)
1304 struct separators_page *s = &ia->separators;
1306 struct string custom;
1311 ds_init_empty (&custom);
1313 for (i = 0; i < ds_length (&s->separators); i++)
1315 unsigned char c = ds_at (&s->separators, i);
1318 for (j = 0; j < SEPARATOR_CNT; j++)
1320 const struct separator *s = &separators[j];
1328 ds_put_char (&custom, c);
1332 for (i = 0; i < SEPARATOR_CNT; i++)
1334 const struct separator *s = &separators[i];
1335 GtkWidget *button = get_widget_assert (ia->asst.xml, s->name);
1336 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
1337 (seps & (1u << i)) != 0);
1339 any_custom = !ds_is_empty (&custom);
1340 gtk_entry_set_text (GTK_ENTRY (s->custom_entry), ds_cstr (&custom));
1341 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->custom_cb),
1343 gtk_widget_set_sensitive (s->custom_entry, any_custom);
1344 ds_destroy (&custom);
1346 any_quotes = !ds_is_empty (&s->quotes);
1347 gtk_entry_set_text (s->quote_entry,
1348 any_quotes ? ds_cstr (&s->quotes) : "\"");
1349 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->quote_cb),
1351 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->escape_cb),
1353 gtk_widget_set_sensitive (s->quote_combo, any_quotes);
1354 gtk_widget_set_sensitive (s->escape_cb, any_quotes);
1357 /* Sets IA's separators substructure to match the widgets. */
1359 get_separators (struct import_assistant *ia)
1361 struct separators_page *s = &ia->separators;
1364 ds_clear (&s->separators);
1365 for (i = 0; i < SEPARATOR_CNT; i++)
1367 const struct separator *sep = &separators[i];
1368 GtkWidget *button = get_widget_assert (ia->asst.xml, sep->name);
1369 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
1370 ds_put_char (&s->separators, sep->c);
1373 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->custom_cb)))
1374 ds_put_cstr (&s->separators,
1375 gtk_entry_get_text (GTK_ENTRY (s->custom_entry)));
1377 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->quote_cb)))
1379 gchar *text = gtk_combo_box_get_active_text (
1380 GTK_COMBO_BOX (s->quote_combo));
1381 ds_assign_cstr (&s->quotes, text);
1385 ds_clear (&s->quotes);
1386 s->escape = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->escape_cb));
1389 /* Called when the user changes the entry field for custom
1392 on_separators_custom_entry_notify (GObject *gobject UNUSED,
1393 GParamSpec *arg1 UNUSED,
1394 struct import_assistant *ia)
1396 revise_fields_preview (ia);
1399 /* Called when the user toggles the checkbox that enables custom
1402 on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
1403 struct import_assistant *ia)
1405 bool is_active = gtk_toggle_button_get_active (custom_cb);
1406 gtk_widget_set_sensitive (ia->separators.custom_entry, is_active);
1407 revise_fields_preview (ia);
1410 /* Called when the user changes the selection in the combo box
1411 that selects a quote character. */
1413 on_quote_combo_change (GtkComboBox *combo, struct import_assistant *ia)
1415 revise_fields_preview (ia);
1418 /* Called when the user toggles the checkbox that enables
1421 on_quote_cb_toggle (GtkToggleButton *quote_cb, struct import_assistant *ia)
1423 bool is_active = gtk_toggle_button_get_active (quote_cb);
1424 gtk_widget_set_sensitive (ia->separators.quote_combo, is_active);
1425 gtk_widget_set_sensitive (ia->separators.escape_cb, is_active);
1426 revise_fields_preview (ia);
1429 /* Called when the user toggles one of the separators
1432 on_separator_toggle (GtkToggleButton *toggle UNUSED,
1433 struct import_assistant *ia)
1435 revise_fields_preview (ia);
1438 /* Called to render one of the cells in the fields preview tree
1441 render_input_cell (GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1442 GtkTreeModel *model, GtkTreeIter *iter,
1445 struct import_assistant *ia = ia_;
1446 struct substring field;
1450 column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1452 row = text_import_model_iter_to_row (iter) + ia->first_line.skip_lines;
1453 field = ia->separators.columns[column].contents[row];
1454 if (field.string != NULL)
1456 GValue text = {0, };
1457 g_value_init (&text, G_TYPE_STRING);
1458 g_value_take_string (&text, ss_xstrdup (field));
1459 g_object_set_property (G_OBJECT (cell), "text", &text);
1460 g_value_unset (&text);
1461 g_object_set (cell, "background-set", FALSE, (void *) NULL);
1466 "background", "red",
1467 "background-set", TRUE,
1471 /* Called to render a tooltip on one of the cells in the fields
1472 preview tree view. */
1474 on_query_input_tooltip (GtkWidget *widget, gint wx, gint wy,
1475 gboolean keyboard_mode UNUSED,
1476 GtkTooltip *tooltip, struct import_assistant *ia)
1480 if (!get_tooltip_location (widget, wx, wy, ia, &row, &column))
1483 if (ia->separators.columns[column].contents[row].string != NULL)
1486 gtk_tooltip_set_text (tooltip,
1487 _("This input line has too few separators "
1488 "to fill in this field."));
1492 /* The "formats" page of the assistant. */
1494 static void on_variable_change (PsppireDict *dict, int idx,
1495 struct import_assistant *);
1496 static void clear_modified_vars (struct import_assistant *);
1498 /* Initializes IA's formats substructure. */
1500 init_formats_page (struct import_assistant *ia)
1502 GladeXML *xml = ia->asst.xml;
1503 struct formats_page *p = &ia->formats;
1505 p->page = add_page_to_assistant (ia, get_widget_assert (xml, "Formats"),
1506 GTK_ASSISTANT_PAGE_CONFIRM);
1507 p->data_tree_view = GTK_TREE_VIEW (get_widget_assert (xml, "data"));
1508 p->modified_vars = NULL;
1509 p->modified_var_cnt = 0;
1512 /* Frees IA's formats substructure. */
1514 destroy_formats_page (struct import_assistant *ia)
1516 struct formats_page *p = &ia->formats;
1518 if (p->psppire_dict != NULL)
1520 /* This destroys p->dict also. */
1521 g_object_unref (p->psppire_dict);
1523 clear_modified_vars (ia);
1526 /* Called just before the formats page of the assistant is
1529 prepare_formats_page (struct import_assistant *ia)
1531 struct dictionary *dict;
1532 PsppireDict *psppire_dict;
1533 PsppireVarStore *var_store;
1534 GtkBin *vars_scroller;
1535 GtkWidget *old_var_sheet;
1536 PsppireVarSheet *var_sheet;
1537 struct separators_page *s = &ia->separators;
1538 struct formats_page *p = &ia->formats;
1539 struct fmt_guesser *fg;
1540 unsigned long int number = 0;
1543 push_watch_cursor (ia);
1545 dict = dict_create ();
1546 fg = fmt_guesser_create ();
1547 for (column_idx = 0; column_idx < s->column_cnt; column_idx++)
1549 struct variable *modified_var;
1550 char name[VAR_NAME_LEN + 1];
1552 modified_var = (column_idx < p->modified_var_cnt
1553 ? p->modified_vars[column_idx] : NULL);
1554 if (modified_var == NULL)
1556 struct column *column = &s->columns[column_idx];
1557 struct variable *var;
1558 struct fmt_spec format;
1561 /* Choose variable name. */
1562 if (!dict_make_unique_var_name (dict, column->name, &number, name))
1565 /* Choose variable format. */
1566 fmt_guesser_clear (fg);
1567 for (row = ia->first_line.skip_lines; row < ia->file.line_cnt; row++)
1568 fmt_guesser_add (fg, column->contents[row]);
1569 fmt_guesser_guess (fg, &format);
1570 fmt_fix_input (&format);
1572 /* Create variable. */
1573 var = dict_create_var_assert (dict, name, fmt_var_width (&format));
1574 var_set_both_formats (var, &format);
1578 if (!dict_make_unique_var_name (dict, var_get_name (modified_var),
1581 dict_clone_var_assert (dict, modified_var, name);
1584 fmt_guesser_destroy (fg);
1586 psppire_dict = psppire_dict_new_from_dict (dict);
1587 g_signal_connect (psppire_dict, "variable_changed",
1588 G_CALLBACK (on_variable_change), ia);
1589 ia->formats.dict = dict;
1590 ia->formats.psppire_dict = psppire_dict;
1592 /* XXX: PsppireVarStore doesn't hold a reference to
1593 psppire_dict for now, but it should. After it does, we
1594 should g_object_ref the psppire_dict here, since we also
1595 hold a reference via ia->formats.dict. */
1596 var_store = psppire_var_store_new (psppire_dict);
1597 g_object_set (var_store,
1599 "format-type", PSPPIRE_VAR_STORE_INPUT_FORMATS,
1601 var_sheet = PSPPIRE_VAR_SHEET (psppire_var_sheet_new ());
1602 g_object_set (var_sheet,
1603 "row-geometry", var_store,
1605 "may-create-vars", FALSE,
1608 vars_scroller = GTK_BIN (get_widget_assert (ia->asst.xml, "vars-scroller"));
1609 old_var_sheet = gtk_bin_get_child (vars_scroller);
1610 if (old_var_sheet != NULL)
1611 gtk_widget_destroy (old_var_sheet);
1612 gtk_container_add (GTK_CONTAINER (vars_scroller), GTK_WIDGET (var_sheet));
1613 gtk_widget_show (GTK_WIDGET (var_sheet));
1615 gtk_widget_destroy (GTK_WIDGET (ia->formats.data_tree_view));
1616 ia->formats.data_tree_view = create_data_tree_view (
1618 GTK_CONTAINER (get_widget_assert (ia->asst.xml, "data-scroller")),
1621 pop_watch_cursor (ia);
1624 /* Clears the set of user-modified variables from IA's formats
1625 substructure. This discards user modifications to variable
1626 formats, thereby causing formats to revert to their
1629 clear_modified_vars (struct import_assistant *ia)
1631 struct formats_page *p = &ia->formats;
1634 for (i = 0; i < p->modified_var_cnt; i++)
1635 var_destroy (p->modified_vars[i]);
1636 free (p->modified_vars);
1637 p->modified_vars = NULL;
1638 p->modified_var_cnt = 0;
1641 /* Resets the formats page to its defaults, discarding user
1644 reset_formats_page (struct import_assistant *ia)
1646 clear_modified_vars (ia);
1647 prepare_formats_page (ia);
1650 /* Called when the user changes one of the variables in the
1653 on_variable_change (PsppireDict *dict, int dict_idx,
1654 struct import_assistant *ia)
1656 struct formats_page *p = &ia->formats;
1657 GtkTreeView *tv = ia->formats.data_tree_view;
1658 gint column_idx = dict_idx + 1;
1660 push_watch_cursor (ia);
1662 /* Remove previous column and replace with new column. */
1663 gtk_tree_view_remove_column (tv, gtk_tree_view_get_column (tv, column_idx));
1664 gtk_tree_view_insert_column (tv, make_data_column (ia, tv, false, dict_idx),
1667 /* Save a copy of the modified variable in modified_vars, so
1668 that its attributes will be preserved if we back up to the
1669 previous page with the Prev button and then come back
1671 if (dict_idx >= p->modified_var_cnt)
1674 p->modified_vars = xnrealloc (p->modified_vars, dict_idx + 1,
1675 sizeof *p->modified_vars);
1676 for (i = 0; i <= dict_idx; i++)
1677 p->modified_vars[i] = NULL;
1678 p->modified_var_cnt = dict_idx + 1;
1680 if (p->modified_vars[dict_idx])
1681 var_destroy (p->modified_vars[dict_idx]);
1682 p->modified_vars[dict_idx]
1683 = var_clone (psppire_dict_get_variable (dict, dict_idx));
1685 pop_watch_cursor (ia);
1688 /* Parses the contents of the field at (ROW,COLUMN) according to
1689 its variable format. If OUTPUTP is non-null, then *OUTPUTP
1690 receives the formatted output for that field (which must be
1691 freed with free). If TOOLTIPP is non-null, then *TOOLTIPP
1692 receives a message suitable for use in a tooltip, if one is
1693 needed, or a null pointer otherwise. Returns true if a
1694 tooltip message is needed, otherwise false. */
1696 parse_field (struct import_assistant *ia,
1697 size_t row, size_t column,
1698 char **outputp, char **tooltipp)
1700 struct substring field;
1702 struct variable *var;
1703 const struct fmt_spec *in;
1704 struct fmt_spec out;
1708 field = ia->separators.columns[column].contents[row];
1709 var = dict_get_var (ia->formats.dict, column);
1710 val = value_create (var_get_width (var));
1711 in = var_get_print_format (var);
1712 out = fmt_for_output_from_input (in);
1714 if (field.string != NULL)
1717 if (!data_in (field, LEGACY_NATIVE, in->type, 0, 0, 0,
1718 val, var_get_width (var)))
1720 char fmt_string[FMT_STRING_LEN_MAX + 1];
1721 fmt_to_string (in, fmt_string);
1722 tooltip = xasprintf (_("Field content \"%.*s\" cannot be parsed in "
1724 (int) field.length, field.string,
1731 tooltip = xstrdup (_("This input line has too few separators "
1732 "to fill in this field."));
1733 value_set_missing (val, var_get_width (var));
1735 if (outputp != NULL)
1737 char *output = xmalloc (out.w + 1);
1738 data_out (val, &out, output);
1739 output[out.w] = '\0';
1744 ok = tooltip == NULL;
1745 if (tooltipp != NULL)
1746 *tooltipp = tooltip;
1752 /* Called to render one of the cells in the data preview tree
1755 render_output_cell (GtkTreeViewColumn *tree_column,
1756 GtkCellRenderer *cell,
1757 GtkTreeModel *model,
1761 struct import_assistant *ia = ia_;
1763 GValue gvalue = { 0, };
1766 ok = parse_field (ia,
1767 (text_import_model_iter_to_row (iter)
1768 + ia->first_line.skip_lines),
1769 GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1773 g_value_init (&gvalue, G_TYPE_STRING);
1774 g_value_take_string (&gvalue, output);
1775 g_object_set_property (G_OBJECT (cell), "text", &gvalue);
1776 g_value_unset (&gvalue);
1779 g_object_set (cell, "background-set", FALSE, (void *) NULL);
1782 "background", "red",
1783 "background-set", TRUE,
1787 /* Called to render a tooltip for one of the cells in the data
1788 preview tree view. */
1790 on_query_output_tooltip (GtkWidget *widget, gint wx, gint wy,
1791 gboolean keyboard_mode UNUSED,
1792 GtkTooltip *tooltip, struct import_assistant *ia)
1797 if (!get_tooltip_location (widget, wx, wy, ia, &row, &column))
1800 if (parse_field (ia, row, column, NULL, &text))
1803 gtk_tooltip_set_text (tooltip, text);
1808 /* Utility functions used by multiple pages of the assistant. */
1811 get_tooltip_location (GtkWidget *widget, gint wx, gint wy,
1812 const struct import_assistant *ia,
1813 size_t *row, size_t *column)
1815 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
1819 GtkTreeViewColumn *tree_column;
1820 GtkTreeModel *tree_model;
1823 /* Check that WIDGET is really visible on the screen before we
1824 do anything else. This is a bug fix for a sticky situation:
1825 when text_data_import_assistant() returns, it frees the data
1826 necessary to compose the tool tip message, but there may be
1827 a tool tip under preparation at that point (even if there is
1828 no visible tool tip) that will call back into us a little
1829 bit later. Perhaps the correct solution to this problem is
1830 to make the data related to the tool tips part of a GObject
1831 that only gets destroyed when all references are released,
1832 but this solution appears to be effective too. */
1833 if (!GTK_WIDGET_MAPPED (widget))
1836 gtk_tree_view_convert_widget_to_bin_window_coords (tree_view,
1838 if (!gtk_tree_view_get_path_at_pos (tree_view, bx, by,
1839 &path, &tree_column, NULL, NULL))
1842 *column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1845 tree_model = gtk_tree_view_get_model (tree_view);
1846 ok = gtk_tree_model_get_iter (tree_model, &iter, path);
1847 gtk_tree_path_free (path);
1851 *row = text_import_model_iter_to_row (&iter) + ia->first_line.skip_lines;
1856 make_tree_view (const struct import_assistant *ia,
1858 GtkTreeView **tree_view)
1860 GtkTreeModel *model;
1862 *tree_view = GTK_TREE_VIEW (gtk_tree_view_new ());
1863 model = GTK_TREE_MODEL (text_import_model_new (
1864 ia->file.lines + first_line,
1865 ia->file.line_cnt - first_line, first_line));
1866 gtk_tree_view_set_model (*tree_view, model);
1868 add_line_number_column (ia, *tree_view);
1872 add_line_number_column (const struct import_assistant *ia,
1873 GtkTreeView *treeview)
1875 GtkTreeViewColumn *column;
1877 column = gtk_tree_view_column_new_with_attributes (
1878 "Line", ia->asst.prop_renderer,
1879 "text", TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER,
1881 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
1882 gtk_tree_view_column_set_fixed_width (
1883 column, get_monospace_width (treeview, ia->asst.prop_renderer, 5));
1884 gtk_tree_view_append_column (treeview, column);
1888 get_monospace_width (GtkTreeView *treeview, GtkCellRenderer *renderer,
1895 ds_put_char_multiple (&s, '0', char_cnt);
1896 ds_put_char (&s, ' ');
1897 width = get_string_width (treeview, renderer, ds_cstr (&s));
1904 get_string_width (GtkTreeView *treeview, GtkCellRenderer *renderer,
1908 g_object_set (G_OBJECT (renderer), "text", string, (void *) NULL);
1909 gtk_cell_renderer_get_size (renderer, GTK_WIDGET (treeview),
1910 NULL, NULL, NULL, &width, NULL);
1914 static GtkTreeViewColumn *
1915 make_data_column (struct import_assistant *ia, GtkTreeView *tree_view,
1916 bool input, gint dict_idx)
1918 struct variable *var = NULL;
1919 struct column *column = NULL;
1920 char name[(VAR_NAME_LEN * 2) + 1];
1922 gint content_width, header_width;
1923 GtkTreeViewColumn *tree_column;
1926 column = &ia->separators.columns[dict_idx];
1928 var = dict_get_var (ia->formats.dict, dict_idx);
1930 escape_underscores (input ? column->name : var_get_name (var), name);
1931 char_cnt = input ? column->width : var_get_print_format (var)->w;
1932 content_width = get_monospace_width (tree_view, ia->asst.fixed_renderer,
1934 header_width = get_string_width (tree_view, ia->asst.prop_renderer,
1937 tree_column = gtk_tree_view_column_new ();
1938 g_object_set_data (G_OBJECT (tree_column), "column-number",
1939 GINT_TO_POINTER (dict_idx));
1940 gtk_tree_view_column_set_title (tree_column, name);
1941 gtk_tree_view_column_pack_start (tree_column, ia->asst.fixed_renderer,
1943 gtk_tree_view_column_set_cell_data_func (
1944 tree_column, ia->asst.fixed_renderer,
1945 input ? render_input_cell : render_output_cell, ia, NULL);
1946 gtk_tree_view_column_set_sizing (tree_column, GTK_TREE_VIEW_COLUMN_FIXED);
1947 gtk_tree_view_column_set_fixed_width (tree_column, MAX (content_width,
1953 static GtkTreeView *
1954 create_data_tree_view (bool input, GtkContainer *parent,
1955 struct import_assistant *ia)
1957 GtkTreeView *tree_view;
1960 make_tree_view (ia, ia->first_line.skip_lines, &tree_view);
1961 gtk_tree_selection_set_mode (gtk_tree_view_get_selection (tree_view),
1962 GTK_SELECTION_NONE);
1964 for (i = 0; i < ia->separators.column_cnt; i++)
1965 gtk_tree_view_append_column (tree_view,
1966 make_data_column (ia, tree_view, input, i));
1968 g_object_set (G_OBJECT (tree_view), "has-tooltip", TRUE, (void *) NULL);
1969 g_signal_connect (tree_view, "query-tooltip",
1970 G_CALLBACK (input ? on_query_input_tooltip
1971 : on_query_output_tooltip), ia);
1972 gtk_tree_view_set_fixed_height_mode (tree_view, true);
1974 gtk_container_add (parent, GTK_WIDGET (tree_view));
1975 gtk_widget_show (GTK_WIDGET (tree_view));
1981 escape_underscores (const char *in, char *out)
1983 for (; *in != '\0'; in++)
1992 /* TextImportModel, a GtkTreeModel implementation used by some
1993 pages of the assistant. */
1995 #define G_TYPE_TEXT_IMPORT_MODEL (text_import_model_get_type ())
1996 #define TEXT_IMPORT_MODEL(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), G_TYPE_TEXT_IMPORT_MODEL, TextImportModel))
1997 #define TEXT_IMPORT_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), G_TYPE_TEXT_IMPORT_MODEL, TextImportModelClass))
1998 #define IS_TEXT_IMPORT_MODEL(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), G_TYPE_TEXT_IMPORT_MODEL))
1999 #define IS_TEXT_IMPORT_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), G_TYPE_TEXT_IMPORT_MODEL))
2000 #define TEXT_IMPORT_MODEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), G_TYPE_TEXT_IMPORT_MODEL, TextImportModelClass))
2002 /* Random number used in 'stamp' member of GtkTreeIter. */
2003 #define TREE_MODEL_STAMP 0x7efd67d3
2005 struct TextImportModel
2008 struct string *lines;
2013 struct TextImportModelClass
2015 GObjectClass parent_class;
2018 GType text_import_model_get_type (void);
2019 static void text_import_model_tree_model_init (gpointer iface, gpointer data);
2022 text_import_model_get_type (void)
2024 static GType object_type = 0;
2028 static const GTypeInfo object_info = {
2029 sizeof (TextImportModelClass),
2030 (GBaseInitFunc) NULL,
2031 (GBaseFinalizeFunc) NULL,
2032 NULL, /* class_init */
2033 NULL, /* class_finalize */
2034 NULL, /* class_data */
2035 sizeof (TextImportModel),
2036 0, /* n_preallocs */
2037 NULL, /* instance_init */
2040 static const GInterfaceInfo tree_model_info = {
2041 text_import_model_tree_model_init,
2046 object_type = g_type_register_static (G_TYPE_OBJECT,
2050 g_type_add_interface_static (object_type, GTK_TYPE_TREE_MODEL,
2060 /* Creates and returns a new TextImportModel that contains the
2061 LINE_CNT lines in LINES. The lines before FIRST_LINE in LINES
2062 are not part of the model, but they are included in the line
2063 numbers in the TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER column.
2065 The caller retains responsibility for freeing LINES and must
2066 ensure that its lifetime and that of the strings that it
2067 contains exceeds that of the TextImportModel. */
2069 text_import_model_new (struct string *lines, size_t line_cnt,
2072 TextImportModel *new_text_import_model
2073 = g_object_new (G_TYPE_TEXT_IMPORT_MODEL, NULL);
2074 new_text_import_model->lines = lines;
2075 new_text_import_model->line_cnt = line_cnt;
2076 new_text_import_model->first_line = first_line;
2077 return new_text_import_model;
2082 tree_model_iter_has_child (GtkTreeModel *tree_model,
2089 tree_model_iter_parent (GtkTreeModel *tree_model,
2096 static GtkTreeModelFlags
2097 tree_model_get_flags (GtkTreeModel *model)
2099 g_return_val_if_fail (IS_TEXT_IMPORT_MODEL (model), (GtkTreeModelFlags) 0);
2101 return GTK_TREE_MODEL_LIST_ONLY | GTK_TREE_MODEL_ITERS_PERSIST;
2106 tree_model_n_columns (GtkTreeModel *model)
2112 tree_model_column_type (GtkTreeModel *model, gint index)
2114 return (index == TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER ? G_TYPE_INT
2115 : index == TEXT_IMPORT_MODEL_COLUMN_LINE ? G_TYPE_STRING
2120 init_iter (TextImportModel *list, gint idx, GtkTreeIter *iter)
2122 if (idx < 0 || idx >= list->line_cnt)
2125 iter->user_data = GINT_TO_POINTER (-1);
2130 iter->stamp = TREE_MODEL_STAMP;
2131 iter->user_data = GINT_TO_POINTER (idx);
2137 tree_model_get_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreePath *path)
2139 gint *indices, depth;
2141 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2143 g_return_val_if_fail (path, FALSE);
2145 indices = gtk_tree_path_get_indices (path);
2146 depth = gtk_tree_path_get_depth (path);
2148 g_return_val_if_fail (depth == 1, FALSE);
2150 return init_iter (list, indices[0], iter);
2155 tree_model_iter_next (GtkTreeModel *model, GtkTreeIter *iter)
2157 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2160 assert (iter->stamp == TREE_MODEL_STAMP);
2162 idx = GPOINTER_TO_INT (iter->user_data);
2163 return init_iter (list, idx == -1 ? -1 : idx + 1, iter);
2166 static GtkTreePath *
2167 tree_model_get_path (GtkTreeModel *model, GtkTreeIter *iter)
2171 g_return_val_if_fail (iter->stamp == TREE_MODEL_STAMP, FALSE);
2173 path = gtk_tree_path_new ();
2174 gtk_tree_path_append_index (path, GPOINTER_TO_INT (iter->user_data));
2180 tree_model_get_value (GtkTreeModel *model, GtkTreeIter *iter,
2181 gint column, GValue *value)
2183 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2186 g_return_if_fail (iter->stamp == TREE_MODEL_STAMP);
2188 idx = GPOINTER_TO_INT (iter->user_data);
2189 assert (idx >= 0 && idx < list->line_cnt);
2193 g_value_init (value, G_TYPE_INT);
2194 g_value_set_int (value, idx + list->first_line + 1);
2198 g_value_init (value, G_TYPE_STRING);
2199 g_value_set_static_string (value, ds_cstr (&list->lines[idx]));
2204 tree_model_iter_children (GtkTreeModel *tree_model,
2206 GtkTreeIter *parent)
2212 tree_model_n_children (GtkTreeModel *model, GtkTreeIter *iter)
2214 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2216 return iter == NULL ? list->line_cnt : 0;
2220 tree_model_nth_child (GtkTreeModel *model, GtkTreeIter *iter,
2221 GtkTreeIter *parent, gint n)
2223 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2224 g_return_val_if_fail (IS_TEXT_IMPORT_MODEL (model), FALSE);
2228 return init_iter (list, n, iter);
2232 text_import_model_tree_model_init (gpointer iface_, gpointer data UNUSED)
2234 GtkTreeModelIface *iface = (GtkTreeModelIface *) iface_;
2236 iface->get_flags = tree_model_get_flags;
2237 iface->get_n_columns = tree_model_n_columns;
2238 iface->get_column_type = tree_model_column_type;
2239 iface->get_iter = tree_model_get_iter;
2240 iface->iter_next = tree_model_iter_next;
2241 iface->get_path = tree_model_get_path;
2242 iface->get_value = tree_model_get_value;
2244 iface->iter_children = tree_model_iter_children;
2245 iface->iter_has_child = tree_model_iter_has_child;
2246 iface->iter_n_children = tree_model_n_children;
2247 iface->iter_nth_child = tree_model_nth_child;
2248 iface->iter_parent = tree_model_iter_parent;
2252 text_import_model_iter_to_row (const GtkTreeIter *iter)
2254 assert (iter->stamp == TREE_MODEL_STAMP);
2255 return GPOINTER_TO_INT (iter->user_data);
2258 /* Increments the "watch cursor" level, setting the cursor for
2259 the assistant window to a watch face to indicate to the user
2260 that the ongoing operation may take some time. */
2262 push_watch_cursor (struct import_assistant *ia)
2264 if (++ia->asst.watch_cursor == 1)
2266 GtkWidget *widget = GTK_WIDGET (ia->asst.assistant);
2267 GdkCursor *cursor = gdk_cursor_new (GDK_WATCH);
2268 gdk_window_set_cursor (widget->window, cursor);
2269 gdk_cursor_unref (cursor);
2270 gdk_display_flush (gtk_widget_get_display (widget));
2274 /* Decrements the "watch cursor" level. If the level reaches
2275 zero, the cursor is reset to its default shape. */
2277 pop_watch_cursor (struct import_assistant *ia)
2279 if (--ia->asst.watch_cursor == 0)
2281 GtkWidget *widget = GTK_WIDGET (ia->asst.assistant);;
2282 gdk_window_set_cursor (widget->window, NULL);