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;
93 GtkCellRenderer *prop_renderer;
94 GtkCellRenderer *fixed_renderer;
96 static void init_assistant (struct import_assistant *, GtkWindow *);
97 static void destroy_assistant (struct import_assistant *);
98 static GtkWidget *add_page_to_assistant (struct import_assistant *,
100 GtkAssistantPageType);
102 /* The introduction page of the assistant. */
106 GtkWidget *all_cases_button;
107 GtkWidget *n_cases_button;
108 GtkWidget *n_cases_spin;
109 GtkWidget *percent_button;
110 GtkWidget *percent_spin;
112 static void init_intro_page (struct import_assistant *);
113 static void reset_intro_page (struct import_assistant *);
115 /* Page where the user chooses the first line of data. */
116 struct first_line_page
118 int skip_lines; /* Number of initial lines to skip? */
119 bool variable_names; /* Variable names above first line of data? */
122 GtkTreeView *tree_view;
123 GtkWidget *variable_names_cb;
125 static void init_first_line_page (struct import_assistant *);
126 static void reset_first_line_page (struct import_assistant *);
128 /* Page where the user chooses field separators. */
129 struct separators_page
131 /* How to break lines into columns. */
132 struct string separators; /* Field separators. */
133 struct string quotes; /* Quote characters. */
134 bool escape; /* Doubled quotes yield a quote mark? */
136 /* The columns produced thereby. */
137 struct column *columns; /* Information about each column. */
138 size_t column_cnt; /* Number of columns. */
141 GtkWidget *custom_cb;
142 GtkWidget *custom_entry;
144 GtkWidget *quote_combo;
145 GtkEntry *quote_entry;
146 GtkWidget *escape_cb;
147 GtkTreeView *fields_tree_view;
149 /* The columns that the separators divide the data into. */
152 /* Variable name for this column. This is the variable name
153 used on the separators page; it can be overridden by the
154 user on the formats page. */
157 /* Maximum length of any row in this column. */
160 /* Contents of this column: contents[row] is the contents for
163 A null substring indicates a missing column for that row
164 (because the line contains an insufficient number of
167 contents[] elements may be substrings of the lines[]
168 strings that represent the whole lines of the file, to
169 save memory. Other elements are dynamically allocated
170 with ss_alloc_substring. */
171 struct substring *contents;
173 static void init_separators_page (struct import_assistant *);
174 static void destroy_separators_page (struct import_assistant *);
175 static void prepare_separators_page (struct import_assistant *);
176 static void reset_separators_page (struct import_assistant *);
178 /* Page where the user verifies and adjusts input formats. */
181 struct dictionary *dict;
184 GtkTreeView *data_tree_view;
185 PsppireDict *psppire_dict;
186 struct variable **modified_vars;
187 size_t modified_var_cnt;
189 static void init_formats_page (struct import_assistant *);
190 static void destroy_formats_page (struct import_assistant *);
191 static void prepare_formats_page (struct import_assistant *);
192 static void reset_formats_page (struct import_assistant *);
194 struct import_assistant
197 struct assistant asst;
198 struct intro_page intro;
199 struct first_line_page first_line;
200 struct separators_page separators;
201 struct formats_page formats;
204 static void apply_dict (const struct dictionary *, struct string *);
205 static char *generate_syntax (const struct import_assistant *);
207 static gboolean get_tooltip_location (GtkWidget *widget, gint wx, gint wy,
208 const struct import_assistant *,
209 size_t *row, size_t *column);
210 static void make_tree_view (const struct import_assistant *ia,
212 GtkTreeView **tree_view);
213 static void add_line_number_column (const struct import_assistant *,
215 static gint get_monospace_width (GtkTreeView *, GtkCellRenderer *,
217 static gint get_string_width (GtkTreeView *, GtkCellRenderer *,
219 static GtkTreeViewColumn *make_data_column (struct import_assistant *,
220 GtkTreeView *, bool input,
222 static GtkTreeView *create_data_tree_view (bool input, GtkContainer *parent,
223 struct import_assistant *);
224 static void escape_underscores (const char *in, char *out);
226 /* Pops up the Text Data Import assistant. */
228 text_data_import_assistant (GObject *o, gpointer de_)
230 struct data_editor *de = de_;
231 GtkWindow *parent_window = de->parent.window;
232 struct import_assistant ia = { { 0, } };
234 if (!init_file (&ia, parent_window))
237 init_assistant (&ia, parent_window);
238 init_intro_page (&ia);
239 init_first_line_page (&ia);
240 init_separators_page (&ia);
241 init_formats_page (&ia);
243 gtk_widget_show_all (GTK_WIDGET (ia.asst.assistant));
245 ia.asst.main_loop = g_main_loop_new (NULL, false);
246 g_main_loop_run (ia.asst.main_loop);
248 switch (ia.asst.response)
250 case GTK_RESPONSE_APPLY:
252 char *syntax = generate_syntax (&ia);
253 execute_syntax (create_syntax_string_source (syntax));
257 case PSPPIRE_RESPONSE_PASTE:
259 char *syntax = generate_syntax (&ia);
260 struct syntax_editor *se =
261 (struct syntax_editor *) window_create (WINDOW_SYNTAX, NULL);
262 gtk_text_buffer_insert_at_cursor (se->buffer, syntax, -1);
270 destroy_formats_page (&ia);
271 destroy_separators_page (&ia);
272 destroy_assistant (&ia);
276 /* Emits PSPP syntax to S that applies the dictionary attributes
277 (such as missing values and value labels) of the variables in
280 apply_dict (const struct dictionary *dict, struct string *s)
282 size_t var_cnt = dict_get_var_cnt (dict);
285 for (i = 0; i < var_cnt; i++)
287 struct variable *var = dict_get_var (dict, i);
288 const char *name = var_get_name (var);
289 enum val_type type = var_get_type (var);
290 int width = var_get_width (var);
291 enum measure measure = var_get_measure (var);
292 enum alignment alignment = var_get_alignment (var);
293 const struct fmt_spec *format = var_get_print_format (var);
295 if (var_has_missing_values (var))
297 const struct missing_values *mv = var_get_missing_values (var);
300 syntax_gen_pspp (s, "MISSING VALUES %ss (", name);
301 for (j = 0; j < mv_n_values (mv); j++)
305 ds_put_cstr (s, ", ");
306 mv_get_value (mv, &value, j);
307 syntax_gen_value (s, &value, width, format);
310 if (mv_has_range (mv))
313 if (mv_has_value (mv))
314 ds_put_cstr (s, ", ");
315 mv_get_range (mv, &low, &high);
316 syntax_gen_num_range (s, low, high, format);
318 ds_put_cstr (s, ").\n");
320 if (var_has_value_labels (var))
322 const struct val_labs *vls = var_get_value_labels (var);
323 struct val_labs_iterator *iter;
326 syntax_gen_pspp (s, "VALUE LABELS %ss", name);
327 for (vl = val_labs_first_sorted (vls, &iter); vl != NULL;
328 vl = val_labs_next (vls, &iter))
330 ds_put_cstr (s, "\n ");
331 syntax_gen_value (s, &vl->value, width, format);
332 ds_put_char (s, ' ');
333 syntax_gen_string (s, ss_cstr (vl->label));
335 ds_put_cstr (s, ".\n");
337 if (var_has_label (var))
338 syntax_gen_pspp (s, "VARIABLE LABELS %ss %sq.\n",
339 name, var_get_label (var));
340 if (measure != var_default_measure (type))
341 syntax_gen_pspp (s, "VARIABLE LEVEL %ss (%ss).\n",
343 (measure == MEASURE_NOMINAL ? "NOMINAL"
344 : measure == MEASURE_ORDINAL ? "ORDINAL"
346 if (alignment != var_default_alignment (type))
347 syntax_gen_pspp (s, "VARIABLE ALIGNMENT %ss (%ss).\n",
349 (alignment == ALIGN_LEFT ? "LEFT"
350 : alignment == ALIGN_CENTRE ? "CENTER"
352 if (var_get_display_width (var) != var_default_display_width (width))
353 syntax_gen_pspp (s, "VARIABLE WIDTH %ss (%d).\n",
354 name, var_get_display_width (var));
358 /* Generates and returns PSPP syntax to execute the import
359 operation described by IA. The caller must free the syntax
362 generate_syntax (const struct import_assistant *ia)
364 struct string s = DS_EMPTY_INITIALIZER;
373 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (
374 ia->intro.n_cases_button)))
375 ds_put_format (&s, " /IMPORTCASES=FIRST %d\n",
376 gtk_spin_button_get_value_as_int (
377 GTK_SPIN_BUTTON (ia->intro.n_cases_spin)));
378 else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (
379 ia->intro.percent_button)))
380 ds_put_format (&s, " /IMPORTCASES=PERCENT %d\n",
381 gtk_spin_button_get_value_as_int (
382 GTK_SPIN_BUTTON (ia->intro.percent_spin)));
384 ds_put_cstr (&s, " /IMPORTCASES=ALL\n");
386 " /ARRANGEMENT=DELIMITED\n"
388 if (ia->first_line.skip_lines > 0)
389 ds_put_format (&s, " /FIRSTCASE=%d\n", ia->first_line.skip_lines + 1);
390 ds_put_cstr (&s, " /DELIMITERS=\"");
391 if (ds_find_char (&ia->separators.separators, '\t') != SIZE_MAX)
392 ds_put_cstr (&s, "\\t");
393 if (ds_find_char (&ia->separators.separators, '\\') != SIZE_MAX)
394 ds_put_cstr (&s, "\\\\");
395 for (i = 0; i < ds_length (&ia->separators.separators); i++)
397 char c = ds_at (&ia->separators.separators, i);
399 ds_put_cstr (&s, "\"\"");
400 else if (c != '\t' && c != '\\')
403 ds_put_cstr (&s, "\"\n");
404 if (!ds_is_empty (&ia->separators.quotes))
405 syntax_gen_pspp (&s, " /QUALIFIER=%sq\n", ds_cstr (&ia->separators.quotes));
406 if (!ds_is_empty (&ia->separators.quotes) && ia->separators.escape)
407 ds_put_cstr (&s, " /ESCAPE\n");
408 ds_put_cstr (&s, " /VARIABLES=\n");
410 var_cnt = dict_get_var_cnt (ia->formats.dict);
411 for (i = 0; i < var_cnt; i++)
413 struct variable *var = dict_get_var (ia->formats.dict, i);
414 char format_string[FMT_STRING_LEN_MAX + 1];
415 fmt_to_string (var_get_print_format (var), format_string);
416 ds_put_format (&s, " %s %s%s\n",
417 var_get_name (var), format_string,
418 i == var_cnt - 1 ? "." : "");
421 apply_dict (ia->formats.dict, &s);
426 /* Choosing a file and reading it. */
428 static char *choose_file (GtkWindow *parent_window);
430 /* Obtains the file to import from the user and initializes IA's
431 file substructure. PARENT_WINDOW must be the window to use
432 as the file chooser window's parent.
434 Returns true if successful, false if the file name could not
435 be obtained or the file could not be read. */
437 init_file (struct import_assistant *ia, GtkWindow *parent_window)
439 struct file *file = &ia->file;
440 enum { MAX_PREVIEW_LINES = 1000 }; /* Max number of lines to read. */
441 enum { MAX_LINE_LEN = 16384 }; /* Max length of an acceptable line. */
444 file->file_name = choose_file (parent_window);
445 if (file->file_name == NULL)
448 stream = fopen (file->file_name, "r");
451 msg (ME, _("Could not open \"%s\": %s"),
452 file->file_name, strerror (errno));
456 file->lines = xnmalloc (MAX_PREVIEW_LINES, sizeof *file->lines);
457 for (; file->line_cnt < MAX_PREVIEW_LINES; file->line_cnt++)
459 struct string *line = &file->lines[file->line_cnt];
461 ds_init_empty (line);
462 if (!ds_read_line (line, stream, MAX_LINE_LEN))
466 else if (ferror (stream))
467 msg (ME, _("Error reading \"%s\": %s"),
468 file->file_name, strerror (errno));
470 msg (ME, _("Failed to read \"%s\", because it contains a line "
471 "over %d bytes long and therefore appears not to be "
473 file->file_name, MAX_LINE_LEN);
478 ds_chomp (line, '\n');
479 ds_chomp (line, '\r');
482 if (file->line_cnt == 0)
484 msg (ME, _("\"%s\" is empty."), file->file_name);
490 /* Estimate the number of lines in the file. */
491 if (file->line_cnt < MAX_PREVIEW_LINES)
492 file->total_lines = file->line_cnt;
496 off_t position = ftello (stream);
497 if (fstat (fileno (stream), &s) == 0 && position > 0)
498 file->total_lines = (double) file->line_cnt / position * s.st_size;
500 file->total_lines = 0;
506 /* Frees IA's file substructure. */
508 destroy_file (struct import_assistant *ia)
510 struct file *f = &ia->file;
513 for (i = 0; i < f->line_cnt; i++)
514 ds_destroy (&f->lines[i]);
516 g_free (f->file_name);
519 /* Obtains the file to read from the user and returns the name of
520 the file as a string that must be freed with g_free if
521 successful, otherwise a null pointer. PARENT_WINDOW must be
522 the window to use as the file chooser window's parent. */
524 choose_file (GtkWindow *parent_window)
529 dialog = gtk_file_chooser_dialog_new (_("Import Delimited Text Data"),
531 GTK_FILE_CHOOSER_ACTION_OPEN,
532 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
533 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
536 switch (gtk_dialog_run (GTK_DIALOG (dialog)))
538 case GTK_RESPONSE_ACCEPT:
539 file_name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
545 gtk_widget_destroy (dialog);
552 static void close_assistant (struct import_assistant *, int response);
553 static void on_prepare (GtkAssistant *assistant, GtkWidget *page,
554 struct import_assistant *);
555 static void on_cancel (GtkAssistant *assistant, struct import_assistant *);
556 static void on_close (GtkAssistant *assistant, struct import_assistant *);
557 static void on_paste (GtkButton *button, struct import_assistant *);
558 static void on_reset (GtkButton *button, struct import_assistant *);
559 static void close_assistant (struct import_assistant *, int response);
561 /* Initializes IA's asst substructure. PARENT_WINDOW must be the
562 window to use as the assistant window's parent. */
564 init_assistant (struct import_assistant *ia, GtkWindow *parent_window)
566 struct assistant *a = &ia->asst;
568 a->xml = XML_NEW ("text-data-import.glade");
569 a->assistant = GTK_ASSISTANT (gtk_assistant_new ());
570 g_signal_connect (a->assistant, "prepare", G_CALLBACK (on_prepare), ia);
571 g_signal_connect (a->assistant, "cancel", G_CALLBACK (on_cancel), ia);
572 g_signal_connect (a->assistant, "close", G_CALLBACK (on_close), ia);
573 a->paste_button = gtk_button_new_from_stock (GTK_STOCK_PASTE);
574 gtk_assistant_add_action_widget (a->assistant, a->paste_button);
575 g_signal_connect (a->paste_button, "clicked", G_CALLBACK (on_paste), ia);
576 a->reset_button = gtk_button_new_from_stock ("pspp-stock-reset");
577 gtk_assistant_add_action_widget (a->assistant, a->reset_button);
578 g_signal_connect (a->reset_button, "clicked", G_CALLBACK (on_reset), ia);
579 gtk_window_set_title (GTK_WINDOW (a->assistant),
580 _("Importing Delimited Text Data"));
581 gtk_window_set_transient_for (GTK_WINDOW (a->assistant), parent_window);
583 a->prop_renderer = gtk_cell_renderer_text_new ();
584 g_object_ref_sink (a->prop_renderer);
585 a->fixed_renderer = gtk_cell_renderer_text_new ();
586 g_object_ref_sink (a->fixed_renderer);
587 g_object_set (G_OBJECT (a->fixed_renderer),
588 "family", "Monospace",
592 /* Frees IA's asst substructure. */
594 destroy_assistant (struct import_assistant *ia)
596 struct assistant *a = &ia->asst;
598 g_object_unref (a->prop_renderer);
599 g_object_unref (a->fixed_renderer);
600 g_object_unref (a->xml);
603 /* Appends a page of the given TYPE, with PAGE as its content, to
604 the GtkAssistant encapsulated by IA. Returns the GtkWidget
605 that represents the page. */
607 add_page_to_assistant (struct import_assistant *ia,
608 GtkWidget *page, GtkAssistantPageType type)
614 title = gtk_window_get_title (GTK_WINDOW (page));
615 title_copy = xstrdup (title ? title : "");
617 content = gtk_bin_get_child (GTK_BIN (page));
619 g_object_ref (content);
620 gtk_container_remove (GTK_CONTAINER (page), content);
622 gtk_widget_destroy (page);
624 gtk_assistant_append_page (ia->asst.assistant, content);
625 gtk_assistant_set_page_type (ia->asst.assistant, content, type);
626 gtk_assistant_set_page_title (ia->asst.assistant, content, title_copy);
633 /* Called just before PAGE is displayed as the current page of
634 ASSISTANT, this updates IA content according to the new
637 on_prepare (GtkAssistant *assistant, GtkWidget *page,
638 struct import_assistant *ia)
640 if (page == ia->separators.page)
641 prepare_separators_page (ia);
642 else if (page == ia->formats.page)
643 prepare_formats_page (ia);
645 gtk_widget_show (ia->asst.reset_button);
646 if (page == ia->formats.page)
647 gtk_widget_show (ia->asst.paste_button);
649 gtk_widget_hide (ia->asst.paste_button);
651 /* Make the user visit each page in the assistant once. Seems
652 like a reasonable user interface, plus visiting the final
653 page is what constructs the dictionary. */
654 gtk_assistant_set_page_complete (assistant, page, true);
657 /* Called when the Cancel button in the assistant is clicked. */
659 on_cancel (GtkAssistant *assistant, struct import_assistant *ia)
661 close_assistant (ia, GTK_RESPONSE_CANCEL);
664 /* Called when the Apply button on the last page of the assistant
667 on_close (GtkAssistant *assistant, struct import_assistant *ia)
669 close_assistant (ia, GTK_RESPONSE_APPLY);
672 /* Called when the Paste button on the last page of the assistant
675 on_paste (GtkButton *button, struct import_assistant *ia)
677 close_assistant (ia, PSPPIRE_RESPONSE_PASTE);
680 /* Called when the Reset button is clicked. */
682 on_reset (GtkButton *button, struct import_assistant *ia)
684 gint page_num = gtk_assistant_get_current_page (ia->asst.assistant);
685 GtkWidget *page = gtk_assistant_get_nth_page (ia->asst.assistant, page_num);
687 if (page == ia->intro.page)
688 reset_intro_page (ia);
689 else if (page == ia->first_line.page)
690 reset_first_line_page (ia);
691 else if (page == ia->separators.page)
692 reset_separators_page (ia);
693 else if (page == ia->formats.page)
694 reset_formats_page (ia);
697 /* Causes the assistant to close, returning RESPONSE for
698 interpretation by text_data_import_assistant. */
700 close_assistant (struct import_assistant *ia, int response)
702 ia->asst.response = response;
703 g_main_loop_quit (ia->asst.main_loop);
704 gtk_widget_hide (GTK_WIDGET (ia->asst.assistant));
707 /* The "intro" page of the assistant. */
709 static void on_intro_amount_changed (GtkToggleButton *button,
710 struct import_assistant *);
712 /* Initializes IA's intro substructure. */
714 init_intro_page (struct import_assistant *ia)
716 GladeXML *xml = ia->asst.xml;
717 struct intro_page *p = &ia->intro;
720 p->page = add_page_to_assistant (ia, get_widget_assert (xml, "Intro"),
721 GTK_ASSISTANT_PAGE_INTRO);
722 p->all_cases_button = get_widget_assert (xml, "import-all-cases");
723 p->n_cases_button = get_widget_assert (xml, "import-n-cases");
724 p->n_cases_spin = get_widget_assert (xml, "n-cases-spin");
725 p->percent_button = get_widget_assert (xml, "import-percent");
726 p->percent_spin = get_widget_assert (xml, "percent-spin");
727 g_signal_connect (p->all_cases_button, "toggled",
728 G_CALLBACK (on_intro_amount_changed), ia);
729 g_signal_connect (p->n_cases_button, "toggled",
730 G_CALLBACK (on_intro_amount_changed), ia);
731 g_signal_connect (p->percent_button, "toggled",
732 G_CALLBACK (on_intro_amount_changed), ia);
735 ds_put_cstr (&s, _("This assistant will guide you through the process of "
736 "importing data into PSPP from a text file with one line "
737 "per case, in which fields are separated by tabs, "
738 "commas, or other delimiters.\n\n"));
739 if (ia->file.total_is_exact)
741 &s, ngettext ("The selected file contains %zu line of text. ",
742 "The selected file contains %zu lines of text. ",
745 else if (ia->file.total_lines > 0)
749 "The selected file contains approximately %lu line of text. ",
750 "The selected file contains approximately %lu lines of text. ",
751 ia->file.total_lines),
752 ia->file.total_lines);
755 "Only the first %zu line of the file will be shown for "
756 "preview purposes in the following screens. ",
757 "Only the first %zu lines of the file will be shown for "
758 "preview purposes in the following screens. ",
762 ds_put_cstr (&s, _("You may choose below how much of the file should "
763 "actually be imported."));
764 gtk_label_set_text (GTK_LABEL (get_widget_assert (xml, "intro-label")),
769 /* Resets IA's intro page to its initial state. */
771 reset_intro_page (struct import_assistant *ia)
773 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->intro.all_cases_button),
777 /* Called when one of the radio buttons is clicked. */
779 on_intro_amount_changed (GtkToggleButton *button UNUSED,
780 struct import_assistant *ia)
782 struct intro_page *p = &ia->intro;
784 gtk_widget_set_sensitive (p->n_cases_spin,
785 gtk_toggle_button_get_active (
786 GTK_TOGGLE_BUTTON (p->n_cases_button)));
788 gtk_widget_set_sensitive (ia->intro.percent_spin,
789 gtk_toggle_button_get_active (
790 GTK_TOGGLE_BUTTON (p->percent_button)));
793 /* The "first line" page of the assistant. */
795 static GtkTreeView *create_lines_tree_view (GtkContainer *parent_window,
796 struct import_assistant *);
797 static void on_first_line_change (GtkTreeSelection *,
798 struct import_assistant *);
799 static void on_variable_names_cb_toggle (GtkToggleButton *,
800 struct import_assistant *);
801 static void set_first_line (struct import_assistant *);
802 static void get_first_line (struct import_assistant *);
804 /* Initializes IA's first_line substructure. */
806 init_first_line_page (struct import_assistant *ia)
808 struct first_line_page *p = &ia->first_line;
809 GladeXML *xml = ia->asst.xml;
811 p->page = add_page_to_assistant (ia, get_widget_assert (xml, "FirstLine"),
812 GTK_ASSISTANT_PAGE_CONTENT);
813 gtk_widget_destroy (get_widget_assert (xml, "first-line"));
814 p->tree_view = create_lines_tree_view (
815 GTK_CONTAINER (get_widget_assert (xml, "first-line-scroller")), ia);
816 p->variable_names_cb = get_widget_assert (xml, "variable-names");
817 gtk_tree_selection_set_mode (
818 gtk_tree_view_get_selection (GTK_TREE_VIEW (p->tree_view)),
819 GTK_SELECTION_BROWSE);
821 g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (p->tree_view)),
822 "changed", G_CALLBACK (on_first_line_change), ia);
823 g_signal_connect (p->variable_names_cb, "toggled",
824 G_CALLBACK (on_variable_names_cb_toggle), ia);
827 /* Resets the first_line page to its initial content. */
829 reset_first_line_page (struct import_assistant *ia)
831 ia->first_line.skip_lines = 0;
832 ia->first_line.variable_names = false;
836 /* Creates and returns a tree view that contains each of the
837 lines in IA's file as a row. */
839 create_lines_tree_view (GtkContainer *parent, struct import_assistant *ia)
841 GtkTreeView *tree_view;
842 GtkTreeViewColumn *column;
843 size_t max_line_length;
844 gint content_width, header_width;
847 make_tree_view (ia, 0, &tree_view);
849 column = gtk_tree_view_column_new_with_attributes (
850 "Text", ia->asst.fixed_renderer,
851 "text", TEXT_IMPORT_MODEL_COLUMN_LINE,
853 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
856 for (i = 0; i < ia->file.line_cnt; i++)
858 size_t w = ds_length (&ia->file.lines[i]);
859 max_line_length = MAX (max_line_length, w);
862 content_width = get_monospace_width (tree_view, ia->asst.fixed_renderer,
864 header_width = get_string_width (tree_view, ia->asst.prop_renderer, "Text");
865 gtk_tree_view_column_set_fixed_width (column, MAX (content_width,
867 gtk_tree_view_append_column (tree_view, column);
869 gtk_tree_view_set_fixed_height_mode (tree_view, true);
871 gtk_container_add (parent, GTK_WIDGET (tree_view));
872 gtk_widget_show (GTK_WIDGET (tree_view));
877 /* Called when the line selected in the first_line tree view
880 on_first_line_change (GtkTreeSelection *selection UNUSED,
881 struct import_assistant *ia)
886 /* Called when the checkbox that indicates whether variable
887 names are in the row above the first line is toggled. */
889 on_variable_names_cb_toggle (GtkToggleButton *variable_names_cb UNUSED,
890 struct import_assistant *ia)
895 /* Sets the widgets to match IA's first_line substructure. */
897 set_first_line (struct import_assistant *ia)
901 path = gtk_tree_path_new_from_indices (ia->first_line.skip_lines, -1);
902 gtk_tree_view_set_cursor (GTK_TREE_VIEW (ia->first_line.tree_view),
904 gtk_tree_path_free (path);
906 gtk_toggle_button_set_active (
907 GTK_TOGGLE_BUTTON (ia->first_line.variable_names_cb),
908 ia->first_line.variable_names);
909 gtk_widget_set_sensitive (ia->first_line.variable_names_cb,
910 ia->first_line.skip_lines > 0);
913 /* Sets IA's first_line substructure to match the widgets. */
915 get_first_line (struct import_assistant *ia)
917 GtkTreeSelection *selection;
921 selection = gtk_tree_view_get_selection (ia->first_line.tree_view);
922 if (gtk_tree_selection_get_selected (selection, &model, &iter))
924 GtkTreePath *path = gtk_tree_model_get_path (model, &iter);
925 int row = gtk_tree_path_get_indices (path)[0];
926 gtk_tree_path_free (path);
928 ia->first_line.skip_lines = row;
929 ia->first_line.variable_names =
930 (ia->first_line.skip_lines > 0
931 && gtk_toggle_button_get_active (
932 GTK_TOGGLE_BUTTON (ia->first_line.variable_names_cb)));
934 gtk_widget_set_sensitive (ia->first_line.variable_names_cb,
935 ia->first_line.skip_lines > 0);
938 /* The "separators" page of the assistant. */
940 static void revise_fields_preview (struct import_assistant *ia);
941 static void choose_likely_separators (struct import_assistant *ia);
942 static void find_commonest_chars (unsigned long int histogram[UCHAR_MAX + 1],
943 const char *targets, const char *def,
944 struct string *result);
945 static void clear_fields (struct import_assistant *ia);
946 static void revise_fields_preview (struct import_assistant *);
947 static void set_separators (struct import_assistant *);
948 static void get_separators (struct import_assistant *);
949 static void on_separators_custom_entry_notify (GObject *UNUSED,
951 struct import_assistant *);
952 static void on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
953 struct import_assistant *);
954 static void on_quote_combo_change (GtkComboBox *combo,
955 struct import_assistant *);
956 static void on_quote_cb_toggle (GtkToggleButton *quote_cb,
957 struct import_assistant *);
958 static void on_separator_toggle (GtkToggleButton *, struct import_assistant *);
959 static void render_input_cell (GtkTreeViewColumn *tree_column,
960 GtkCellRenderer *cell,
961 GtkTreeModel *model, GtkTreeIter *iter,
963 static gboolean on_query_input_tooltip (GtkWidget *widget, gint wx, gint wy,
964 gboolean keyboard_mode UNUSED,
966 struct import_assistant *);
968 /* A common field separator and its identifying name. */
971 const char *name; /* Name (for use with get_widget_assert). */
972 int c; /* Separator character. */
975 /* All the separators in the dialog box. */
976 static const struct separator separators[] =
988 #define SEPARATOR_CNT (sizeof separators / sizeof *separators)
990 /* Initializes IA's separators substructure. */
992 init_separators_page (struct import_assistant *ia)
994 GladeXML *xml = ia->asst.xml;
995 struct separators_page *p = &ia->separators;
998 choose_likely_separators (ia);
1000 p->page = add_page_to_assistant (ia, get_widget_assert (xml, "Separators"),
1001 GTK_ASSISTANT_PAGE_CONTENT);
1002 p->custom_cb = get_widget_assert (xml, "custom-cb");
1003 p->custom_entry = get_widget_assert (xml, "custom-entry");
1004 p->quote_combo = get_widget_assert (xml, "quote-combo");
1005 p->quote_entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (p->quote_combo)));
1006 p->quote_cb = get_widget_assert (xml, "quote-cb");
1007 p->escape_cb = get_widget_assert (xml, "escape");
1009 set_separators (ia);
1010 p->fields_tree_view = GTK_TREE_VIEW (get_widget_assert (xml, "fields"));
1011 g_signal_connect (GTK_COMBO_BOX (p->quote_combo), "changed",
1012 G_CALLBACK (on_quote_combo_change), ia);
1013 g_signal_connect (p->quote_cb, "toggled",
1014 G_CALLBACK (on_quote_cb_toggle), ia);
1015 g_signal_connect (GTK_ENTRY (p->custom_entry), "notify::text",
1016 G_CALLBACK (on_separators_custom_entry_notify), ia);
1017 g_signal_connect (p->custom_cb, "toggled",
1018 G_CALLBACK (on_separators_custom_cb_toggle), ia);
1019 for (i = 0; i < SEPARATOR_CNT; i++)
1020 g_signal_connect (get_widget_assert (xml, separators[i].name),
1021 "toggled", G_CALLBACK (on_separator_toggle), ia);
1022 g_signal_connect (p->escape_cb, "toggled",
1023 G_CALLBACK (on_separator_toggle), ia);
1026 /* Frees IA's separators substructure. */
1028 destroy_separators_page (struct import_assistant *ia)
1030 struct separators_page *s = &ia->separators;
1032 ds_destroy (&s->separators);
1033 ds_destroy (&s->quotes);
1037 /* Called just before the separators page becomes visible in the
1040 prepare_separators_page (struct import_assistant *ia)
1042 revise_fields_preview (ia);
1045 /* Called when the Reset button is clicked on the separators
1046 page, resets the separators to the defaults. */
1048 reset_separators_page (struct import_assistant *ia)
1050 choose_likely_separators (ia);
1051 set_separators (ia);
1054 /* Frees and clears the column data in IA's separators
1057 clear_fields (struct import_assistant *ia)
1059 struct separators_page *s = &ia->separators;
1061 if (s->column_cnt > 0)
1066 for (row = 0; row < ia->file.line_cnt; row++)
1068 const struct string *line = &ia->file.lines[row];
1069 const char *line_start = ds_data (line);
1070 const char *line_end = ds_end (line);
1072 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1074 char *s = ss_data (col->contents[row]);
1075 if (!(s >= line_start && s <= line_end))
1076 ss_dealloc (&col->contents[row]);
1080 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1083 free (col->contents);
1092 /* Breaks the file data in IA into columns based on the
1093 separators set in IA's separators substructure. */
1095 split_fields (struct import_assistant *ia)
1097 struct separators_page *s = &ia->separators;
1098 size_t columns_allocated;
1104 /* Is space in the set of separators? */
1105 space_sep = ss_find_char (ds_ss (&s->separators), ' ') != SIZE_MAX;
1107 /* Split all the lines, not just those from
1108 ia->first_line.skip_lines on, so that we split the line that
1109 contains variables names if ia->first_line.variable_names is
1111 columns_allocated = 0;
1112 for (row = 0; row < ia->file.line_cnt; row++)
1114 struct string *line = &ia->file.lines[row];
1115 struct substring text = ds_ss (line);
1118 for (column_idx = 0; ; column_idx++)
1120 struct substring field;
1121 struct column *column;
1124 ss_ltrim (&text, ss_cstr (" "));
1125 if (ss_is_empty (text))
1127 if (column_idx != 0)
1131 else if (!ds_is_empty (&s->quotes)
1132 && ds_find_char (&s->quotes, text.string[0]) != SIZE_MAX)
1134 int quote = ss_get_char (&text);
1136 ss_get_until (&text, quote, &field);
1143 while ((c = ss_get_char (&text)) != EOF)
1145 ds_put_char (&s, c);
1146 else if (ss_match_char (&text, quote))
1147 ds_put_char (&s, quote);
1154 ss_get_chars (&text, ss_cspan (text, ds_ss (&s->separators)),
1157 if (column_idx >= s->column_cnt)
1159 struct column *column;
1161 if (s->column_cnt >= columns_allocated)
1162 s->columns = x2nrealloc (s->columns, &columns_allocated,
1163 sizeof *s->columns);
1164 column = &s->columns[s->column_cnt++];
1165 column->name = NULL;
1167 column->contents = xcalloc (ia->file.line_cnt,
1168 sizeof *column->contents);
1170 column = &s->columns[column_idx];
1171 column->contents[row] = field;
1172 if (ss_length (field) > column->width)
1173 column->width = ss_length (field);
1176 ss_ltrim (&text, ss_cstr (" "));
1177 if (ss_is_empty (text))
1179 if (ss_find_char (ds_ss (&s->separators), ss_first (text))
1181 ss_advance (&text, 1);
1186 /* Chooses a name for each column on the separators page */
1188 choose_column_names (struct import_assistant *ia)
1190 const struct first_line_page *f = &ia->first_line;
1191 struct separators_page *s = &ia->separators;
1192 struct dictionary *dict;
1193 unsigned long int generated_name_count = 0;
1197 dict = dict_create ();
1198 name_row = f->variable_names && f->skip_lines ? f->skip_lines : 0;
1199 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1201 char name[VAR_NAME_LEN + 1];
1204 hint = name_row ? ss_xstrdup (col->contents[name_row - 1]) : NULL;
1205 if (!dict_make_unique_var_name (dict, hint, &generated_name_count, name))
1209 col->name = xstrdup (name);
1210 dict_create_var_assert (dict, name, 0);
1212 dict_destroy (dict);
1215 /* Picks the most likely separator and quote characters based on
1218 choose_likely_separators (struct import_assistant *ia)
1220 unsigned long int histogram[UCHAR_MAX + 1] = { 0 };
1223 /* Construct a histogram of all the characters used in the
1225 for (row = 0; row < ia->file.line_cnt; row++)
1227 struct substring line = ds_ss (&ia->file.lines[row]);
1228 size_t length = ss_length (line);
1230 for (i = 0; i < length; i++)
1231 histogram[(unsigned char) line.string[i]]++;
1234 find_commonest_chars (histogram, "\"'", "", &ia->separators.quotes);
1235 find_commonest_chars (histogram, ",;:/|!\t-", ",",
1236 &ia->separators.separators);
1237 ia->separators.escape = true;
1240 /* Chooses the most common character among those in TARGETS,
1241 based on the frequency data in HISTOGRAM, and stores it in
1242 RESULT. If there is a tie for the most common character among
1243 those in TARGETS, the earliest character is chosen. If none
1244 of the TARGETS appear at all, then DEF is used as a
1247 find_commonest_chars (unsigned long int histogram[UCHAR_MAX + 1],
1248 const char *targets, const char *def,
1249 struct string *result)
1251 unsigned char max = 0;
1252 unsigned long int max_count = 0;
1254 for (; *targets != '\0'; targets++)
1256 unsigned char c = *targets;
1257 unsigned long int count = histogram[c];
1258 if (count > max_count)
1267 ds_put_char (result, max);
1270 ds_assign_cstr (result, def);
1273 /* Revises the contents of the fields tree view based on the
1274 currently chosen set of separators. */
1276 revise_fields_preview (struct import_assistant *ia)
1279 w = GTK_WIDGET (ia->separators.fields_tree_view);
1280 gtk_widget_destroy (w);
1281 get_separators (ia);
1283 choose_column_names (ia);
1284 ia->separators.fields_tree_view = create_data_tree_view (
1286 GTK_CONTAINER (get_widget_assert (ia->asst.xml, "fields-scroller")),
1290 /* Sets the widgets to match IA's separators substructure. */
1292 set_separators (struct import_assistant *ia)
1294 struct separators_page *s = &ia->separators;
1296 struct string custom;
1301 ds_init_empty (&custom);
1303 for (i = 0; i < ds_length (&s->separators); i++)
1305 unsigned char c = ds_at (&s->separators, i);
1308 for (j = 0; j < SEPARATOR_CNT; j++)
1310 const struct separator *s = &separators[j];
1318 ds_put_char (&custom, c);
1322 for (i = 0; i < SEPARATOR_CNT; i++)
1324 const struct separator *s = &separators[i];
1325 GtkWidget *button = get_widget_assert (ia->asst.xml, s->name);
1326 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
1327 (seps & (1u << i)) != 0);
1329 any_custom = !ds_is_empty (&custom);
1330 gtk_entry_set_text (GTK_ENTRY (s->custom_entry), ds_cstr (&custom));
1331 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->custom_cb),
1333 gtk_widget_set_sensitive (s->custom_entry, any_custom);
1334 ds_destroy (&custom);
1336 any_quotes = !ds_is_empty (&s->quotes);
1337 gtk_entry_set_text (s->quote_entry,
1338 any_quotes ? ds_cstr (&s->quotes) : "\"");
1339 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->quote_cb),
1341 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->escape_cb),
1343 gtk_widget_set_sensitive (s->quote_combo, any_quotes);
1344 gtk_widget_set_sensitive (s->escape_cb, any_quotes);
1347 /* Sets IA's separators substructure to match the widgets. */
1349 get_separators (struct import_assistant *ia)
1351 struct separators_page *s = &ia->separators;
1354 ds_clear (&s->separators);
1355 for (i = 0; i < SEPARATOR_CNT; i++)
1357 const struct separator *sep = &separators[i];
1358 GtkWidget *button = get_widget_assert (ia->asst.xml, sep->name);
1359 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
1360 ds_put_char (&s->separators, sep->c);
1363 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->custom_cb)))
1364 ds_put_cstr (&s->separators,
1365 gtk_entry_get_text (GTK_ENTRY (s->custom_entry)));
1367 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->quote_cb)))
1369 gchar *text = gtk_combo_box_get_active_text (
1370 GTK_COMBO_BOX (s->quote_combo));
1371 ds_assign_cstr (&s->quotes, text);
1375 ds_clear (&s->quotes);
1376 s->escape = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->escape_cb));
1379 /* Called when the user changes the entry field for custom
1382 on_separators_custom_entry_notify (GObject *gobject UNUSED,
1383 GParamSpec *arg1 UNUSED,
1384 struct import_assistant *ia)
1386 revise_fields_preview (ia);
1389 /* Called when the user toggles the checkbox that enables custom
1392 on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
1393 struct import_assistant *ia)
1395 bool is_active = gtk_toggle_button_get_active (custom_cb);
1396 gtk_widget_set_sensitive (ia->separators.custom_entry, is_active);
1397 revise_fields_preview (ia);
1400 /* Called when the user changes the selection in the combo box
1401 that selects a quote character. */
1403 on_quote_combo_change (GtkComboBox *combo, struct import_assistant *ia)
1405 revise_fields_preview (ia);
1408 /* Called when the user toggles the checkbox that enables
1411 on_quote_cb_toggle (GtkToggleButton *quote_cb, struct import_assistant *ia)
1413 bool is_active = gtk_toggle_button_get_active (quote_cb);
1414 gtk_widget_set_sensitive (ia->separators.quote_combo, is_active);
1415 gtk_widget_set_sensitive (ia->separators.escape_cb, is_active);
1416 revise_fields_preview (ia);
1419 /* Called when the user toggles one of the separators
1422 on_separator_toggle (GtkToggleButton *toggle UNUSED,
1423 struct import_assistant *ia)
1425 revise_fields_preview (ia);
1428 /* Called to render one of the cells in the fields preview tree
1431 render_input_cell (GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1432 GtkTreeModel *model, GtkTreeIter *iter,
1435 struct import_assistant *ia = ia_;
1436 struct substring field;
1440 column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1442 row = text_import_model_iter_to_row (iter) + ia->first_line.skip_lines;
1443 field = ia->separators.columns[column].contents[row];
1444 if (field.string != NULL)
1446 GValue text = {0, };
1447 g_value_init (&text, G_TYPE_STRING);
1448 g_value_take_string (&text, ss_xstrdup (field));
1449 g_object_set_property (G_OBJECT (cell), "text", &text);
1450 g_value_unset (&text);
1451 g_object_set (cell, "background-set", FALSE, (void *) NULL);
1456 "background", "red",
1457 "background-set", TRUE,
1461 /* Called to render a tooltip on one of the cells in the fields
1462 preview tree view. */
1464 on_query_input_tooltip (GtkWidget *widget, gint wx, gint wy,
1465 gboolean keyboard_mode UNUSED,
1466 GtkTooltip *tooltip, struct import_assistant *ia)
1470 if (!get_tooltip_location (widget, wx, wy, ia, &row, &column))
1473 if (ia->separators.columns[column].contents[row].string != NULL)
1476 gtk_tooltip_set_text (tooltip,
1477 _("This input line has too few separators "
1478 "to fill in this field."));
1482 /* The "formats" page of the assistant. */
1484 static void on_variable_change (PsppireDict *dict, int idx,
1485 struct import_assistant *);
1486 static void clear_modified_vars (struct import_assistant *);
1488 /* Initializes IA's formats substructure. */
1490 init_formats_page (struct import_assistant *ia)
1492 GladeXML *xml = ia->asst.xml;
1493 struct formats_page *p = &ia->formats;
1495 p->page = add_page_to_assistant (ia, get_widget_assert (xml, "Formats"),
1496 GTK_ASSISTANT_PAGE_CONFIRM);
1497 p->data_tree_view = GTK_TREE_VIEW (get_widget_assert (xml, "data"));
1498 p->modified_vars = NULL;
1499 p->modified_var_cnt = 0;
1502 /* Frees IA's formats substructure. */
1504 destroy_formats_page (struct import_assistant *ia)
1506 struct formats_page *p = &ia->formats;
1508 if (p->psppire_dict != NULL)
1510 /* This destroys p->dict also. */
1511 g_object_unref (p->psppire_dict);
1513 clear_modified_vars (ia);
1516 /* Called just before the formats page of the assistant is
1519 prepare_formats_page (struct import_assistant *ia)
1521 struct dictionary *dict;
1522 PsppireDict *psppire_dict;
1523 PsppireVarStore *var_store;
1524 GtkBin *vars_scroller;
1525 GtkWidget *old_var_sheet;
1526 PsppireVarSheet *var_sheet;
1527 struct separators_page *s = &ia->separators;
1528 struct formats_page *p = &ia->formats;
1529 struct fmt_guesser *fg;
1530 unsigned long int number = 0;
1533 dict = dict_create ();
1534 fg = fmt_guesser_create ();
1535 for (column_idx = 0; column_idx < s->column_cnt; column_idx++)
1537 struct variable *modified_var;
1538 char name[VAR_NAME_LEN + 1];
1540 modified_var = (column_idx < p->modified_var_cnt
1541 ? p->modified_vars[column_idx] : NULL);
1542 if (modified_var == NULL)
1544 struct column *column = &s->columns[column_idx];
1545 struct variable *var;
1546 struct fmt_spec format;
1549 /* Choose variable name. */
1550 if (!dict_make_unique_var_name (dict, column->name, &number, name))
1553 /* Choose variable format. */
1554 fmt_guesser_clear (fg);
1555 for (row = ia->first_line.skip_lines; row < ia->file.line_cnt; row++)
1556 fmt_guesser_add (fg, column->contents[row]);
1557 fmt_guesser_guess (fg, &format);
1558 fmt_fix_input (&format);
1560 /* Create variable. */
1561 var = dict_create_var_assert (dict, name, fmt_var_width (&format));
1562 var_set_both_formats (var, &format);
1566 if (!dict_make_unique_var_name (dict, var_get_name (modified_var),
1569 dict_clone_var_assert (dict, modified_var, name);
1572 fmt_guesser_destroy (fg);
1574 psppire_dict = psppire_dict_new_from_dict (dict);
1575 g_signal_connect (psppire_dict, "variable_changed",
1576 G_CALLBACK (on_variable_change), ia);
1577 ia->formats.dict = dict;
1578 ia->formats.psppire_dict = psppire_dict;
1580 /* XXX: PsppireVarStore doesn't hold a reference to
1581 psppire_dict for now, but it should. After it does, we
1582 should g_object_ref the psppire_dict here, since we also
1583 hold a reference via ia->formats.dict. */
1584 var_store = psppire_var_store_new (psppire_dict);
1585 g_object_set (var_store,
1587 "format-type", PSPPIRE_VAR_STORE_INPUT_FORMATS,
1589 var_sheet = PSPPIRE_VAR_SHEET (psppire_var_sheet_new ());
1590 g_object_set (var_sheet,
1591 "row-geometry", var_store,
1593 "may-create-vars", FALSE,
1596 vars_scroller = GTK_BIN (get_widget_assert (ia->asst.xml, "vars-scroller"));
1597 old_var_sheet = gtk_bin_get_child (vars_scroller);
1598 if (old_var_sheet != NULL)
1599 gtk_widget_destroy (old_var_sheet);
1600 gtk_container_add (GTK_CONTAINER (vars_scroller), GTK_WIDGET (var_sheet));
1601 gtk_widget_show (GTK_WIDGET (var_sheet));
1603 gtk_widget_destroy (GTK_WIDGET (ia->formats.data_tree_view));
1604 ia->formats.data_tree_view = create_data_tree_view (
1606 GTK_CONTAINER (get_widget_assert (ia->asst.xml, "data-scroller")),
1610 /* Clears the set of user-modified variables from IA's formats
1611 substructure. This discards user modifications to variable
1612 formats, thereby causing formats to revert to their
1615 clear_modified_vars (struct import_assistant *ia)
1617 struct formats_page *p = &ia->formats;
1620 for (i = 0; i < p->modified_var_cnt; i++)
1621 var_destroy (p->modified_vars[i]);
1622 free (p->modified_vars);
1623 p->modified_vars = NULL;
1624 p->modified_var_cnt = 0;
1627 /* Resets the formats page to its defaults, discarding user
1630 reset_formats_page (struct import_assistant *ia)
1632 clear_modified_vars (ia);
1633 prepare_formats_page (ia);
1636 /* Called when the user changes one of the variables in the
1639 on_variable_change (PsppireDict *dict, int dict_idx,
1640 struct import_assistant *ia)
1642 struct formats_page *p = &ia->formats;
1643 GtkTreeView *tv = ia->formats.data_tree_view;
1644 gint column_idx = dict_idx + 1;
1646 /* Remove previous column and replace with new column. */
1647 gtk_tree_view_remove_column (tv, gtk_tree_view_get_column (tv, column_idx));
1648 gtk_tree_view_insert_column (tv, make_data_column (ia, tv, false, dict_idx),
1651 /* Save a copy of the modified variable in modified_vars, so
1652 that its attributes will be preserved if we back up to the
1653 previous page with the Prev button and then come back
1655 if (dict_idx >= p->modified_var_cnt)
1658 p->modified_vars = xnrealloc (p->modified_vars, dict_idx + 1,
1659 sizeof *p->modified_vars);
1660 for (i = 0; i <= dict_idx; i++)
1661 p->modified_vars[i] = NULL;
1662 p->modified_var_cnt = dict_idx + 1;
1664 if (p->modified_vars[dict_idx])
1665 var_destroy (p->modified_vars[dict_idx]);
1666 p->modified_vars[dict_idx]
1667 = var_clone (psppire_dict_get_variable (dict, dict_idx));
1670 /* Parses the contents of the field at (ROW,COLUMN) according to
1671 its variable format. If OUTPUTP is non-null, then *OUTPUTP
1672 receives the formatted output for that field (which must be
1673 freed with free). If TOOLTIPP is non-null, then *TOOLTIPP
1674 receives a message suitable for use in a tooltip, if one is
1675 needed, or a null pointer otherwise. Returns true if a
1676 tooltip message is needed, otherwise false. */
1678 parse_field (struct import_assistant *ia,
1679 size_t row, size_t column,
1680 char **outputp, char **tooltipp)
1682 struct substring field;
1684 struct variable *var;
1685 const struct fmt_spec *in;
1686 struct fmt_spec out;
1690 field = ia->separators.columns[column].contents[row];
1691 var = dict_get_var (ia->formats.dict, column);
1692 val = value_create (var_get_width (var));
1693 in = var_get_print_format (var);
1694 out = fmt_for_output_from_input (in);
1696 if (field.string != NULL)
1699 if (!data_in (field, LEGACY_NATIVE, in->type, 0, 0, 0,
1700 val, var_get_width (var)))
1702 char fmt_string[FMT_STRING_LEN_MAX + 1];
1703 fmt_to_string (in, fmt_string);
1704 tooltip = xasprintf (_("Field content \"%.*s\" cannot be parsed in "
1706 (int) field.length, field.string,
1713 tooltip = xstrdup (_("This input line has too few separators "
1714 "to fill in this field."));
1715 value_set_missing (val, var_get_width (var));
1717 if (outputp != NULL)
1719 char *output = xmalloc (out.w + 1);
1720 data_out (val, &out, output);
1721 output[out.w] = '\0';
1726 ok = tooltip == NULL;
1727 if (tooltipp != NULL)
1728 *tooltipp = tooltip;
1734 /* Called to render one of the cells in the data preview tree
1737 render_output_cell (GtkTreeViewColumn *tree_column,
1738 GtkCellRenderer *cell,
1739 GtkTreeModel *model,
1743 struct import_assistant *ia = ia_;
1745 GValue gvalue = { 0, };
1748 ok = parse_field (ia,
1749 (text_import_model_iter_to_row (iter)
1750 + ia->first_line.skip_lines),
1751 GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1755 g_value_init (&gvalue, G_TYPE_STRING);
1756 g_value_take_string (&gvalue, output);
1757 g_object_set_property (G_OBJECT (cell), "text", &gvalue);
1758 g_value_unset (&gvalue);
1761 g_object_set (cell, "background-set", FALSE, (void *) NULL);
1764 "background", "red",
1765 "background-set", TRUE,
1769 /* Called to render a tooltip for one of the cells in the data
1770 preview tree view. */
1772 on_query_output_tooltip (GtkWidget *widget, gint wx, gint wy,
1773 gboolean keyboard_mode UNUSED,
1774 GtkTooltip *tooltip, struct import_assistant *ia)
1779 if (!get_tooltip_location (widget, wx, wy, ia, &row, &column))
1782 if (parse_field (ia, row, column, NULL, &text))
1785 gtk_tooltip_set_text (tooltip, text);
1790 /* Utility functions used by multiple pages of the assistant. */
1793 get_tooltip_location (GtkWidget *widget, gint wx, gint wy,
1794 const struct import_assistant *ia,
1795 size_t *row, size_t *column)
1797 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
1801 GtkTreeViewColumn *tree_column;
1802 GtkTreeModel *tree_model;
1805 gtk_tree_view_convert_widget_to_bin_window_coords (tree_view,
1807 if (!gtk_tree_view_get_path_at_pos (tree_view, bx, by,
1808 &path, &tree_column, NULL, NULL))
1811 *column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1814 tree_model = gtk_tree_view_get_model (tree_view);
1815 ok = gtk_tree_model_get_iter (tree_model, &iter, path);
1816 gtk_tree_path_free (path);
1820 *row = text_import_model_iter_to_row (&iter) + ia->first_line.skip_lines;
1825 make_tree_view (const struct import_assistant *ia,
1827 GtkTreeView **tree_view)
1829 GtkTreeModel *model;
1831 *tree_view = GTK_TREE_VIEW (gtk_tree_view_new ());
1832 model = GTK_TREE_MODEL (text_import_model_new (
1833 ia->file.lines + first_line,
1834 ia->file.line_cnt - first_line, first_line));
1835 gtk_tree_view_set_model (*tree_view, model);
1837 add_line_number_column (ia, *tree_view);
1841 add_line_number_column (const struct import_assistant *ia,
1842 GtkTreeView *treeview)
1844 GtkTreeViewColumn *column;
1846 column = gtk_tree_view_column_new_with_attributes (
1847 "Line", ia->asst.prop_renderer,
1848 "text", TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER,
1850 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
1851 gtk_tree_view_column_set_fixed_width (
1852 column, get_monospace_width (treeview, ia->asst.prop_renderer, 5));
1853 gtk_tree_view_append_column (treeview, column);
1857 get_monospace_width (GtkTreeView *treeview, GtkCellRenderer *renderer,
1864 ds_put_char_multiple (&s, '0', char_cnt);
1865 ds_put_char (&s, ' ');
1866 width = get_string_width (treeview, renderer, ds_cstr (&s));
1873 get_string_width (GtkTreeView *treeview, GtkCellRenderer *renderer,
1877 g_object_set (G_OBJECT (renderer), "text", string, (void *) NULL);
1878 gtk_cell_renderer_get_size (renderer, GTK_WIDGET (treeview),
1879 NULL, NULL, NULL, &width, NULL);
1883 static GtkTreeViewColumn *
1884 make_data_column (struct import_assistant *ia, GtkTreeView *tree_view,
1885 bool input, gint dict_idx)
1887 struct variable *var = NULL;
1888 struct column *column = NULL;
1889 char name[(VAR_NAME_LEN * 2) + 1];
1891 gint content_width, header_width;
1892 GtkTreeViewColumn *tree_column;
1895 column = &ia->separators.columns[dict_idx];
1897 var = dict_get_var (ia->formats.dict, dict_idx);
1899 escape_underscores (input ? column->name : var_get_name (var), name);
1900 char_cnt = input ? column->width : var_get_print_format (var)->w;
1901 content_width = get_monospace_width (tree_view, ia->asst.fixed_renderer,
1903 header_width = get_string_width (tree_view, ia->asst.prop_renderer,
1906 tree_column = gtk_tree_view_column_new ();
1907 g_object_set_data (G_OBJECT (tree_column), "column-number",
1908 GINT_TO_POINTER (dict_idx));
1909 gtk_tree_view_column_set_title (tree_column, name);
1910 gtk_tree_view_column_pack_start (tree_column, ia->asst.fixed_renderer,
1912 gtk_tree_view_column_set_cell_data_func (
1913 tree_column, ia->asst.fixed_renderer,
1914 input ? render_input_cell : render_output_cell, ia, NULL);
1915 gtk_tree_view_column_set_sizing (tree_column, GTK_TREE_VIEW_COLUMN_FIXED);
1916 gtk_tree_view_column_set_fixed_width (tree_column, MAX (content_width,
1922 static GtkTreeView *
1923 create_data_tree_view (bool input, GtkContainer *parent,
1924 struct import_assistant *ia)
1926 GtkTreeView *tree_view;
1929 make_tree_view (ia, ia->first_line.skip_lines, &tree_view);
1930 gtk_tree_selection_set_mode (gtk_tree_view_get_selection (tree_view),
1931 GTK_SELECTION_NONE);
1933 for (i = 0; i < ia->separators.column_cnt; i++)
1934 gtk_tree_view_append_column (tree_view,
1935 make_data_column (ia, tree_view, input, i));
1937 g_object_set (G_OBJECT (tree_view), "has-tooltip", TRUE, (void *) NULL);
1938 g_signal_connect (tree_view, "query-tooltip",
1939 G_CALLBACK (input ? on_query_input_tooltip
1940 : on_query_output_tooltip), ia);
1941 gtk_tree_view_set_fixed_height_mode (tree_view, true);
1943 gtk_container_add (parent, GTK_WIDGET (tree_view));
1944 gtk_widget_show (GTK_WIDGET (tree_view));
1950 escape_underscores (const char *in, char *out)
1952 for (; *in != '\0'; in++)
1961 /* TextImportModel, a GtkTreeModel implementation used by some
1962 pages of the assistant. */
1964 #define G_TYPE_TEXT_IMPORT_MODEL (text_import_model_get_type ())
1965 #define TEXT_IMPORT_MODEL(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), G_TYPE_TEXT_IMPORT_MODEL, TextImportModel))
1966 #define TEXT_IMPORT_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), G_TYPE_TEXT_IMPORT_MODEL, TextImportModelClass))
1967 #define IS_TEXT_IMPORT_MODEL(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), G_TYPE_TEXT_IMPORT_MODEL))
1968 #define IS_TEXT_IMPORT_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), G_TYPE_TEXT_IMPORT_MODEL))
1969 #define TEXT_IMPORT_MODEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), G_TYPE_TEXT_IMPORT_MODEL, TextImportModelClass))
1971 /* Random number used in 'stamp' member of GtkTreeIter. */
1972 #define TREE_MODEL_STAMP 0x7efd67d3
1974 struct TextImportModel
1977 struct string *lines;
1982 struct TextImportModelClass
1984 GObjectClass parent_class;
1987 GType text_import_model_get_type (void);
1988 static void text_import_model_tree_model_init (gpointer iface, gpointer data);
1991 text_import_model_get_type (void)
1993 static GType object_type = 0;
1997 static const GTypeInfo object_info = {
1998 sizeof (TextImportModelClass),
1999 (GBaseInitFunc) NULL,
2000 (GBaseFinalizeFunc) NULL,
2001 NULL, /* class_init */
2002 NULL, /* class_finalize */
2003 NULL, /* class_data */
2004 sizeof (TextImportModel),
2005 0, /* n_preallocs */
2006 NULL, /* instance_init */
2009 static const GInterfaceInfo tree_model_info = {
2010 text_import_model_tree_model_init,
2015 object_type = g_type_register_static (G_TYPE_OBJECT,
2019 g_type_add_interface_static (object_type, GTK_TYPE_TREE_MODEL,
2029 /* Creates and returns a new TextImportModel that contains the
2030 LINE_CNT lines in LINES. The lines before FIRST_LINE in LINES
2031 are not part of the model, but they are included in the line
2032 numbers in the TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER column.
2034 The caller retains responsibility for freeing LINES and must
2035 ensure that its lifetime and that of the strings that it
2036 contains exceeds that of the TextImportModel. */
2038 text_import_model_new (struct string *lines, size_t line_cnt,
2041 TextImportModel *new_text_import_model
2042 = g_object_new (G_TYPE_TEXT_IMPORT_MODEL, NULL);
2043 new_text_import_model->lines = lines;
2044 new_text_import_model->line_cnt = line_cnt;
2045 new_text_import_model->first_line = first_line;
2046 return new_text_import_model;
2051 tree_model_iter_has_child (GtkTreeModel *tree_model,
2058 tree_model_iter_parent (GtkTreeModel *tree_model,
2065 static GtkTreeModelFlags
2066 tree_model_get_flags (GtkTreeModel *model)
2068 g_return_val_if_fail (IS_TEXT_IMPORT_MODEL (model), (GtkTreeModelFlags) 0);
2070 return GTK_TREE_MODEL_LIST_ONLY | GTK_TREE_MODEL_ITERS_PERSIST;
2075 tree_model_n_columns (GtkTreeModel *model)
2081 tree_model_column_type (GtkTreeModel *model, gint index)
2083 return (index == TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER ? G_TYPE_INT
2084 : index == TEXT_IMPORT_MODEL_COLUMN_LINE ? G_TYPE_STRING
2089 init_iter (TextImportModel *list, gint idx, GtkTreeIter *iter)
2091 if (idx < 0 || idx >= list->line_cnt)
2094 iter->user_data = GINT_TO_POINTER (-1);
2099 iter->stamp = TREE_MODEL_STAMP;
2100 iter->user_data = GINT_TO_POINTER (idx);
2106 tree_model_get_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreePath *path)
2108 gint *indices, depth;
2110 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2112 g_return_val_if_fail (path, FALSE);
2114 indices = gtk_tree_path_get_indices (path);
2115 depth = gtk_tree_path_get_depth (path);
2117 g_return_val_if_fail (depth == 1, FALSE);
2119 return init_iter (list, indices[0], iter);
2124 tree_model_iter_next (GtkTreeModel *model, GtkTreeIter *iter)
2126 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2129 assert (iter->stamp == TREE_MODEL_STAMP);
2131 idx = GPOINTER_TO_INT (iter->user_data);
2132 return init_iter (list, idx == -1 ? -1 : idx + 1, iter);
2135 static GtkTreePath *
2136 tree_model_get_path (GtkTreeModel *model, GtkTreeIter *iter)
2140 g_return_val_if_fail (iter->stamp == TREE_MODEL_STAMP, FALSE);
2142 path = gtk_tree_path_new ();
2143 gtk_tree_path_append_index (path, GPOINTER_TO_INT (iter->user_data));
2149 tree_model_get_value (GtkTreeModel *model, GtkTreeIter *iter,
2150 gint column, GValue *value)
2152 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2155 g_return_if_fail (iter->stamp == TREE_MODEL_STAMP);
2157 idx = GPOINTER_TO_INT (iter->user_data);
2158 assert (idx >= 0 && idx < list->line_cnt);
2162 g_value_init (value, G_TYPE_INT);
2163 g_value_set_int (value, idx + list->first_line + 1);
2167 g_value_init (value, G_TYPE_STRING);
2168 g_value_set_static_string (value, ds_cstr (&list->lines[idx]));
2173 tree_model_iter_children (GtkTreeModel *tree_model,
2175 GtkTreeIter *parent)
2181 tree_model_n_children (GtkTreeModel *model, GtkTreeIter *iter)
2183 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2185 return iter == NULL ? list->line_cnt : 0;
2189 tree_model_nth_child (GtkTreeModel *model, GtkTreeIter *iter,
2190 GtkTreeIter *parent, gint n)
2192 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2193 g_return_val_if_fail (IS_TEXT_IMPORT_MODEL (model), FALSE);
2197 return init_iter (list, n, iter);
2201 text_import_model_tree_model_init (gpointer iface_, gpointer data UNUSED)
2203 GtkTreeModelIface *iface = (GtkTreeModelIface *) iface_;
2205 iface->get_flags = tree_model_get_flags;
2206 iface->get_n_columns = tree_model_n_columns;
2207 iface->get_column_type = tree_model_column_type;
2208 iface->get_iter = tree_model_get_iter;
2209 iface->iter_next = tree_model_iter_next;
2210 iface->get_path = tree_model_get_path;
2211 iface->get_value = tree_model_get_value;
2213 iface->iter_children = tree_model_iter_children;
2214 iface->iter_has_child = tree_model_iter_has_child;
2215 iface->iter_n_children = tree_model_n_children;
2216 iface->iter_nth_child = tree_model_nth_child;
2217 iface->iter_parent = tree_model_iter_parent;
2221 text_import_model_iter_to_row (const GtkTreeIter *iter)
2223 assert (iter->stamp == TREE_MODEL_STAMP);
2224 return GPOINTER_TO_INT (iter->user_data);