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);
642 /* Called just before PAGE is displayed as the current page of
643 ASSISTANT, this updates IA content according to the new
646 on_prepare (GtkAssistant *assistant, GtkWidget *page,
647 struct import_assistant *ia)
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);
660 /* Make the user visit each page in the assistant once. Seems
661 like a reasonable user interface, plus visiting the final
662 page is what constructs the dictionary. */
663 gtk_assistant_set_page_complete (assistant, page, true);
666 /* Called when the Cancel button in the assistant is clicked. */
668 on_cancel (GtkAssistant *assistant, struct import_assistant *ia)
670 close_assistant (ia, GTK_RESPONSE_CANCEL);
673 /* Called when the Apply button on the last page of the assistant
676 on_close (GtkAssistant *assistant, struct import_assistant *ia)
678 close_assistant (ia, GTK_RESPONSE_APPLY);
681 /* Called when the Paste button on the last page of the assistant
684 on_paste (GtkButton *button, struct import_assistant *ia)
686 close_assistant (ia, PSPPIRE_RESPONSE_PASTE);
689 /* Called when the Reset button is clicked. */
691 on_reset (GtkButton *button, struct import_assistant *ia)
693 gint page_num = gtk_assistant_get_current_page (ia->asst.assistant);
694 GtkWidget *page = gtk_assistant_get_nth_page (ia->asst.assistant, page_num);
696 if (page == ia->intro.page)
697 reset_intro_page (ia);
698 else if (page == ia->first_line.page)
699 reset_first_line_page (ia);
700 else if (page == ia->separators.page)
701 reset_separators_page (ia);
702 else if (page == ia->formats.page)
703 reset_formats_page (ia);
706 /* Causes the assistant to close, returning RESPONSE for
707 interpretation by text_data_import_assistant. */
709 close_assistant (struct import_assistant *ia, int response)
711 ia->asst.response = response;
712 g_main_loop_quit (ia->asst.main_loop);
713 gtk_widget_hide (GTK_WIDGET (ia->asst.assistant));
716 /* The "intro" page of the assistant. */
718 static void on_intro_amount_changed (GtkToggleButton *button,
719 struct import_assistant *);
721 /* Initializes IA's intro substructure. */
723 init_intro_page (struct import_assistant *ia)
725 GladeXML *xml = ia->asst.xml;
726 struct intro_page *p = &ia->intro;
729 p->page = add_page_to_assistant (ia, get_widget_assert (xml, "Intro"),
730 GTK_ASSISTANT_PAGE_INTRO);
731 p->all_cases_button = get_widget_assert (xml, "import-all-cases");
732 p->n_cases_button = get_widget_assert (xml, "import-n-cases");
733 p->n_cases_spin = get_widget_assert (xml, "n-cases-spin");
734 p->percent_button = get_widget_assert (xml, "import-percent");
735 p->percent_spin = get_widget_assert (xml, "percent-spin");
736 g_signal_connect (p->all_cases_button, "toggled",
737 G_CALLBACK (on_intro_amount_changed), ia);
738 g_signal_connect (p->n_cases_button, "toggled",
739 G_CALLBACK (on_intro_amount_changed), ia);
740 g_signal_connect (p->percent_button, "toggled",
741 G_CALLBACK (on_intro_amount_changed), ia);
744 ds_put_cstr (&s, _("This assistant will guide you through the process of "
745 "importing data into PSPP from a text file with one line "
746 "per case, in which fields are separated by tabs, "
747 "commas, or other delimiters.\n\n"));
748 if (ia->file.total_is_exact)
750 &s, ngettext ("The selected file contains %zu line of text. ",
751 "The selected file contains %zu lines of text. ",
754 else if (ia->file.total_lines > 0)
758 "The selected file contains approximately %lu line of text. ",
759 "The selected file contains approximately %lu lines of text. ",
760 ia->file.total_lines),
761 ia->file.total_lines);
764 "Only the first %zu line of the file will be shown for "
765 "preview purposes in the following screens. ",
766 "Only the first %zu lines of the file will be shown for "
767 "preview purposes in the following screens. ",
771 ds_put_cstr (&s, _("You may choose below how much of the file should "
772 "actually be imported."));
773 gtk_label_set_text (GTK_LABEL (get_widget_assert (xml, "intro-label")),
778 /* Resets IA's intro page to its initial state. */
780 reset_intro_page (struct import_assistant *ia)
782 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->intro.all_cases_button),
786 /* Called when one of the radio buttons is clicked. */
788 on_intro_amount_changed (GtkToggleButton *button UNUSED,
789 struct import_assistant *ia)
791 struct intro_page *p = &ia->intro;
793 gtk_widget_set_sensitive (p->n_cases_spin,
794 gtk_toggle_button_get_active (
795 GTK_TOGGLE_BUTTON (p->n_cases_button)));
797 gtk_widget_set_sensitive (ia->intro.percent_spin,
798 gtk_toggle_button_get_active (
799 GTK_TOGGLE_BUTTON (p->percent_button)));
802 /* The "first line" page of the assistant. */
804 static GtkTreeView *create_lines_tree_view (GtkContainer *parent_window,
805 struct import_assistant *);
806 static void on_first_line_change (GtkTreeSelection *,
807 struct import_assistant *);
808 static void on_variable_names_cb_toggle (GtkToggleButton *,
809 struct import_assistant *);
810 static void set_first_line (struct import_assistant *);
811 static void get_first_line (struct import_assistant *);
813 /* Initializes IA's first_line substructure. */
815 init_first_line_page (struct import_assistant *ia)
817 struct first_line_page *p = &ia->first_line;
818 GladeXML *xml = ia->asst.xml;
820 p->page = add_page_to_assistant (ia, get_widget_assert (xml, "FirstLine"),
821 GTK_ASSISTANT_PAGE_CONTENT);
822 gtk_widget_destroy (get_widget_assert (xml, "first-line"));
823 p->tree_view = create_lines_tree_view (
824 GTK_CONTAINER (get_widget_assert (xml, "first-line-scroller")), ia);
825 p->variable_names_cb = get_widget_assert (xml, "variable-names");
826 gtk_tree_selection_set_mode (
827 gtk_tree_view_get_selection (GTK_TREE_VIEW (p->tree_view)),
828 GTK_SELECTION_BROWSE);
830 g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (p->tree_view)),
831 "changed", G_CALLBACK (on_first_line_change), ia);
832 g_signal_connect (p->variable_names_cb, "toggled",
833 G_CALLBACK (on_variable_names_cb_toggle), ia);
836 /* Resets the first_line page to its initial content. */
838 reset_first_line_page (struct import_assistant *ia)
840 ia->first_line.skip_lines = 0;
841 ia->first_line.variable_names = false;
845 /* Creates and returns a tree view that contains each of the
846 lines in IA's file as a row. */
848 create_lines_tree_view (GtkContainer *parent, struct import_assistant *ia)
850 GtkTreeView *tree_view;
851 GtkTreeViewColumn *column;
852 size_t max_line_length;
853 gint content_width, header_width;
856 make_tree_view (ia, 0, &tree_view);
858 column = gtk_tree_view_column_new_with_attributes (
859 "Text", ia->asst.fixed_renderer,
860 "text", TEXT_IMPORT_MODEL_COLUMN_LINE,
862 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
865 for (i = 0; i < ia->file.line_cnt; i++)
867 size_t w = ds_length (&ia->file.lines[i]);
868 max_line_length = MAX (max_line_length, w);
871 content_width = get_monospace_width (tree_view, ia->asst.fixed_renderer,
873 header_width = get_string_width (tree_view, ia->asst.prop_renderer, "Text");
874 gtk_tree_view_column_set_fixed_width (column, MAX (content_width,
876 gtk_tree_view_append_column (tree_view, column);
878 gtk_tree_view_set_fixed_height_mode (tree_view, true);
880 gtk_container_add (parent, GTK_WIDGET (tree_view));
881 gtk_widget_show (GTK_WIDGET (tree_view));
886 /* Called when the line selected in the first_line tree view
889 on_first_line_change (GtkTreeSelection *selection UNUSED,
890 struct import_assistant *ia)
895 /* Called when the checkbox that indicates whether variable
896 names are in the row above the first line is toggled. */
898 on_variable_names_cb_toggle (GtkToggleButton *variable_names_cb UNUSED,
899 struct import_assistant *ia)
904 /* Sets the widgets to match IA's first_line substructure. */
906 set_first_line (struct import_assistant *ia)
910 path = gtk_tree_path_new_from_indices (ia->first_line.skip_lines, -1);
911 gtk_tree_view_set_cursor (GTK_TREE_VIEW (ia->first_line.tree_view),
913 gtk_tree_path_free (path);
915 gtk_toggle_button_set_active (
916 GTK_TOGGLE_BUTTON (ia->first_line.variable_names_cb),
917 ia->first_line.variable_names);
918 gtk_widget_set_sensitive (ia->first_line.variable_names_cb,
919 ia->first_line.skip_lines > 0);
922 /* Sets IA's first_line substructure to match the widgets. */
924 get_first_line (struct import_assistant *ia)
926 GtkTreeSelection *selection;
930 selection = gtk_tree_view_get_selection (ia->first_line.tree_view);
931 if (gtk_tree_selection_get_selected (selection, &model, &iter))
933 GtkTreePath *path = gtk_tree_model_get_path (model, &iter);
934 int row = gtk_tree_path_get_indices (path)[0];
935 gtk_tree_path_free (path);
937 ia->first_line.skip_lines = row;
938 ia->first_line.variable_names =
939 (ia->first_line.skip_lines > 0
940 && gtk_toggle_button_get_active (
941 GTK_TOGGLE_BUTTON (ia->first_line.variable_names_cb)));
943 gtk_widget_set_sensitive (ia->first_line.variable_names_cb,
944 ia->first_line.skip_lines > 0);
947 /* The "separators" page of the assistant. */
949 static void revise_fields_preview (struct import_assistant *ia);
950 static void choose_likely_separators (struct import_assistant *ia);
951 static void find_commonest_chars (unsigned long int histogram[UCHAR_MAX + 1],
952 const char *targets, const char *def,
953 struct string *result);
954 static void clear_fields (struct import_assistant *ia);
955 static void revise_fields_preview (struct import_assistant *);
956 static void set_separators (struct import_assistant *);
957 static void get_separators (struct import_assistant *);
958 static void on_separators_custom_entry_notify (GObject *UNUSED,
960 struct import_assistant *);
961 static void on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
962 struct import_assistant *);
963 static void on_quote_combo_change (GtkComboBox *combo,
964 struct import_assistant *);
965 static void on_quote_cb_toggle (GtkToggleButton *quote_cb,
966 struct import_assistant *);
967 static void on_separator_toggle (GtkToggleButton *, struct import_assistant *);
968 static void render_input_cell (GtkTreeViewColumn *tree_column,
969 GtkCellRenderer *cell,
970 GtkTreeModel *model, GtkTreeIter *iter,
972 static gboolean on_query_input_tooltip (GtkWidget *widget, gint wx, gint wy,
973 gboolean keyboard_mode UNUSED,
975 struct import_assistant *);
977 /* A common field separator and its identifying name. */
980 const char *name; /* Name (for use with get_widget_assert). */
981 int c; /* Separator character. */
984 /* All the separators in the dialog box. */
985 static const struct separator separators[] =
997 #define SEPARATOR_CNT (sizeof separators / sizeof *separators)
999 /* Initializes IA's separators substructure. */
1001 init_separators_page (struct import_assistant *ia)
1003 GladeXML *xml = ia->asst.xml;
1004 struct separators_page *p = &ia->separators;
1007 choose_likely_separators (ia);
1009 p->page = add_page_to_assistant (ia, get_widget_assert (xml, "Separators"),
1010 GTK_ASSISTANT_PAGE_CONTENT);
1011 p->custom_cb = get_widget_assert (xml, "custom-cb");
1012 p->custom_entry = get_widget_assert (xml, "custom-entry");
1013 p->quote_combo = get_widget_assert (xml, "quote-combo");
1014 p->quote_entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (p->quote_combo)));
1015 p->quote_cb = get_widget_assert (xml, "quote-cb");
1016 p->escape_cb = get_widget_assert (xml, "escape");
1018 set_separators (ia);
1019 p->fields_tree_view = GTK_TREE_VIEW (get_widget_assert (xml, "fields"));
1020 g_signal_connect (GTK_COMBO_BOX (p->quote_combo), "changed",
1021 G_CALLBACK (on_quote_combo_change), ia);
1022 g_signal_connect (p->quote_cb, "toggled",
1023 G_CALLBACK (on_quote_cb_toggle), ia);
1024 g_signal_connect (GTK_ENTRY (p->custom_entry), "notify::text",
1025 G_CALLBACK (on_separators_custom_entry_notify), ia);
1026 g_signal_connect (p->custom_cb, "toggled",
1027 G_CALLBACK (on_separators_custom_cb_toggle), ia);
1028 for (i = 0; i < SEPARATOR_CNT; i++)
1029 g_signal_connect (get_widget_assert (xml, separators[i].name),
1030 "toggled", G_CALLBACK (on_separator_toggle), ia);
1031 g_signal_connect (p->escape_cb, "toggled",
1032 G_CALLBACK (on_separator_toggle), ia);
1035 /* Frees IA's separators substructure. */
1037 destroy_separators_page (struct import_assistant *ia)
1039 struct separators_page *s = &ia->separators;
1041 ds_destroy (&s->separators);
1042 ds_destroy (&s->quotes);
1046 /* Called just before the separators page becomes visible in the
1049 prepare_separators_page (struct import_assistant *ia)
1051 revise_fields_preview (ia);
1054 /* Called when the Reset button is clicked on the separators
1055 page, resets the separators to the defaults. */
1057 reset_separators_page (struct import_assistant *ia)
1059 choose_likely_separators (ia);
1060 set_separators (ia);
1063 /* Frees and clears the column data in IA's separators
1066 clear_fields (struct import_assistant *ia)
1068 struct separators_page *s = &ia->separators;
1070 if (s->column_cnt > 0)
1075 for (row = 0; row < ia->file.line_cnt; row++)
1077 const struct string *line = &ia->file.lines[row];
1078 const char *line_start = ds_data (line);
1079 const char *line_end = ds_end (line);
1081 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1083 char *s = ss_data (col->contents[row]);
1084 if (!(s >= line_start && s <= line_end))
1085 ss_dealloc (&col->contents[row]);
1089 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1092 free (col->contents);
1101 /* Breaks the file data in IA into columns based on the
1102 separators set in IA's separators substructure. */
1104 split_fields (struct import_assistant *ia)
1106 struct separators_page *s = &ia->separators;
1107 size_t columns_allocated;
1113 /* Is space in the set of separators? */
1114 space_sep = ss_find_char (ds_ss (&s->separators), ' ') != SIZE_MAX;
1116 /* Split all the lines, not just those from
1117 ia->first_line.skip_lines on, so that we split the line that
1118 contains variables names if ia->first_line.variable_names is
1120 columns_allocated = 0;
1121 for (row = 0; row < ia->file.line_cnt; row++)
1123 struct string *line = &ia->file.lines[row];
1124 struct substring text = ds_ss (line);
1127 for (column_idx = 0; ; column_idx++)
1129 struct substring field;
1130 struct column *column;
1133 ss_ltrim (&text, ss_cstr (" "));
1134 if (ss_is_empty (text))
1136 if (column_idx != 0)
1140 else if (!ds_is_empty (&s->quotes)
1141 && ds_find_char (&s->quotes, text.string[0]) != SIZE_MAX)
1143 int quote = ss_get_char (&text);
1145 ss_get_until (&text, quote, &field);
1152 while ((c = ss_get_char (&text)) != EOF)
1154 ds_put_char (&s, c);
1155 else if (ss_match_char (&text, quote))
1156 ds_put_char (&s, quote);
1163 ss_get_chars (&text, ss_cspan (text, ds_ss (&s->separators)),
1166 if (column_idx >= s->column_cnt)
1168 struct column *column;
1170 if (s->column_cnt >= columns_allocated)
1171 s->columns = x2nrealloc (s->columns, &columns_allocated,
1172 sizeof *s->columns);
1173 column = &s->columns[s->column_cnt++];
1174 column->name = NULL;
1176 column->contents = xcalloc (ia->file.line_cnt,
1177 sizeof *column->contents);
1179 column = &s->columns[column_idx];
1180 column->contents[row] = field;
1181 if (ss_length (field) > column->width)
1182 column->width = ss_length (field);
1185 ss_ltrim (&text, ss_cstr (" "));
1186 if (ss_is_empty (text))
1188 if (ss_find_char (ds_ss (&s->separators), ss_first (text))
1190 ss_advance (&text, 1);
1195 /* Chooses a name for each column on the separators page */
1197 choose_column_names (struct import_assistant *ia)
1199 const struct first_line_page *f = &ia->first_line;
1200 struct separators_page *s = &ia->separators;
1201 struct dictionary *dict;
1202 unsigned long int generated_name_count = 0;
1206 dict = dict_create ();
1207 name_row = f->variable_names && f->skip_lines ? f->skip_lines : 0;
1208 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1210 char name[VAR_NAME_LEN + 1];
1213 hint = name_row ? ss_xstrdup (col->contents[name_row - 1]) : NULL;
1214 if (!dict_make_unique_var_name (dict, hint, &generated_name_count, name))
1218 col->name = xstrdup (name);
1219 dict_create_var_assert (dict, name, 0);
1221 dict_destroy (dict);
1224 /* Picks the most likely separator and quote characters based on
1227 choose_likely_separators (struct import_assistant *ia)
1229 unsigned long int histogram[UCHAR_MAX + 1] = { 0 };
1232 /* Construct a histogram of all the characters used in the
1234 for (row = 0; row < ia->file.line_cnt; row++)
1236 struct substring line = ds_ss (&ia->file.lines[row]);
1237 size_t length = ss_length (line);
1239 for (i = 0; i < length; i++)
1240 histogram[(unsigned char) line.string[i]]++;
1243 find_commonest_chars (histogram, "\"'", "", &ia->separators.quotes);
1244 find_commonest_chars (histogram, ",;:/|!\t-", ",",
1245 &ia->separators.separators);
1246 ia->separators.escape = true;
1249 /* Chooses the most common character among those in TARGETS,
1250 based on the frequency data in HISTOGRAM, and stores it in
1251 RESULT. If there is a tie for the most common character among
1252 those in TARGETS, the earliest character is chosen. If none
1253 of the TARGETS appear at all, then DEF is used as a
1256 find_commonest_chars (unsigned long int histogram[UCHAR_MAX + 1],
1257 const char *targets, const char *def,
1258 struct string *result)
1260 unsigned char max = 0;
1261 unsigned long int max_count = 0;
1263 for (; *targets != '\0'; targets++)
1265 unsigned char c = *targets;
1266 unsigned long int count = histogram[c];
1267 if (count > max_count)
1276 ds_put_char (result, max);
1279 ds_assign_cstr (result, def);
1282 /* Revises the contents of the fields tree view based on the
1283 currently chosen set of separators. */
1285 revise_fields_preview (struct import_assistant *ia)
1289 push_watch_cursor (ia);
1291 w = GTK_WIDGET (ia->separators.fields_tree_view);
1292 gtk_widget_destroy (w);
1293 get_separators (ia);
1295 choose_column_names (ia);
1296 ia->separators.fields_tree_view = create_data_tree_view (
1298 GTK_CONTAINER (get_widget_assert (ia->asst.xml, "fields-scroller")),
1301 pop_watch_cursor (ia);
1304 /* Sets the widgets to match IA's separators substructure. */
1306 set_separators (struct import_assistant *ia)
1308 struct separators_page *s = &ia->separators;
1310 struct string custom;
1315 ds_init_empty (&custom);
1317 for (i = 0; i < ds_length (&s->separators); i++)
1319 unsigned char c = ds_at (&s->separators, i);
1322 for (j = 0; j < SEPARATOR_CNT; j++)
1324 const struct separator *s = &separators[j];
1332 ds_put_char (&custom, c);
1336 for (i = 0; i < SEPARATOR_CNT; i++)
1338 const struct separator *s = &separators[i];
1339 GtkWidget *button = get_widget_assert (ia->asst.xml, s->name);
1340 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
1341 (seps & (1u << i)) != 0);
1343 any_custom = !ds_is_empty (&custom);
1344 gtk_entry_set_text (GTK_ENTRY (s->custom_entry), ds_cstr (&custom));
1345 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->custom_cb),
1347 gtk_widget_set_sensitive (s->custom_entry, any_custom);
1348 ds_destroy (&custom);
1350 any_quotes = !ds_is_empty (&s->quotes);
1351 gtk_entry_set_text (s->quote_entry,
1352 any_quotes ? ds_cstr (&s->quotes) : "\"");
1353 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->quote_cb),
1355 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->escape_cb),
1357 gtk_widget_set_sensitive (s->quote_combo, any_quotes);
1358 gtk_widget_set_sensitive (s->escape_cb, any_quotes);
1361 /* Sets IA's separators substructure to match the widgets. */
1363 get_separators (struct import_assistant *ia)
1365 struct separators_page *s = &ia->separators;
1368 ds_clear (&s->separators);
1369 for (i = 0; i < SEPARATOR_CNT; i++)
1371 const struct separator *sep = &separators[i];
1372 GtkWidget *button = get_widget_assert (ia->asst.xml, sep->name);
1373 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
1374 ds_put_char (&s->separators, sep->c);
1377 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->custom_cb)))
1378 ds_put_cstr (&s->separators,
1379 gtk_entry_get_text (GTK_ENTRY (s->custom_entry)));
1381 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->quote_cb)))
1383 gchar *text = gtk_combo_box_get_active_text (
1384 GTK_COMBO_BOX (s->quote_combo));
1385 ds_assign_cstr (&s->quotes, text);
1389 ds_clear (&s->quotes);
1390 s->escape = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->escape_cb));
1393 /* Called when the user changes the entry field for custom
1396 on_separators_custom_entry_notify (GObject *gobject UNUSED,
1397 GParamSpec *arg1 UNUSED,
1398 struct import_assistant *ia)
1400 revise_fields_preview (ia);
1403 /* Called when the user toggles the checkbox that enables custom
1406 on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
1407 struct import_assistant *ia)
1409 bool is_active = gtk_toggle_button_get_active (custom_cb);
1410 gtk_widget_set_sensitive (ia->separators.custom_entry, is_active);
1411 revise_fields_preview (ia);
1414 /* Called when the user changes the selection in the combo box
1415 that selects a quote character. */
1417 on_quote_combo_change (GtkComboBox *combo, struct import_assistant *ia)
1419 revise_fields_preview (ia);
1422 /* Called when the user toggles the checkbox that enables
1425 on_quote_cb_toggle (GtkToggleButton *quote_cb, struct import_assistant *ia)
1427 bool is_active = gtk_toggle_button_get_active (quote_cb);
1428 gtk_widget_set_sensitive (ia->separators.quote_combo, is_active);
1429 gtk_widget_set_sensitive (ia->separators.escape_cb, is_active);
1430 revise_fields_preview (ia);
1433 /* Called when the user toggles one of the separators
1436 on_separator_toggle (GtkToggleButton *toggle UNUSED,
1437 struct import_assistant *ia)
1439 revise_fields_preview (ia);
1442 /* Called to render one of the cells in the fields preview tree
1445 render_input_cell (GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1446 GtkTreeModel *model, GtkTreeIter *iter,
1449 struct import_assistant *ia = ia_;
1450 struct substring field;
1454 column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1456 row = text_import_model_iter_to_row (iter) + ia->first_line.skip_lines;
1457 field = ia->separators.columns[column].contents[row];
1458 if (field.string != NULL)
1460 GValue text = {0, };
1461 g_value_init (&text, G_TYPE_STRING);
1462 g_value_take_string (&text, ss_xstrdup (field));
1463 g_object_set_property (G_OBJECT (cell), "text", &text);
1464 g_value_unset (&text);
1465 g_object_set (cell, "background-set", FALSE, (void *) NULL);
1470 "background", "red",
1471 "background-set", TRUE,
1475 /* Called to render a tooltip on one of the cells in the fields
1476 preview tree view. */
1478 on_query_input_tooltip (GtkWidget *widget, gint wx, gint wy,
1479 gboolean keyboard_mode UNUSED,
1480 GtkTooltip *tooltip, struct import_assistant *ia)
1484 if (!get_tooltip_location (widget, wx, wy, ia, &row, &column))
1487 if (ia->separators.columns[column].contents[row].string != NULL)
1490 gtk_tooltip_set_text (tooltip,
1491 _("This input line has too few separators "
1492 "to fill in this field."));
1496 /* The "formats" page of the assistant. */
1498 static void on_variable_change (PsppireDict *dict, int idx,
1499 struct import_assistant *);
1500 static void clear_modified_vars (struct import_assistant *);
1502 /* Initializes IA's formats substructure. */
1504 init_formats_page (struct import_assistant *ia)
1506 GladeXML *xml = ia->asst.xml;
1507 struct formats_page *p = &ia->formats;
1509 p->page = add_page_to_assistant (ia, get_widget_assert (xml, "Formats"),
1510 GTK_ASSISTANT_PAGE_CONFIRM);
1511 p->data_tree_view = GTK_TREE_VIEW (get_widget_assert (xml, "data"));
1512 p->modified_vars = NULL;
1513 p->modified_var_cnt = 0;
1516 /* Frees IA's formats substructure. */
1518 destroy_formats_page (struct import_assistant *ia)
1520 struct formats_page *p = &ia->formats;
1522 if (p->psppire_dict != NULL)
1524 /* This destroys p->dict also. */
1525 g_object_unref (p->psppire_dict);
1527 clear_modified_vars (ia);
1530 /* Called just before the formats page of the assistant is
1533 prepare_formats_page (struct import_assistant *ia)
1535 struct dictionary *dict;
1536 PsppireDict *psppire_dict;
1537 PsppireVarStore *var_store;
1538 GtkBin *vars_scroller;
1539 GtkWidget *old_var_sheet;
1540 PsppireVarSheet *var_sheet;
1541 struct separators_page *s = &ia->separators;
1542 struct formats_page *p = &ia->formats;
1543 struct fmt_guesser *fg;
1544 unsigned long int number = 0;
1547 push_watch_cursor (ia);
1549 dict = dict_create ();
1550 fg = fmt_guesser_create ();
1551 for (column_idx = 0; column_idx < s->column_cnt; column_idx++)
1553 struct variable *modified_var;
1554 char name[VAR_NAME_LEN + 1];
1556 modified_var = (column_idx < p->modified_var_cnt
1557 ? p->modified_vars[column_idx] : NULL);
1558 if (modified_var == NULL)
1560 struct column *column = &s->columns[column_idx];
1561 struct variable *var;
1562 struct fmt_spec format;
1565 /* Choose variable name. */
1566 if (!dict_make_unique_var_name (dict, column->name, &number, name))
1569 /* Choose variable format. */
1570 fmt_guesser_clear (fg);
1571 for (row = ia->first_line.skip_lines; row < ia->file.line_cnt; row++)
1572 fmt_guesser_add (fg, column->contents[row]);
1573 fmt_guesser_guess (fg, &format);
1574 fmt_fix_input (&format);
1576 /* Create variable. */
1577 var = dict_create_var_assert (dict, name, fmt_var_width (&format));
1578 var_set_both_formats (var, &format);
1582 if (!dict_make_unique_var_name (dict, var_get_name (modified_var),
1585 dict_clone_var_assert (dict, modified_var, name);
1588 fmt_guesser_destroy (fg);
1590 psppire_dict = psppire_dict_new_from_dict (dict);
1591 g_signal_connect (psppire_dict, "variable_changed",
1592 G_CALLBACK (on_variable_change), ia);
1593 ia->formats.dict = dict;
1594 ia->formats.psppire_dict = psppire_dict;
1596 /* XXX: PsppireVarStore doesn't hold a reference to
1597 psppire_dict for now, but it should. After it does, we
1598 should g_object_ref the psppire_dict here, since we also
1599 hold a reference via ia->formats.dict. */
1600 var_store = psppire_var_store_new (psppire_dict);
1601 g_object_set (var_store,
1603 "format-type", PSPPIRE_VAR_STORE_INPUT_FORMATS,
1605 var_sheet = PSPPIRE_VAR_SHEET (psppire_var_sheet_new ());
1606 g_object_set (var_sheet,
1607 "row-geometry", var_store,
1609 "may-create-vars", FALSE,
1612 vars_scroller = GTK_BIN (get_widget_assert (ia->asst.xml, "vars-scroller"));
1613 old_var_sheet = gtk_bin_get_child (vars_scroller);
1614 if (old_var_sheet != NULL)
1615 gtk_widget_destroy (old_var_sheet);
1616 gtk_container_add (GTK_CONTAINER (vars_scroller), GTK_WIDGET (var_sheet));
1617 gtk_widget_show (GTK_WIDGET (var_sheet));
1619 gtk_widget_destroy (GTK_WIDGET (ia->formats.data_tree_view));
1620 ia->formats.data_tree_view = create_data_tree_view (
1622 GTK_CONTAINER (get_widget_assert (ia->asst.xml, "data-scroller")),
1625 pop_watch_cursor (ia);
1628 /* Clears the set of user-modified variables from IA's formats
1629 substructure. This discards user modifications to variable
1630 formats, thereby causing formats to revert to their
1633 clear_modified_vars (struct import_assistant *ia)
1635 struct formats_page *p = &ia->formats;
1638 for (i = 0; i < p->modified_var_cnt; i++)
1639 var_destroy (p->modified_vars[i]);
1640 free (p->modified_vars);
1641 p->modified_vars = NULL;
1642 p->modified_var_cnt = 0;
1645 /* Resets the formats page to its defaults, discarding user
1648 reset_formats_page (struct import_assistant *ia)
1650 clear_modified_vars (ia);
1651 prepare_formats_page (ia);
1654 /* Called when the user changes one of the variables in the
1657 on_variable_change (PsppireDict *dict, int dict_idx,
1658 struct import_assistant *ia)
1660 struct formats_page *p = &ia->formats;
1661 GtkTreeView *tv = ia->formats.data_tree_view;
1662 gint column_idx = dict_idx + 1;
1664 push_watch_cursor (ia);
1666 /* Remove previous column and replace with new column. */
1667 gtk_tree_view_remove_column (tv, gtk_tree_view_get_column (tv, column_idx));
1668 gtk_tree_view_insert_column (tv, make_data_column (ia, tv, false, dict_idx),
1671 /* Save a copy of the modified variable in modified_vars, so
1672 that its attributes will be preserved if we back up to the
1673 previous page with the Prev button and then come back
1675 if (dict_idx >= p->modified_var_cnt)
1678 p->modified_vars = xnrealloc (p->modified_vars, dict_idx + 1,
1679 sizeof *p->modified_vars);
1680 for (i = 0; i <= dict_idx; i++)
1681 p->modified_vars[i] = NULL;
1682 p->modified_var_cnt = dict_idx + 1;
1684 if (p->modified_vars[dict_idx])
1685 var_destroy (p->modified_vars[dict_idx]);
1686 p->modified_vars[dict_idx]
1687 = var_clone (psppire_dict_get_variable (dict, dict_idx));
1689 pop_watch_cursor (ia);
1692 /* Parses the contents of the field at (ROW,COLUMN) according to
1693 its variable format. If OUTPUTP is non-null, then *OUTPUTP
1694 receives the formatted output for that field (which must be
1695 freed with free). If TOOLTIPP is non-null, then *TOOLTIPP
1696 receives a message suitable for use in a tooltip, if one is
1697 needed, or a null pointer otherwise. Returns true if a
1698 tooltip message is needed, otherwise false. */
1700 parse_field (struct import_assistant *ia,
1701 size_t row, size_t column,
1702 char **outputp, char **tooltipp)
1704 struct substring field;
1706 struct variable *var;
1707 const struct fmt_spec *in;
1708 struct fmt_spec out;
1712 field = ia->separators.columns[column].contents[row];
1713 var = dict_get_var (ia->formats.dict, column);
1714 val = value_create (var_get_width (var));
1715 in = var_get_print_format (var);
1716 out = fmt_for_output_from_input (in);
1718 if (field.string != NULL)
1721 if (!data_in (field, LEGACY_NATIVE, in->type, 0, 0, 0,
1722 val, var_get_width (var)))
1724 char fmt_string[FMT_STRING_LEN_MAX + 1];
1725 fmt_to_string (in, fmt_string);
1726 tooltip = xasprintf (_("Field content \"%.*s\" cannot be parsed in "
1728 (int) field.length, field.string,
1735 tooltip = xstrdup (_("This input line has too few separators "
1736 "to fill in this field."));
1737 value_set_missing (val, var_get_width (var));
1739 if (outputp != NULL)
1741 char *output = xmalloc (out.w + 1);
1742 data_out (val, &out, output);
1743 output[out.w] = '\0';
1748 ok = tooltip == NULL;
1749 if (tooltipp != NULL)
1750 *tooltipp = tooltip;
1756 /* Called to render one of the cells in the data preview tree
1759 render_output_cell (GtkTreeViewColumn *tree_column,
1760 GtkCellRenderer *cell,
1761 GtkTreeModel *model,
1765 struct import_assistant *ia = ia_;
1767 GValue gvalue = { 0, };
1770 ok = parse_field (ia,
1771 (text_import_model_iter_to_row (iter)
1772 + ia->first_line.skip_lines),
1773 GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1777 g_value_init (&gvalue, G_TYPE_STRING);
1778 g_value_take_string (&gvalue, output);
1779 g_object_set_property (G_OBJECT (cell), "text", &gvalue);
1780 g_value_unset (&gvalue);
1783 g_object_set (cell, "background-set", FALSE, (void *) NULL);
1786 "background", "red",
1787 "background-set", TRUE,
1791 /* Called to render a tooltip for one of the cells in the data
1792 preview tree view. */
1794 on_query_output_tooltip (GtkWidget *widget, gint wx, gint wy,
1795 gboolean keyboard_mode UNUSED,
1796 GtkTooltip *tooltip, struct import_assistant *ia)
1801 if (!get_tooltip_location (widget, wx, wy, ia, &row, &column))
1804 if (parse_field (ia, row, column, NULL, &text))
1807 gtk_tooltip_set_text (tooltip, text);
1812 /* Utility functions used by multiple pages of the assistant. */
1815 get_tooltip_location (GtkWidget *widget, gint wx, gint wy,
1816 const struct import_assistant *ia,
1817 size_t *row, size_t *column)
1819 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
1823 GtkTreeViewColumn *tree_column;
1824 GtkTreeModel *tree_model;
1827 /* Check that WIDGET is really visible on the screen before we
1828 do anything else. This is a bug fix for a sticky situation:
1829 when text_data_import_assistant() returns, it frees the data
1830 necessary to compose the tool tip message, but there may be
1831 a tool tip under preparation at that point (even if there is
1832 no visible tool tip) that will call back into us a little
1833 bit later. Perhaps the correct solution to this problem is
1834 to make the data related to the tool tips part of a GObject
1835 that only gets destroyed when all references are released,
1836 but this solution appears to be effective too. */
1837 if (!GTK_WIDGET_MAPPED (widget))
1840 gtk_tree_view_convert_widget_to_bin_window_coords (tree_view,
1842 if (!gtk_tree_view_get_path_at_pos (tree_view, bx, by,
1843 &path, &tree_column, NULL, NULL))
1846 *column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1849 tree_model = gtk_tree_view_get_model (tree_view);
1850 ok = gtk_tree_model_get_iter (tree_model, &iter, path);
1851 gtk_tree_path_free (path);
1855 *row = text_import_model_iter_to_row (&iter) + ia->first_line.skip_lines;
1860 make_tree_view (const struct import_assistant *ia,
1862 GtkTreeView **tree_view)
1864 GtkTreeModel *model;
1866 *tree_view = GTK_TREE_VIEW (gtk_tree_view_new ());
1867 model = GTK_TREE_MODEL (text_import_model_new (
1868 ia->file.lines + first_line,
1869 ia->file.line_cnt - first_line, first_line));
1870 gtk_tree_view_set_model (*tree_view, model);
1872 add_line_number_column (ia, *tree_view);
1876 add_line_number_column (const struct import_assistant *ia,
1877 GtkTreeView *treeview)
1879 GtkTreeViewColumn *column;
1881 column = gtk_tree_view_column_new_with_attributes (
1882 "Line", ia->asst.prop_renderer,
1883 "text", TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER,
1885 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
1886 gtk_tree_view_column_set_fixed_width (
1887 column, get_monospace_width (treeview, ia->asst.prop_renderer, 5));
1888 gtk_tree_view_append_column (treeview, column);
1892 get_monospace_width (GtkTreeView *treeview, GtkCellRenderer *renderer,
1899 ds_put_char_multiple (&s, '0', char_cnt);
1900 ds_put_char (&s, ' ');
1901 width = get_string_width (treeview, renderer, ds_cstr (&s));
1908 get_string_width (GtkTreeView *treeview, GtkCellRenderer *renderer,
1912 g_object_set (G_OBJECT (renderer), "text", string, (void *) NULL);
1913 gtk_cell_renderer_get_size (renderer, GTK_WIDGET (treeview),
1914 NULL, NULL, NULL, &width, NULL);
1918 static GtkTreeViewColumn *
1919 make_data_column (struct import_assistant *ia, GtkTreeView *tree_view,
1920 bool input, gint dict_idx)
1922 struct variable *var = NULL;
1923 struct column *column = NULL;
1924 char name[(VAR_NAME_LEN * 2) + 1];
1926 gint content_width, header_width;
1927 GtkTreeViewColumn *tree_column;
1930 column = &ia->separators.columns[dict_idx];
1932 var = dict_get_var (ia->formats.dict, dict_idx);
1934 escape_underscores (input ? column->name : var_get_name (var), name);
1935 char_cnt = input ? column->width : var_get_print_format (var)->w;
1936 content_width = get_monospace_width (tree_view, ia->asst.fixed_renderer,
1938 header_width = get_string_width (tree_view, ia->asst.prop_renderer,
1941 tree_column = gtk_tree_view_column_new ();
1942 g_object_set_data (G_OBJECT (tree_column), "column-number",
1943 GINT_TO_POINTER (dict_idx));
1944 gtk_tree_view_column_set_title (tree_column, name);
1945 gtk_tree_view_column_pack_start (tree_column, ia->asst.fixed_renderer,
1947 gtk_tree_view_column_set_cell_data_func (
1948 tree_column, ia->asst.fixed_renderer,
1949 input ? render_input_cell : render_output_cell, ia, NULL);
1950 gtk_tree_view_column_set_sizing (tree_column, GTK_TREE_VIEW_COLUMN_FIXED);
1951 gtk_tree_view_column_set_fixed_width (tree_column, MAX (content_width,
1957 static GtkTreeView *
1958 create_data_tree_view (bool input, GtkContainer *parent,
1959 struct import_assistant *ia)
1961 GtkTreeView *tree_view;
1964 make_tree_view (ia, ia->first_line.skip_lines, &tree_view);
1965 gtk_tree_selection_set_mode (gtk_tree_view_get_selection (tree_view),
1966 GTK_SELECTION_NONE);
1968 for (i = 0; i < ia->separators.column_cnt; i++)
1969 gtk_tree_view_append_column (tree_view,
1970 make_data_column (ia, tree_view, input, i));
1972 g_object_set (G_OBJECT (tree_view), "has-tooltip", TRUE, (void *) NULL);
1973 g_signal_connect (tree_view, "query-tooltip",
1974 G_CALLBACK (input ? on_query_input_tooltip
1975 : on_query_output_tooltip), ia);
1976 gtk_tree_view_set_fixed_height_mode (tree_view, true);
1978 gtk_container_add (parent, GTK_WIDGET (tree_view));
1979 gtk_widget_show (GTK_WIDGET (tree_view));
1985 escape_underscores (const char *in, char *out)
1987 for (; *in != '\0'; in++)
1996 /* TextImportModel, a GtkTreeModel implementation used by some
1997 pages of the assistant. */
1999 #define G_TYPE_TEXT_IMPORT_MODEL (text_import_model_get_type ())
2000 #define TEXT_IMPORT_MODEL(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), G_TYPE_TEXT_IMPORT_MODEL, TextImportModel))
2001 #define TEXT_IMPORT_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), G_TYPE_TEXT_IMPORT_MODEL, TextImportModelClass))
2002 #define IS_TEXT_IMPORT_MODEL(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), G_TYPE_TEXT_IMPORT_MODEL))
2003 #define IS_TEXT_IMPORT_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), G_TYPE_TEXT_IMPORT_MODEL))
2004 #define TEXT_IMPORT_MODEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), G_TYPE_TEXT_IMPORT_MODEL, TextImportModelClass))
2006 /* Random number used in 'stamp' member of GtkTreeIter. */
2007 #define TREE_MODEL_STAMP 0x7efd67d3
2009 struct TextImportModel
2012 struct string *lines;
2017 struct TextImportModelClass
2019 GObjectClass parent_class;
2022 GType text_import_model_get_type (void);
2023 static void text_import_model_tree_model_init (gpointer iface, gpointer data);
2026 text_import_model_get_type (void)
2028 static GType object_type = 0;
2032 static const GTypeInfo object_info = {
2033 sizeof (TextImportModelClass),
2034 (GBaseInitFunc) NULL,
2035 (GBaseFinalizeFunc) NULL,
2036 NULL, /* class_init */
2037 NULL, /* class_finalize */
2038 NULL, /* class_data */
2039 sizeof (TextImportModel),
2040 0, /* n_preallocs */
2041 NULL, /* instance_init */
2044 static const GInterfaceInfo tree_model_info = {
2045 text_import_model_tree_model_init,
2050 object_type = g_type_register_static (G_TYPE_OBJECT,
2054 g_type_add_interface_static (object_type, GTK_TYPE_TREE_MODEL,
2064 /* Creates and returns a new TextImportModel that contains the
2065 LINE_CNT lines in LINES. The lines before FIRST_LINE in LINES
2066 are not part of the model, but they are included in the line
2067 numbers in the TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER column.
2069 The caller retains responsibility for freeing LINES and must
2070 ensure that its lifetime and that of the strings that it
2071 contains exceeds that of the TextImportModel. */
2073 text_import_model_new (struct string *lines, size_t line_cnt,
2076 TextImportModel *new_text_import_model
2077 = g_object_new (G_TYPE_TEXT_IMPORT_MODEL, NULL);
2078 new_text_import_model->lines = lines;
2079 new_text_import_model->line_cnt = line_cnt;
2080 new_text_import_model->first_line = first_line;
2081 return new_text_import_model;
2086 tree_model_iter_has_child (GtkTreeModel *tree_model,
2093 tree_model_iter_parent (GtkTreeModel *tree_model,
2100 static GtkTreeModelFlags
2101 tree_model_get_flags (GtkTreeModel *model)
2103 g_return_val_if_fail (IS_TEXT_IMPORT_MODEL (model), (GtkTreeModelFlags) 0);
2105 return GTK_TREE_MODEL_LIST_ONLY | GTK_TREE_MODEL_ITERS_PERSIST;
2110 tree_model_n_columns (GtkTreeModel *model)
2116 tree_model_column_type (GtkTreeModel *model, gint index)
2118 return (index == TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER ? G_TYPE_INT
2119 : index == TEXT_IMPORT_MODEL_COLUMN_LINE ? G_TYPE_STRING
2124 init_iter (TextImportModel *list, gint idx, GtkTreeIter *iter)
2126 if (idx < 0 || idx >= list->line_cnt)
2129 iter->user_data = GINT_TO_POINTER (-1);
2134 iter->stamp = TREE_MODEL_STAMP;
2135 iter->user_data = GINT_TO_POINTER (idx);
2141 tree_model_get_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreePath *path)
2143 gint *indices, depth;
2145 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2147 g_return_val_if_fail (path, FALSE);
2149 indices = gtk_tree_path_get_indices (path);
2150 depth = gtk_tree_path_get_depth (path);
2152 g_return_val_if_fail (depth == 1, FALSE);
2154 return init_iter (list, indices[0], iter);
2159 tree_model_iter_next (GtkTreeModel *model, GtkTreeIter *iter)
2161 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2164 assert (iter->stamp == TREE_MODEL_STAMP);
2166 idx = GPOINTER_TO_INT (iter->user_data);
2167 return init_iter (list, idx == -1 ? -1 : idx + 1, iter);
2170 static GtkTreePath *
2171 tree_model_get_path (GtkTreeModel *model, GtkTreeIter *iter)
2175 g_return_val_if_fail (iter->stamp == TREE_MODEL_STAMP, FALSE);
2177 path = gtk_tree_path_new ();
2178 gtk_tree_path_append_index (path, GPOINTER_TO_INT (iter->user_data));
2184 tree_model_get_value (GtkTreeModel *model, GtkTreeIter *iter,
2185 gint column, GValue *value)
2187 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2190 g_return_if_fail (iter->stamp == TREE_MODEL_STAMP);
2192 idx = GPOINTER_TO_INT (iter->user_data);
2193 assert (idx >= 0 && idx < list->line_cnt);
2197 g_value_init (value, G_TYPE_INT);
2198 g_value_set_int (value, idx + list->first_line + 1);
2202 g_value_init (value, G_TYPE_STRING);
2203 g_value_set_static_string (value, ds_cstr (&list->lines[idx]));
2208 tree_model_iter_children (GtkTreeModel *tree_model,
2210 GtkTreeIter *parent)
2216 tree_model_n_children (GtkTreeModel *model, GtkTreeIter *iter)
2218 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2220 return iter == NULL ? list->line_cnt : 0;
2224 tree_model_nth_child (GtkTreeModel *model, GtkTreeIter *iter,
2225 GtkTreeIter *parent, gint n)
2227 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2228 g_return_val_if_fail (IS_TEXT_IMPORT_MODEL (model), FALSE);
2232 return init_iter (list, n, iter);
2236 text_import_model_tree_model_init (gpointer iface_, gpointer data UNUSED)
2238 GtkTreeModelIface *iface = (GtkTreeModelIface *) iface_;
2240 iface->get_flags = tree_model_get_flags;
2241 iface->get_n_columns = tree_model_n_columns;
2242 iface->get_column_type = tree_model_column_type;
2243 iface->get_iter = tree_model_get_iter;
2244 iface->iter_next = tree_model_iter_next;
2245 iface->get_path = tree_model_get_path;
2246 iface->get_value = tree_model_get_value;
2248 iface->iter_children = tree_model_iter_children;
2249 iface->iter_has_child = tree_model_iter_has_child;
2250 iface->iter_n_children = tree_model_n_children;
2251 iface->iter_nth_child = tree_model_nth_child;
2252 iface->iter_parent = tree_model_iter_parent;
2256 text_import_model_iter_to_row (const GtkTreeIter *iter)
2258 assert (iter->stamp == TREE_MODEL_STAMP);
2259 return GPOINTER_TO_INT (iter->user_data);
2262 /* Increments the "watch cursor" level, setting the cursor for
2263 the assistant window to a watch face to indicate to the user
2264 that the ongoing operation may take some time. */
2266 push_watch_cursor (struct import_assistant *ia)
2268 if (++ia->asst.watch_cursor == 1)
2270 GtkWidget *widget = GTK_WIDGET (ia->asst.assistant);
2271 GdkCursor *cursor = gdk_cursor_new (GDK_WATCH);
2272 gdk_window_set_cursor (widget->window, cursor);
2273 gdk_cursor_unref (cursor);
2274 gdk_display_flush (gtk_widget_get_display (widget));
2278 /* Decrements the "watch cursor" level. If the level reaches
2279 zero, the cursor is reset to its default shape. */
2281 pop_watch_cursor (struct import_assistant *ia)
2283 if (--ia->asst.watch_cursor == 0)
2285 GtkWidget *widget = GTK_WIDGET (ia->asst.assistant);;
2286 gdk_window_set_cursor (widget->window, NULL);