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;
234 ia = xzalloc (sizeof *ia);
235 if (!init_file (ia, parent_window))
241 init_assistant (ia, parent_window);
242 init_intro_page (ia);
243 init_first_line_page (ia);
244 init_separators_page (ia);
245 init_formats_page (ia);
247 gtk_widget_show_all (GTK_WIDGET (ia->asst.assistant));
249 ia->asst.main_loop = g_main_loop_new (NULL, false);
250 g_main_loop_run (ia->asst.main_loop);
251 g_main_loop_unref (ia->asst.main_loop);
253 switch (ia->asst.response)
255 case GTK_RESPONSE_APPLY:
257 char *syntax = generate_syntax (ia);
258 execute_syntax (create_syntax_string_source (syntax));
262 case PSPPIRE_RESPONSE_PASTE:
264 char *syntax = generate_syntax (ia);
265 struct syntax_editor *se =
266 (struct syntax_editor *) window_create (WINDOW_SYNTAX, NULL);
267 gtk_text_buffer_insert_at_cursor (se->buffer, syntax, -1);
275 destroy_formats_page (ia);
276 destroy_separators_page (ia);
277 destroy_assistant (ia);
282 /* Emits PSPP syntax to S that applies the dictionary attributes
283 (such as missing values and value labels) of the variables in
286 apply_dict (const struct dictionary *dict, struct string *s)
288 size_t var_cnt = dict_get_var_cnt (dict);
291 for (i = 0; i < var_cnt; i++)
293 struct variable *var = dict_get_var (dict, i);
294 const char *name = var_get_name (var);
295 enum val_type type = var_get_type (var);
296 int width = var_get_width (var);
297 enum measure measure = var_get_measure (var);
298 enum alignment alignment = var_get_alignment (var);
299 const struct fmt_spec *format = var_get_print_format (var);
301 if (var_has_missing_values (var))
303 const struct missing_values *mv = var_get_missing_values (var);
306 syntax_gen_pspp (s, "MISSING VALUES %ss (", name);
307 for (j = 0; j < mv_n_values (mv); j++)
311 ds_put_cstr (s, ", ");
312 mv_get_value (mv, &value, j);
313 syntax_gen_value (s, &value, width, format);
316 if (mv_has_range (mv))
319 if (mv_has_value (mv))
320 ds_put_cstr (s, ", ");
321 mv_get_range (mv, &low, &high);
322 syntax_gen_num_range (s, low, high, format);
324 ds_put_cstr (s, ").\n");
326 if (var_has_value_labels (var))
328 const struct val_labs *vls = var_get_value_labels (var);
329 struct val_labs_iterator *iter;
332 syntax_gen_pspp (s, "VALUE LABELS %ss", name);
333 for (vl = val_labs_first_sorted (vls, &iter); vl != NULL;
334 vl = val_labs_next (vls, &iter))
336 ds_put_cstr (s, "\n ");
337 syntax_gen_value (s, &vl->value, width, format);
338 ds_put_char (s, ' ');
339 syntax_gen_string (s, ss_cstr (vl->label));
341 ds_put_cstr (s, ".\n");
343 if (var_has_label (var))
344 syntax_gen_pspp (s, "VARIABLE LABELS %ss %sq.\n",
345 name, var_get_label (var));
346 if (measure != var_default_measure (type))
347 syntax_gen_pspp (s, "VARIABLE LEVEL %ss (%ss).\n",
349 (measure == MEASURE_NOMINAL ? "NOMINAL"
350 : measure == MEASURE_ORDINAL ? "ORDINAL"
352 if (alignment != var_default_alignment (type))
353 syntax_gen_pspp (s, "VARIABLE ALIGNMENT %ss (%ss).\n",
355 (alignment == ALIGN_LEFT ? "LEFT"
356 : alignment == ALIGN_CENTRE ? "CENTER"
358 if (var_get_display_width (var) != var_default_display_width (width))
359 syntax_gen_pspp (s, "VARIABLE WIDTH %ss (%d).\n",
360 name, var_get_display_width (var));
364 /* Generates and returns PSPP syntax to execute the import
365 operation described by IA. The caller must free the syntax
368 generate_syntax (const struct import_assistant *ia)
370 struct string s = DS_EMPTY_INITIALIZER;
379 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (
380 ia->intro.n_cases_button)))
381 ds_put_format (&s, " /IMPORTCASES=FIRST %d\n",
382 gtk_spin_button_get_value_as_int (
383 GTK_SPIN_BUTTON (ia->intro.n_cases_spin)));
384 else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (
385 ia->intro.percent_button)))
386 ds_put_format (&s, " /IMPORTCASES=PERCENT %d\n",
387 gtk_spin_button_get_value_as_int (
388 GTK_SPIN_BUTTON (ia->intro.percent_spin)));
390 ds_put_cstr (&s, " /IMPORTCASES=ALL\n");
392 " /ARRANGEMENT=DELIMITED\n"
394 if (ia->first_line.skip_lines > 0)
395 ds_put_format (&s, " /FIRSTCASE=%d\n", ia->first_line.skip_lines + 1);
396 ds_put_cstr (&s, " /DELIMITERS=\"");
397 if (ds_find_char (&ia->separators.separators, '\t') != SIZE_MAX)
398 ds_put_cstr (&s, "\\t");
399 if (ds_find_char (&ia->separators.separators, '\\') != SIZE_MAX)
400 ds_put_cstr (&s, "\\\\");
401 for (i = 0; i < ds_length (&ia->separators.separators); i++)
403 char c = ds_at (&ia->separators.separators, i);
405 ds_put_cstr (&s, "\"\"");
406 else if (c != '\t' && c != '\\')
409 ds_put_cstr (&s, "\"\n");
410 if (!ds_is_empty (&ia->separators.quotes))
411 syntax_gen_pspp (&s, " /QUALIFIER=%sq\n", ds_cstr (&ia->separators.quotes));
412 if (!ds_is_empty (&ia->separators.quotes) && ia->separators.escape)
413 ds_put_cstr (&s, " /ESCAPE\n");
414 ds_put_cstr (&s, " /VARIABLES=\n");
416 var_cnt = dict_get_var_cnt (ia->formats.dict);
417 for (i = 0; i < var_cnt; i++)
419 struct variable *var = dict_get_var (ia->formats.dict, i);
420 char format_string[FMT_STRING_LEN_MAX + 1];
421 fmt_to_string (var_get_print_format (var), format_string);
422 ds_put_format (&s, " %s %s%s\n",
423 var_get_name (var), format_string,
424 i == var_cnt - 1 ? "." : "");
427 apply_dict (ia->formats.dict, &s);
432 /* Choosing a file and reading it. */
434 static char *choose_file (GtkWindow *parent_window);
436 /* Obtains the file to import from the user and initializes IA's
437 file substructure. PARENT_WINDOW must be the window to use
438 as the file chooser window's parent.
440 Returns true if successful, false if the file name could not
441 be obtained or the file could not be read. */
443 init_file (struct import_assistant *ia, GtkWindow *parent_window)
445 struct file *file = &ia->file;
446 enum { MAX_PREVIEW_LINES = 1000 }; /* Max number of lines to read. */
447 enum { MAX_LINE_LEN = 16384 }; /* Max length of an acceptable line. */
450 file->file_name = choose_file (parent_window);
451 if (file->file_name == NULL)
454 stream = fopen (file->file_name, "r");
457 msg (ME, _("Could not open \"%s\": %s"),
458 file->file_name, strerror (errno));
462 file->lines = xnmalloc (MAX_PREVIEW_LINES, sizeof *file->lines);
463 for (; file->line_cnt < MAX_PREVIEW_LINES; file->line_cnt++)
465 struct string *line = &file->lines[file->line_cnt];
467 ds_init_empty (line);
468 if (!ds_read_line (line, stream, MAX_LINE_LEN))
472 else if (ferror (stream))
473 msg (ME, _("Error reading \"%s\": %s"),
474 file->file_name, strerror (errno));
476 msg (ME, _("Failed to read \"%s\", because it contains a line "
477 "over %d bytes long and therefore appears not to be "
479 file->file_name, MAX_LINE_LEN);
484 ds_chomp (line, '\n');
485 ds_chomp (line, '\r');
488 if (file->line_cnt == 0)
490 msg (ME, _("\"%s\" is empty."), file->file_name);
496 /* Estimate the number of lines in the file. */
497 if (file->line_cnt < MAX_PREVIEW_LINES)
498 file->total_lines = file->line_cnt;
502 off_t position = ftello (stream);
503 if (fstat (fileno (stream), &s) == 0 && position > 0)
504 file->total_lines = (double) file->line_cnt / position * s.st_size;
506 file->total_lines = 0;
512 /* Frees IA's file substructure. */
514 destroy_file (struct import_assistant *ia)
516 struct file *f = &ia->file;
519 for (i = 0; i < f->line_cnt; i++)
520 ds_destroy (&f->lines[i]);
522 g_free (f->file_name);
525 /* Obtains the file to read from the user and returns the name of
526 the file as a string that must be freed with g_free if
527 successful, otherwise a null pointer. PARENT_WINDOW must be
528 the window to use as the file chooser window's parent. */
530 choose_file (GtkWindow *parent_window)
535 dialog = gtk_file_chooser_dialog_new (_("Import Delimited Text Data"),
537 GTK_FILE_CHOOSER_ACTION_OPEN,
538 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
539 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
542 switch (gtk_dialog_run (GTK_DIALOG (dialog)))
544 case GTK_RESPONSE_ACCEPT:
545 file_name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
551 gtk_widget_destroy (dialog);
558 static void close_assistant (struct import_assistant *, int response);
559 static void on_prepare (GtkAssistant *assistant, GtkWidget *page,
560 struct import_assistant *);
561 static void on_cancel (GtkAssistant *assistant, struct import_assistant *);
562 static void on_close (GtkAssistant *assistant, struct import_assistant *);
563 static void on_paste (GtkButton *button, struct import_assistant *);
564 static void on_reset (GtkButton *button, struct import_assistant *);
565 static void close_assistant (struct import_assistant *, int response);
567 /* Initializes IA's asst substructure. PARENT_WINDOW must be the
568 window to use as the assistant window's parent. */
570 init_assistant (struct import_assistant *ia, GtkWindow *parent_window)
572 struct assistant *a = &ia->asst;
574 a->xml = XML_NEW ("text-data-import.glade");
575 a->assistant = GTK_ASSISTANT (gtk_assistant_new ());
576 g_signal_connect (a->assistant, "prepare", G_CALLBACK (on_prepare), ia);
577 g_signal_connect (a->assistant, "cancel", G_CALLBACK (on_cancel), ia);
578 g_signal_connect (a->assistant, "close", G_CALLBACK (on_close), ia);
579 a->paste_button = gtk_button_new_from_stock (GTK_STOCK_PASTE);
580 gtk_assistant_add_action_widget (a->assistant, a->paste_button);
581 g_signal_connect (a->paste_button, "clicked", G_CALLBACK (on_paste), ia);
582 a->reset_button = gtk_button_new_from_stock ("pspp-stock-reset");
583 gtk_assistant_add_action_widget (a->assistant, a->reset_button);
584 g_signal_connect (a->reset_button, "clicked", G_CALLBACK (on_reset), ia);
585 gtk_window_set_title (GTK_WINDOW (a->assistant),
586 _("Importing Delimited Text Data"));
587 gtk_window_set_transient_for (GTK_WINDOW (a->assistant), parent_window);
589 a->prop_renderer = gtk_cell_renderer_text_new ();
590 g_object_ref_sink (a->prop_renderer);
591 a->fixed_renderer = gtk_cell_renderer_text_new ();
592 g_object_ref_sink (a->fixed_renderer);
593 g_object_set (G_OBJECT (a->fixed_renderer),
594 "family", "Monospace",
598 /* Frees IA's asst substructure. */
600 destroy_assistant (struct import_assistant *ia)
602 struct assistant *a = &ia->asst;
604 g_object_unref (a->prop_renderer);
605 g_object_unref (a->fixed_renderer);
606 g_object_unref (a->xml);
609 /* Appends a page of the given TYPE, with PAGE as its content, to
610 the GtkAssistant encapsulated by IA. Returns the GtkWidget
611 that represents the page. */
613 add_page_to_assistant (struct import_assistant *ia,
614 GtkWidget *page, GtkAssistantPageType type)
620 title = gtk_window_get_title (GTK_WINDOW (page));
621 title_copy = xstrdup (title ? title : "");
623 content = gtk_bin_get_child (GTK_BIN (page));
625 g_object_ref (content);
626 gtk_container_remove (GTK_CONTAINER (page), content);
628 gtk_widget_destroy (page);
630 gtk_assistant_append_page (ia->asst.assistant, content);
631 gtk_assistant_set_page_type (ia->asst.assistant, content, type);
632 gtk_assistant_set_page_title (ia->asst.assistant, content, title_copy);
639 /* Called just before PAGE is displayed as the current page of
640 ASSISTANT, this updates IA content according to the new
643 on_prepare (GtkAssistant *assistant, GtkWidget *page,
644 struct import_assistant *ia)
646 if (page == ia->separators.page)
647 prepare_separators_page (ia);
648 else if (page == ia->formats.page)
649 prepare_formats_page (ia);
651 gtk_widget_show (ia->asst.reset_button);
652 if (page == ia->formats.page)
653 gtk_widget_show (ia->asst.paste_button);
655 gtk_widget_hide (ia->asst.paste_button);
657 /* Make the user visit each page in the assistant once. Seems
658 like a reasonable user interface, plus visiting the final
659 page is what constructs the dictionary. */
660 gtk_assistant_set_page_complete (assistant, page, true);
663 /* Called when the Cancel button in the assistant is clicked. */
665 on_cancel (GtkAssistant *assistant, struct import_assistant *ia)
667 close_assistant (ia, GTK_RESPONSE_CANCEL);
670 /* Called when the Apply button on the last page of the assistant
673 on_close (GtkAssistant *assistant, struct import_assistant *ia)
675 close_assistant (ia, GTK_RESPONSE_APPLY);
678 /* Called when the Paste button on the last page of the assistant
681 on_paste (GtkButton *button, struct import_assistant *ia)
683 close_assistant (ia, PSPPIRE_RESPONSE_PASTE);
686 /* Called when the Reset button is clicked. */
688 on_reset (GtkButton *button, struct import_assistant *ia)
690 gint page_num = gtk_assistant_get_current_page (ia->asst.assistant);
691 GtkWidget *page = gtk_assistant_get_nth_page (ia->asst.assistant, page_num);
693 if (page == ia->intro.page)
694 reset_intro_page (ia);
695 else if (page == ia->first_line.page)
696 reset_first_line_page (ia);
697 else if (page == ia->separators.page)
698 reset_separators_page (ia);
699 else if (page == ia->formats.page)
700 reset_formats_page (ia);
703 /* Causes the assistant to close, returning RESPONSE for
704 interpretation by text_data_import_assistant. */
706 close_assistant (struct import_assistant *ia, int response)
708 ia->asst.response = response;
709 g_main_loop_quit (ia->asst.main_loop);
710 gtk_widget_hide (GTK_WIDGET (ia->asst.assistant));
713 /* The "intro" page of the assistant. */
715 static void on_intro_amount_changed (GtkToggleButton *button,
716 struct import_assistant *);
718 /* Initializes IA's intro substructure. */
720 init_intro_page (struct import_assistant *ia)
722 GladeXML *xml = ia->asst.xml;
723 struct intro_page *p = &ia->intro;
726 p->page = add_page_to_assistant (ia, get_widget_assert (xml, "Intro"),
727 GTK_ASSISTANT_PAGE_INTRO);
728 p->all_cases_button = get_widget_assert (xml, "import-all-cases");
729 p->n_cases_button = get_widget_assert (xml, "import-n-cases");
730 p->n_cases_spin = get_widget_assert (xml, "n-cases-spin");
731 p->percent_button = get_widget_assert (xml, "import-percent");
732 p->percent_spin = get_widget_assert (xml, "percent-spin");
733 g_signal_connect (p->all_cases_button, "toggled",
734 G_CALLBACK (on_intro_amount_changed), ia);
735 g_signal_connect (p->n_cases_button, "toggled",
736 G_CALLBACK (on_intro_amount_changed), ia);
737 g_signal_connect (p->percent_button, "toggled",
738 G_CALLBACK (on_intro_amount_changed), ia);
741 ds_put_cstr (&s, _("This assistant will guide you through the process of "
742 "importing data into PSPP from a text file with one line "
743 "per case, in which fields are separated by tabs, "
744 "commas, or other delimiters.\n\n"));
745 if (ia->file.total_is_exact)
747 &s, ngettext ("The selected file contains %zu line of text. ",
748 "The selected file contains %zu lines of text. ",
751 else if (ia->file.total_lines > 0)
755 "The selected file contains approximately %lu line of text. ",
756 "The selected file contains approximately %lu lines of text. ",
757 ia->file.total_lines),
758 ia->file.total_lines);
761 "Only the first %zu line of the file will be shown for "
762 "preview purposes in the following screens. ",
763 "Only the first %zu lines of the file will be shown for "
764 "preview purposes in the following screens. ",
768 ds_put_cstr (&s, _("You may choose below how much of the file should "
769 "actually be imported."));
770 gtk_label_set_text (GTK_LABEL (get_widget_assert (xml, "intro-label")),
775 /* Resets IA's intro page to its initial state. */
777 reset_intro_page (struct import_assistant *ia)
779 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->intro.all_cases_button),
783 /* Called when one of the radio buttons is clicked. */
785 on_intro_amount_changed (GtkToggleButton *button UNUSED,
786 struct import_assistant *ia)
788 struct intro_page *p = &ia->intro;
790 gtk_widget_set_sensitive (p->n_cases_spin,
791 gtk_toggle_button_get_active (
792 GTK_TOGGLE_BUTTON (p->n_cases_button)));
794 gtk_widget_set_sensitive (ia->intro.percent_spin,
795 gtk_toggle_button_get_active (
796 GTK_TOGGLE_BUTTON (p->percent_button)));
799 /* The "first line" page of the assistant. */
801 static GtkTreeView *create_lines_tree_view (GtkContainer *parent_window,
802 struct import_assistant *);
803 static void on_first_line_change (GtkTreeSelection *,
804 struct import_assistant *);
805 static void on_variable_names_cb_toggle (GtkToggleButton *,
806 struct import_assistant *);
807 static void set_first_line (struct import_assistant *);
808 static void get_first_line (struct import_assistant *);
810 /* Initializes IA's first_line substructure. */
812 init_first_line_page (struct import_assistant *ia)
814 struct first_line_page *p = &ia->first_line;
815 GladeXML *xml = ia->asst.xml;
817 p->page = add_page_to_assistant (ia, get_widget_assert (xml, "FirstLine"),
818 GTK_ASSISTANT_PAGE_CONTENT);
819 gtk_widget_destroy (get_widget_assert (xml, "first-line"));
820 p->tree_view = create_lines_tree_view (
821 GTK_CONTAINER (get_widget_assert (xml, "first-line-scroller")), ia);
822 p->variable_names_cb = get_widget_assert (xml, "variable-names");
823 gtk_tree_selection_set_mode (
824 gtk_tree_view_get_selection (GTK_TREE_VIEW (p->tree_view)),
825 GTK_SELECTION_BROWSE);
827 g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (p->tree_view)),
828 "changed", G_CALLBACK (on_first_line_change), ia);
829 g_signal_connect (p->variable_names_cb, "toggled",
830 G_CALLBACK (on_variable_names_cb_toggle), ia);
833 /* Resets the first_line page to its initial content. */
835 reset_first_line_page (struct import_assistant *ia)
837 ia->first_line.skip_lines = 0;
838 ia->first_line.variable_names = false;
842 /* Creates and returns a tree view that contains each of the
843 lines in IA's file as a row. */
845 create_lines_tree_view (GtkContainer *parent, struct import_assistant *ia)
847 GtkTreeView *tree_view;
848 GtkTreeViewColumn *column;
849 size_t max_line_length;
850 gint content_width, header_width;
853 make_tree_view (ia, 0, &tree_view);
855 column = gtk_tree_view_column_new_with_attributes (
856 "Text", ia->asst.fixed_renderer,
857 "text", TEXT_IMPORT_MODEL_COLUMN_LINE,
859 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
862 for (i = 0; i < ia->file.line_cnt; i++)
864 size_t w = ds_length (&ia->file.lines[i]);
865 max_line_length = MAX (max_line_length, w);
868 content_width = get_monospace_width (tree_view, ia->asst.fixed_renderer,
870 header_width = get_string_width (tree_view, ia->asst.prop_renderer, "Text");
871 gtk_tree_view_column_set_fixed_width (column, MAX (content_width,
873 gtk_tree_view_append_column (tree_view, column);
875 gtk_tree_view_set_fixed_height_mode (tree_view, true);
877 gtk_container_add (parent, GTK_WIDGET (tree_view));
878 gtk_widget_show (GTK_WIDGET (tree_view));
883 /* Called when the line selected in the first_line tree view
886 on_first_line_change (GtkTreeSelection *selection UNUSED,
887 struct import_assistant *ia)
892 /* Called when the checkbox that indicates whether variable
893 names are in the row above the first line is toggled. */
895 on_variable_names_cb_toggle (GtkToggleButton *variable_names_cb UNUSED,
896 struct import_assistant *ia)
901 /* Sets the widgets to match IA's first_line substructure. */
903 set_first_line (struct import_assistant *ia)
907 path = gtk_tree_path_new_from_indices (ia->first_line.skip_lines, -1);
908 gtk_tree_view_set_cursor (GTK_TREE_VIEW (ia->first_line.tree_view),
910 gtk_tree_path_free (path);
912 gtk_toggle_button_set_active (
913 GTK_TOGGLE_BUTTON (ia->first_line.variable_names_cb),
914 ia->first_line.variable_names);
915 gtk_widget_set_sensitive (ia->first_line.variable_names_cb,
916 ia->first_line.skip_lines > 0);
919 /* Sets IA's first_line substructure to match the widgets. */
921 get_first_line (struct import_assistant *ia)
923 GtkTreeSelection *selection;
927 selection = gtk_tree_view_get_selection (ia->first_line.tree_view);
928 if (gtk_tree_selection_get_selected (selection, &model, &iter))
930 GtkTreePath *path = gtk_tree_model_get_path (model, &iter);
931 int row = gtk_tree_path_get_indices (path)[0];
932 gtk_tree_path_free (path);
934 ia->first_line.skip_lines = row;
935 ia->first_line.variable_names =
936 (ia->first_line.skip_lines > 0
937 && gtk_toggle_button_get_active (
938 GTK_TOGGLE_BUTTON (ia->first_line.variable_names_cb)));
940 gtk_widget_set_sensitive (ia->first_line.variable_names_cb,
941 ia->first_line.skip_lines > 0);
944 /* The "separators" page of the assistant. */
946 static void revise_fields_preview (struct import_assistant *ia);
947 static void choose_likely_separators (struct import_assistant *ia);
948 static void find_commonest_chars (unsigned long int histogram[UCHAR_MAX + 1],
949 const char *targets, const char *def,
950 struct string *result);
951 static void clear_fields (struct import_assistant *ia);
952 static void revise_fields_preview (struct import_assistant *);
953 static void set_separators (struct import_assistant *);
954 static void get_separators (struct import_assistant *);
955 static void on_separators_custom_entry_notify (GObject *UNUSED,
957 struct import_assistant *);
958 static void on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
959 struct import_assistant *);
960 static void on_quote_combo_change (GtkComboBox *combo,
961 struct import_assistant *);
962 static void on_quote_cb_toggle (GtkToggleButton *quote_cb,
963 struct import_assistant *);
964 static void on_separator_toggle (GtkToggleButton *, struct import_assistant *);
965 static void render_input_cell (GtkTreeViewColumn *tree_column,
966 GtkCellRenderer *cell,
967 GtkTreeModel *model, GtkTreeIter *iter,
969 static gboolean on_query_input_tooltip (GtkWidget *widget, gint wx, gint wy,
970 gboolean keyboard_mode UNUSED,
972 struct import_assistant *);
974 /* A common field separator and its identifying name. */
977 const char *name; /* Name (for use with get_widget_assert). */
978 int c; /* Separator character. */
981 /* All the separators in the dialog box. */
982 static const struct separator separators[] =
994 #define SEPARATOR_CNT (sizeof separators / sizeof *separators)
996 /* Initializes IA's separators substructure. */
998 init_separators_page (struct import_assistant *ia)
1000 GladeXML *xml = ia->asst.xml;
1001 struct separators_page *p = &ia->separators;
1004 choose_likely_separators (ia);
1006 p->page = add_page_to_assistant (ia, get_widget_assert (xml, "Separators"),
1007 GTK_ASSISTANT_PAGE_CONTENT);
1008 p->custom_cb = get_widget_assert (xml, "custom-cb");
1009 p->custom_entry = get_widget_assert (xml, "custom-entry");
1010 p->quote_combo = get_widget_assert (xml, "quote-combo");
1011 p->quote_entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (p->quote_combo)));
1012 p->quote_cb = get_widget_assert (xml, "quote-cb");
1013 p->escape_cb = get_widget_assert (xml, "escape");
1015 set_separators (ia);
1016 p->fields_tree_view = GTK_TREE_VIEW (get_widget_assert (xml, "fields"));
1017 g_signal_connect (GTK_COMBO_BOX (p->quote_combo), "changed",
1018 G_CALLBACK (on_quote_combo_change), ia);
1019 g_signal_connect (p->quote_cb, "toggled",
1020 G_CALLBACK (on_quote_cb_toggle), ia);
1021 g_signal_connect (GTK_ENTRY (p->custom_entry), "notify::text",
1022 G_CALLBACK (on_separators_custom_entry_notify), ia);
1023 g_signal_connect (p->custom_cb, "toggled",
1024 G_CALLBACK (on_separators_custom_cb_toggle), ia);
1025 for (i = 0; i < SEPARATOR_CNT; i++)
1026 g_signal_connect (get_widget_assert (xml, separators[i].name),
1027 "toggled", G_CALLBACK (on_separator_toggle), ia);
1028 g_signal_connect (p->escape_cb, "toggled",
1029 G_CALLBACK (on_separator_toggle), ia);
1032 /* Frees IA's separators substructure. */
1034 destroy_separators_page (struct import_assistant *ia)
1036 struct separators_page *s = &ia->separators;
1038 ds_destroy (&s->separators);
1039 ds_destroy (&s->quotes);
1043 /* Called just before the separators page becomes visible in the
1046 prepare_separators_page (struct import_assistant *ia)
1048 revise_fields_preview (ia);
1051 /* Called when the Reset button is clicked on the separators
1052 page, resets the separators to the defaults. */
1054 reset_separators_page (struct import_assistant *ia)
1056 choose_likely_separators (ia);
1057 set_separators (ia);
1060 /* Frees and clears the column data in IA's separators
1063 clear_fields (struct import_assistant *ia)
1065 struct separators_page *s = &ia->separators;
1067 if (s->column_cnt > 0)
1072 for (row = 0; row < ia->file.line_cnt; row++)
1074 const struct string *line = &ia->file.lines[row];
1075 const char *line_start = ds_data (line);
1076 const char *line_end = ds_end (line);
1078 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1080 char *s = ss_data (col->contents[row]);
1081 if (!(s >= line_start && s <= line_end))
1082 ss_dealloc (&col->contents[row]);
1086 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1089 free (col->contents);
1098 /* Breaks the file data in IA into columns based on the
1099 separators set in IA's separators substructure. */
1101 split_fields (struct import_assistant *ia)
1103 struct separators_page *s = &ia->separators;
1104 size_t columns_allocated;
1110 /* Is space in the set of separators? */
1111 space_sep = ss_find_char (ds_ss (&s->separators), ' ') != SIZE_MAX;
1113 /* Split all the lines, not just those from
1114 ia->first_line.skip_lines on, so that we split the line that
1115 contains variables names if ia->first_line.variable_names is
1117 columns_allocated = 0;
1118 for (row = 0; row < ia->file.line_cnt; row++)
1120 struct string *line = &ia->file.lines[row];
1121 struct substring text = ds_ss (line);
1124 for (column_idx = 0; ; column_idx++)
1126 struct substring field;
1127 struct column *column;
1130 ss_ltrim (&text, ss_cstr (" "));
1131 if (ss_is_empty (text))
1133 if (column_idx != 0)
1137 else if (!ds_is_empty (&s->quotes)
1138 && ds_find_char (&s->quotes, text.string[0]) != SIZE_MAX)
1140 int quote = ss_get_char (&text);
1142 ss_get_until (&text, quote, &field);
1149 while ((c = ss_get_char (&text)) != EOF)
1151 ds_put_char (&s, c);
1152 else if (ss_match_char (&text, quote))
1153 ds_put_char (&s, quote);
1160 ss_get_chars (&text, ss_cspan (text, ds_ss (&s->separators)),
1163 if (column_idx >= s->column_cnt)
1165 struct column *column;
1167 if (s->column_cnt >= columns_allocated)
1168 s->columns = x2nrealloc (s->columns, &columns_allocated,
1169 sizeof *s->columns);
1170 column = &s->columns[s->column_cnt++];
1171 column->name = NULL;
1173 column->contents = xcalloc (ia->file.line_cnt,
1174 sizeof *column->contents);
1176 column = &s->columns[column_idx];
1177 column->contents[row] = field;
1178 if (ss_length (field) > column->width)
1179 column->width = ss_length (field);
1182 ss_ltrim (&text, ss_cstr (" "));
1183 if (ss_is_empty (text))
1185 if (ss_find_char (ds_ss (&s->separators), ss_first (text))
1187 ss_advance (&text, 1);
1192 /* Chooses a name for each column on the separators page */
1194 choose_column_names (struct import_assistant *ia)
1196 const struct first_line_page *f = &ia->first_line;
1197 struct separators_page *s = &ia->separators;
1198 struct dictionary *dict;
1199 unsigned long int generated_name_count = 0;
1203 dict = dict_create ();
1204 name_row = f->variable_names && f->skip_lines ? f->skip_lines : 0;
1205 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1207 char name[VAR_NAME_LEN + 1];
1210 hint = name_row ? ss_xstrdup (col->contents[name_row - 1]) : NULL;
1211 if (!dict_make_unique_var_name (dict, hint, &generated_name_count, name))
1215 col->name = xstrdup (name);
1216 dict_create_var_assert (dict, name, 0);
1218 dict_destroy (dict);
1221 /* Picks the most likely separator and quote characters based on
1224 choose_likely_separators (struct import_assistant *ia)
1226 unsigned long int histogram[UCHAR_MAX + 1] = { 0 };
1229 /* Construct a histogram of all the characters used in the
1231 for (row = 0; row < ia->file.line_cnt; row++)
1233 struct substring line = ds_ss (&ia->file.lines[row]);
1234 size_t length = ss_length (line);
1236 for (i = 0; i < length; i++)
1237 histogram[(unsigned char) line.string[i]]++;
1240 find_commonest_chars (histogram, "\"'", "", &ia->separators.quotes);
1241 find_commonest_chars (histogram, ",;:/|!\t-", ",",
1242 &ia->separators.separators);
1243 ia->separators.escape = true;
1246 /* Chooses the most common character among those in TARGETS,
1247 based on the frequency data in HISTOGRAM, and stores it in
1248 RESULT. If there is a tie for the most common character among
1249 those in TARGETS, the earliest character is chosen. If none
1250 of the TARGETS appear at all, then DEF is used as a
1253 find_commonest_chars (unsigned long int histogram[UCHAR_MAX + 1],
1254 const char *targets, const char *def,
1255 struct string *result)
1257 unsigned char max = 0;
1258 unsigned long int max_count = 0;
1260 for (; *targets != '\0'; targets++)
1262 unsigned char c = *targets;
1263 unsigned long int count = histogram[c];
1264 if (count > max_count)
1273 ds_put_char (result, max);
1276 ds_assign_cstr (result, def);
1279 /* Revises the contents of the fields tree view based on the
1280 currently chosen set of separators. */
1282 revise_fields_preview (struct import_assistant *ia)
1285 w = GTK_WIDGET (ia->separators.fields_tree_view);
1286 gtk_widget_destroy (w);
1287 get_separators (ia);
1289 choose_column_names (ia);
1290 ia->separators.fields_tree_view = create_data_tree_view (
1292 GTK_CONTAINER (get_widget_assert (ia->asst.xml, "fields-scroller")),
1296 /* Sets the widgets to match IA's separators substructure. */
1298 set_separators (struct import_assistant *ia)
1300 struct separators_page *s = &ia->separators;
1302 struct string custom;
1307 ds_init_empty (&custom);
1309 for (i = 0; i < ds_length (&s->separators); i++)
1311 unsigned char c = ds_at (&s->separators, i);
1314 for (j = 0; j < SEPARATOR_CNT; j++)
1316 const struct separator *s = &separators[j];
1324 ds_put_char (&custom, c);
1328 for (i = 0; i < SEPARATOR_CNT; i++)
1330 const struct separator *s = &separators[i];
1331 GtkWidget *button = get_widget_assert (ia->asst.xml, s->name);
1332 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
1333 (seps & (1u << i)) != 0);
1335 any_custom = !ds_is_empty (&custom);
1336 gtk_entry_set_text (GTK_ENTRY (s->custom_entry), ds_cstr (&custom));
1337 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->custom_cb),
1339 gtk_widget_set_sensitive (s->custom_entry, any_custom);
1340 ds_destroy (&custom);
1342 any_quotes = !ds_is_empty (&s->quotes);
1343 gtk_entry_set_text (s->quote_entry,
1344 any_quotes ? ds_cstr (&s->quotes) : "\"");
1345 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->quote_cb),
1347 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->escape_cb),
1349 gtk_widget_set_sensitive (s->quote_combo, any_quotes);
1350 gtk_widget_set_sensitive (s->escape_cb, any_quotes);
1353 /* Sets IA's separators substructure to match the widgets. */
1355 get_separators (struct import_assistant *ia)
1357 struct separators_page *s = &ia->separators;
1360 ds_clear (&s->separators);
1361 for (i = 0; i < SEPARATOR_CNT; i++)
1363 const struct separator *sep = &separators[i];
1364 GtkWidget *button = get_widget_assert (ia->asst.xml, sep->name);
1365 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
1366 ds_put_char (&s->separators, sep->c);
1369 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->custom_cb)))
1370 ds_put_cstr (&s->separators,
1371 gtk_entry_get_text (GTK_ENTRY (s->custom_entry)));
1373 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->quote_cb)))
1375 gchar *text = gtk_combo_box_get_active_text (
1376 GTK_COMBO_BOX (s->quote_combo));
1377 ds_assign_cstr (&s->quotes, text);
1381 ds_clear (&s->quotes);
1382 s->escape = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->escape_cb));
1385 /* Called when the user changes the entry field for custom
1388 on_separators_custom_entry_notify (GObject *gobject UNUSED,
1389 GParamSpec *arg1 UNUSED,
1390 struct import_assistant *ia)
1392 revise_fields_preview (ia);
1395 /* Called when the user toggles the checkbox that enables custom
1398 on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
1399 struct import_assistant *ia)
1401 bool is_active = gtk_toggle_button_get_active (custom_cb);
1402 gtk_widget_set_sensitive (ia->separators.custom_entry, is_active);
1403 revise_fields_preview (ia);
1406 /* Called when the user changes the selection in the combo box
1407 that selects a quote character. */
1409 on_quote_combo_change (GtkComboBox *combo, struct import_assistant *ia)
1411 revise_fields_preview (ia);
1414 /* Called when the user toggles the checkbox that enables
1417 on_quote_cb_toggle (GtkToggleButton *quote_cb, struct import_assistant *ia)
1419 bool is_active = gtk_toggle_button_get_active (quote_cb);
1420 gtk_widget_set_sensitive (ia->separators.quote_combo, is_active);
1421 gtk_widget_set_sensitive (ia->separators.escape_cb, is_active);
1422 revise_fields_preview (ia);
1425 /* Called when the user toggles one of the separators
1428 on_separator_toggle (GtkToggleButton *toggle UNUSED,
1429 struct import_assistant *ia)
1431 revise_fields_preview (ia);
1434 /* Called to render one of the cells in the fields preview tree
1437 render_input_cell (GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1438 GtkTreeModel *model, GtkTreeIter *iter,
1441 struct import_assistant *ia = ia_;
1442 struct substring field;
1446 column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1448 row = text_import_model_iter_to_row (iter) + ia->first_line.skip_lines;
1449 field = ia->separators.columns[column].contents[row];
1450 if (field.string != NULL)
1452 GValue text = {0, };
1453 g_value_init (&text, G_TYPE_STRING);
1454 g_value_take_string (&text, ss_xstrdup (field));
1455 g_object_set_property (G_OBJECT (cell), "text", &text);
1456 g_value_unset (&text);
1457 g_object_set (cell, "background-set", FALSE, (void *) NULL);
1462 "background", "red",
1463 "background-set", TRUE,
1467 /* Called to render a tooltip on one of the cells in the fields
1468 preview tree view. */
1470 on_query_input_tooltip (GtkWidget *widget, gint wx, gint wy,
1471 gboolean keyboard_mode UNUSED,
1472 GtkTooltip *tooltip, struct import_assistant *ia)
1476 if (!get_tooltip_location (widget, wx, wy, ia, &row, &column))
1479 if (ia->separators.columns[column].contents[row].string != NULL)
1482 gtk_tooltip_set_text (tooltip,
1483 _("This input line has too few separators "
1484 "to fill in this field."));
1488 /* The "formats" page of the assistant. */
1490 static void on_variable_change (PsppireDict *dict, int idx,
1491 struct import_assistant *);
1492 static void clear_modified_vars (struct import_assistant *);
1494 /* Initializes IA's formats substructure. */
1496 init_formats_page (struct import_assistant *ia)
1498 GladeXML *xml = ia->asst.xml;
1499 struct formats_page *p = &ia->formats;
1501 p->page = add_page_to_assistant (ia, get_widget_assert (xml, "Formats"),
1502 GTK_ASSISTANT_PAGE_CONFIRM);
1503 p->data_tree_view = GTK_TREE_VIEW (get_widget_assert (xml, "data"));
1504 p->modified_vars = NULL;
1505 p->modified_var_cnt = 0;
1508 /* Frees IA's formats substructure. */
1510 destroy_formats_page (struct import_assistant *ia)
1512 struct formats_page *p = &ia->formats;
1514 if (p->psppire_dict != NULL)
1516 /* This destroys p->dict also. */
1517 g_object_unref (p->psppire_dict);
1519 clear_modified_vars (ia);
1522 /* Called just before the formats page of the assistant is
1525 prepare_formats_page (struct import_assistant *ia)
1527 struct dictionary *dict;
1528 PsppireDict *psppire_dict;
1529 PsppireVarStore *var_store;
1530 GtkBin *vars_scroller;
1531 GtkWidget *old_var_sheet;
1532 PsppireVarSheet *var_sheet;
1533 struct separators_page *s = &ia->separators;
1534 struct formats_page *p = &ia->formats;
1535 struct fmt_guesser *fg;
1536 unsigned long int number = 0;
1539 dict = dict_create ();
1540 fg = fmt_guesser_create ();
1541 for (column_idx = 0; column_idx < s->column_cnt; column_idx++)
1543 struct variable *modified_var;
1544 char name[VAR_NAME_LEN + 1];
1546 modified_var = (column_idx < p->modified_var_cnt
1547 ? p->modified_vars[column_idx] : NULL);
1548 if (modified_var == NULL)
1550 struct column *column = &s->columns[column_idx];
1551 struct variable *var;
1552 struct fmt_spec format;
1555 /* Choose variable name. */
1556 if (!dict_make_unique_var_name (dict, column->name, &number, name))
1559 /* Choose variable format. */
1560 fmt_guesser_clear (fg);
1561 for (row = ia->first_line.skip_lines; row < ia->file.line_cnt; row++)
1562 fmt_guesser_add (fg, column->contents[row]);
1563 fmt_guesser_guess (fg, &format);
1564 fmt_fix_input (&format);
1566 /* Create variable. */
1567 var = dict_create_var_assert (dict, name, fmt_var_width (&format));
1568 var_set_both_formats (var, &format);
1572 if (!dict_make_unique_var_name (dict, var_get_name (modified_var),
1575 dict_clone_var_assert (dict, modified_var, name);
1578 fmt_guesser_destroy (fg);
1580 psppire_dict = psppire_dict_new_from_dict (dict);
1581 g_signal_connect (psppire_dict, "variable_changed",
1582 G_CALLBACK (on_variable_change), ia);
1583 ia->formats.dict = dict;
1584 ia->formats.psppire_dict = psppire_dict;
1586 /* XXX: PsppireVarStore doesn't hold a reference to
1587 psppire_dict for now, but it should. After it does, we
1588 should g_object_ref the psppire_dict here, since we also
1589 hold a reference via ia->formats.dict. */
1590 var_store = psppire_var_store_new (psppire_dict);
1591 g_object_set (var_store,
1593 "format-type", PSPPIRE_VAR_STORE_INPUT_FORMATS,
1595 var_sheet = PSPPIRE_VAR_SHEET (psppire_var_sheet_new ());
1596 g_object_set (var_sheet,
1597 "row-geometry", var_store,
1599 "may-create-vars", FALSE,
1602 vars_scroller = GTK_BIN (get_widget_assert (ia->asst.xml, "vars-scroller"));
1603 old_var_sheet = gtk_bin_get_child (vars_scroller);
1604 if (old_var_sheet != NULL)
1605 gtk_widget_destroy (old_var_sheet);
1606 gtk_container_add (GTK_CONTAINER (vars_scroller), GTK_WIDGET (var_sheet));
1607 gtk_widget_show (GTK_WIDGET (var_sheet));
1609 gtk_widget_destroy (GTK_WIDGET (ia->formats.data_tree_view));
1610 ia->formats.data_tree_view = create_data_tree_view (
1612 GTK_CONTAINER (get_widget_assert (ia->asst.xml, "data-scroller")),
1616 /* Clears the set of user-modified variables from IA's formats
1617 substructure. This discards user modifications to variable
1618 formats, thereby causing formats to revert to their
1621 clear_modified_vars (struct import_assistant *ia)
1623 struct formats_page *p = &ia->formats;
1626 for (i = 0; i < p->modified_var_cnt; i++)
1627 var_destroy (p->modified_vars[i]);
1628 free (p->modified_vars);
1629 p->modified_vars = NULL;
1630 p->modified_var_cnt = 0;
1633 /* Resets the formats page to its defaults, discarding user
1636 reset_formats_page (struct import_assistant *ia)
1638 clear_modified_vars (ia);
1639 prepare_formats_page (ia);
1642 /* Called when the user changes one of the variables in the
1645 on_variable_change (PsppireDict *dict, int dict_idx,
1646 struct import_assistant *ia)
1648 struct formats_page *p = &ia->formats;
1649 GtkTreeView *tv = ia->formats.data_tree_view;
1650 gint column_idx = dict_idx + 1;
1652 /* Remove previous column and replace with new column. */
1653 gtk_tree_view_remove_column (tv, gtk_tree_view_get_column (tv, column_idx));
1654 gtk_tree_view_insert_column (tv, make_data_column (ia, tv, false, dict_idx),
1657 /* Save a copy of the modified variable in modified_vars, so
1658 that its attributes will be preserved if we back up to the
1659 previous page with the Prev button and then come back
1661 if (dict_idx >= p->modified_var_cnt)
1664 p->modified_vars = xnrealloc (p->modified_vars, dict_idx + 1,
1665 sizeof *p->modified_vars);
1666 for (i = 0; i <= dict_idx; i++)
1667 p->modified_vars[i] = NULL;
1668 p->modified_var_cnt = dict_idx + 1;
1670 if (p->modified_vars[dict_idx])
1671 var_destroy (p->modified_vars[dict_idx]);
1672 p->modified_vars[dict_idx]
1673 = var_clone (psppire_dict_get_variable (dict, dict_idx));
1676 /* Parses the contents of the field at (ROW,COLUMN) according to
1677 its variable format. If OUTPUTP is non-null, then *OUTPUTP
1678 receives the formatted output for that field (which must be
1679 freed with free). If TOOLTIPP is non-null, then *TOOLTIPP
1680 receives a message suitable for use in a tooltip, if one is
1681 needed, or a null pointer otherwise. Returns true if a
1682 tooltip message is needed, otherwise false. */
1684 parse_field (struct import_assistant *ia,
1685 size_t row, size_t column,
1686 char **outputp, char **tooltipp)
1688 struct substring field;
1690 struct variable *var;
1691 const struct fmt_spec *in;
1692 struct fmt_spec out;
1696 field = ia->separators.columns[column].contents[row];
1697 var = dict_get_var (ia->formats.dict, column);
1698 val = value_create (var_get_width (var));
1699 in = var_get_print_format (var);
1700 out = fmt_for_output_from_input (in);
1702 if (field.string != NULL)
1705 if (!data_in (field, LEGACY_NATIVE, in->type, 0, 0, 0,
1706 val, var_get_width (var)))
1708 char fmt_string[FMT_STRING_LEN_MAX + 1];
1709 fmt_to_string (in, fmt_string);
1710 tooltip = xasprintf (_("Field content \"%.*s\" cannot be parsed in "
1712 (int) field.length, field.string,
1719 tooltip = xstrdup (_("This input line has too few separators "
1720 "to fill in this field."));
1721 value_set_missing (val, var_get_width (var));
1723 if (outputp != NULL)
1725 char *output = xmalloc (out.w + 1);
1726 data_out (val, &out, output);
1727 output[out.w] = '\0';
1732 ok = tooltip == NULL;
1733 if (tooltipp != NULL)
1734 *tooltipp = tooltip;
1740 /* Called to render one of the cells in the data preview tree
1743 render_output_cell (GtkTreeViewColumn *tree_column,
1744 GtkCellRenderer *cell,
1745 GtkTreeModel *model,
1749 struct import_assistant *ia = ia_;
1751 GValue gvalue = { 0, };
1754 ok = parse_field (ia,
1755 (text_import_model_iter_to_row (iter)
1756 + ia->first_line.skip_lines),
1757 GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1761 g_value_init (&gvalue, G_TYPE_STRING);
1762 g_value_take_string (&gvalue, output);
1763 g_object_set_property (G_OBJECT (cell), "text", &gvalue);
1764 g_value_unset (&gvalue);
1767 g_object_set (cell, "background-set", FALSE, (void *) NULL);
1770 "background", "red",
1771 "background-set", TRUE,
1775 /* Called to render a tooltip for one of the cells in the data
1776 preview tree view. */
1778 on_query_output_tooltip (GtkWidget *widget, gint wx, gint wy,
1779 gboolean keyboard_mode UNUSED,
1780 GtkTooltip *tooltip, struct import_assistant *ia)
1785 if (!get_tooltip_location (widget, wx, wy, ia, &row, &column))
1788 if (parse_field (ia, row, column, NULL, &text))
1791 gtk_tooltip_set_text (tooltip, text);
1796 /* Utility functions used by multiple pages of the assistant. */
1799 get_tooltip_location (GtkWidget *widget, gint wx, gint wy,
1800 const struct import_assistant *ia,
1801 size_t *row, size_t *column)
1803 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
1807 GtkTreeViewColumn *tree_column;
1808 GtkTreeModel *tree_model;
1811 gtk_tree_view_convert_widget_to_bin_window_coords (tree_view,
1813 if (!gtk_tree_view_get_path_at_pos (tree_view, bx, by,
1814 &path, &tree_column, NULL, NULL))
1817 *column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1820 tree_model = gtk_tree_view_get_model (tree_view);
1821 ok = gtk_tree_model_get_iter (tree_model, &iter, path);
1822 gtk_tree_path_free (path);
1826 *row = text_import_model_iter_to_row (&iter) + ia->first_line.skip_lines;
1831 make_tree_view (const struct import_assistant *ia,
1833 GtkTreeView **tree_view)
1835 GtkTreeModel *model;
1837 *tree_view = GTK_TREE_VIEW (gtk_tree_view_new ());
1838 model = GTK_TREE_MODEL (text_import_model_new (
1839 ia->file.lines + first_line,
1840 ia->file.line_cnt - first_line, first_line));
1841 gtk_tree_view_set_model (*tree_view, model);
1843 add_line_number_column (ia, *tree_view);
1847 add_line_number_column (const struct import_assistant *ia,
1848 GtkTreeView *treeview)
1850 GtkTreeViewColumn *column;
1852 column = gtk_tree_view_column_new_with_attributes (
1853 "Line", ia->asst.prop_renderer,
1854 "text", TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER,
1856 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
1857 gtk_tree_view_column_set_fixed_width (
1858 column, get_monospace_width (treeview, ia->asst.prop_renderer, 5));
1859 gtk_tree_view_append_column (treeview, column);
1863 get_monospace_width (GtkTreeView *treeview, GtkCellRenderer *renderer,
1870 ds_put_char_multiple (&s, '0', char_cnt);
1871 ds_put_char (&s, ' ');
1872 width = get_string_width (treeview, renderer, ds_cstr (&s));
1879 get_string_width (GtkTreeView *treeview, GtkCellRenderer *renderer,
1883 g_object_set (G_OBJECT (renderer), "text", string, (void *) NULL);
1884 gtk_cell_renderer_get_size (renderer, GTK_WIDGET (treeview),
1885 NULL, NULL, NULL, &width, NULL);
1889 static GtkTreeViewColumn *
1890 make_data_column (struct import_assistant *ia, GtkTreeView *tree_view,
1891 bool input, gint dict_idx)
1893 struct variable *var = NULL;
1894 struct column *column = NULL;
1895 char name[(VAR_NAME_LEN * 2) + 1];
1897 gint content_width, header_width;
1898 GtkTreeViewColumn *tree_column;
1901 column = &ia->separators.columns[dict_idx];
1903 var = dict_get_var (ia->formats.dict, dict_idx);
1905 escape_underscores (input ? column->name : var_get_name (var), name);
1906 char_cnt = input ? column->width : var_get_print_format (var)->w;
1907 content_width = get_monospace_width (tree_view, ia->asst.fixed_renderer,
1909 header_width = get_string_width (tree_view, ia->asst.prop_renderer,
1912 tree_column = gtk_tree_view_column_new ();
1913 g_object_set_data (G_OBJECT (tree_column), "column-number",
1914 GINT_TO_POINTER (dict_idx));
1915 gtk_tree_view_column_set_title (tree_column, name);
1916 gtk_tree_view_column_pack_start (tree_column, ia->asst.fixed_renderer,
1918 gtk_tree_view_column_set_cell_data_func (
1919 tree_column, ia->asst.fixed_renderer,
1920 input ? render_input_cell : render_output_cell, ia, NULL);
1921 gtk_tree_view_column_set_sizing (tree_column, GTK_TREE_VIEW_COLUMN_FIXED);
1922 gtk_tree_view_column_set_fixed_width (tree_column, MAX (content_width,
1928 static GtkTreeView *
1929 create_data_tree_view (bool input, GtkContainer *parent,
1930 struct import_assistant *ia)
1932 GtkTreeView *tree_view;
1935 make_tree_view (ia, ia->first_line.skip_lines, &tree_view);
1936 gtk_tree_selection_set_mode (gtk_tree_view_get_selection (tree_view),
1937 GTK_SELECTION_NONE);
1939 for (i = 0; i < ia->separators.column_cnt; i++)
1940 gtk_tree_view_append_column (tree_view,
1941 make_data_column (ia, tree_view, input, i));
1943 g_object_set (G_OBJECT (tree_view), "has-tooltip", TRUE, (void *) NULL);
1944 g_signal_connect (tree_view, "query-tooltip",
1945 G_CALLBACK (input ? on_query_input_tooltip
1946 : on_query_output_tooltip), ia);
1947 gtk_tree_view_set_fixed_height_mode (tree_view, true);
1949 gtk_container_add (parent, GTK_WIDGET (tree_view));
1950 gtk_widget_show (GTK_WIDGET (tree_view));
1956 escape_underscores (const char *in, char *out)
1958 for (; *in != '\0'; in++)
1967 /* TextImportModel, a GtkTreeModel implementation used by some
1968 pages of the assistant. */
1970 #define G_TYPE_TEXT_IMPORT_MODEL (text_import_model_get_type ())
1971 #define TEXT_IMPORT_MODEL(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), G_TYPE_TEXT_IMPORT_MODEL, TextImportModel))
1972 #define TEXT_IMPORT_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), G_TYPE_TEXT_IMPORT_MODEL, TextImportModelClass))
1973 #define IS_TEXT_IMPORT_MODEL(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), G_TYPE_TEXT_IMPORT_MODEL))
1974 #define IS_TEXT_IMPORT_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), G_TYPE_TEXT_IMPORT_MODEL))
1975 #define TEXT_IMPORT_MODEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), G_TYPE_TEXT_IMPORT_MODEL, TextImportModelClass))
1977 /* Random number used in 'stamp' member of GtkTreeIter. */
1978 #define TREE_MODEL_STAMP 0x7efd67d3
1980 struct TextImportModel
1983 struct string *lines;
1988 struct TextImportModelClass
1990 GObjectClass parent_class;
1993 GType text_import_model_get_type (void);
1994 static void text_import_model_tree_model_init (gpointer iface, gpointer data);
1997 text_import_model_get_type (void)
1999 static GType object_type = 0;
2003 static const GTypeInfo object_info = {
2004 sizeof (TextImportModelClass),
2005 (GBaseInitFunc) NULL,
2006 (GBaseFinalizeFunc) NULL,
2007 NULL, /* class_init */
2008 NULL, /* class_finalize */
2009 NULL, /* class_data */
2010 sizeof (TextImportModel),
2011 0, /* n_preallocs */
2012 NULL, /* instance_init */
2015 static const GInterfaceInfo tree_model_info = {
2016 text_import_model_tree_model_init,
2021 object_type = g_type_register_static (G_TYPE_OBJECT,
2025 g_type_add_interface_static (object_type, GTK_TYPE_TREE_MODEL,
2035 /* Creates and returns a new TextImportModel that contains the
2036 LINE_CNT lines in LINES. The lines before FIRST_LINE in LINES
2037 are not part of the model, but they are included in the line
2038 numbers in the TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER column.
2040 The caller retains responsibility for freeing LINES and must
2041 ensure that its lifetime and that of the strings that it
2042 contains exceeds that of the TextImportModel. */
2044 text_import_model_new (struct string *lines, size_t line_cnt,
2047 TextImportModel *new_text_import_model
2048 = g_object_new (G_TYPE_TEXT_IMPORT_MODEL, NULL);
2049 new_text_import_model->lines = lines;
2050 new_text_import_model->line_cnt = line_cnt;
2051 new_text_import_model->first_line = first_line;
2052 return new_text_import_model;
2057 tree_model_iter_has_child (GtkTreeModel *tree_model,
2064 tree_model_iter_parent (GtkTreeModel *tree_model,
2071 static GtkTreeModelFlags
2072 tree_model_get_flags (GtkTreeModel *model)
2074 g_return_val_if_fail (IS_TEXT_IMPORT_MODEL (model), (GtkTreeModelFlags) 0);
2076 return GTK_TREE_MODEL_LIST_ONLY | GTK_TREE_MODEL_ITERS_PERSIST;
2081 tree_model_n_columns (GtkTreeModel *model)
2087 tree_model_column_type (GtkTreeModel *model, gint index)
2089 return (index == TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER ? G_TYPE_INT
2090 : index == TEXT_IMPORT_MODEL_COLUMN_LINE ? G_TYPE_STRING
2095 init_iter (TextImportModel *list, gint idx, GtkTreeIter *iter)
2097 if (idx < 0 || idx >= list->line_cnt)
2100 iter->user_data = GINT_TO_POINTER (-1);
2105 iter->stamp = TREE_MODEL_STAMP;
2106 iter->user_data = GINT_TO_POINTER (idx);
2112 tree_model_get_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreePath *path)
2114 gint *indices, depth;
2116 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2118 g_return_val_if_fail (path, FALSE);
2120 indices = gtk_tree_path_get_indices (path);
2121 depth = gtk_tree_path_get_depth (path);
2123 g_return_val_if_fail (depth == 1, FALSE);
2125 return init_iter (list, indices[0], iter);
2130 tree_model_iter_next (GtkTreeModel *model, GtkTreeIter *iter)
2132 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2135 assert (iter->stamp == TREE_MODEL_STAMP);
2137 idx = GPOINTER_TO_INT (iter->user_data);
2138 return init_iter (list, idx == -1 ? -1 : idx + 1, iter);
2141 static GtkTreePath *
2142 tree_model_get_path (GtkTreeModel *model, GtkTreeIter *iter)
2146 g_return_val_if_fail (iter->stamp == TREE_MODEL_STAMP, FALSE);
2148 path = gtk_tree_path_new ();
2149 gtk_tree_path_append_index (path, GPOINTER_TO_INT (iter->user_data));
2155 tree_model_get_value (GtkTreeModel *model, GtkTreeIter *iter,
2156 gint column, GValue *value)
2158 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2161 g_return_if_fail (iter->stamp == TREE_MODEL_STAMP);
2163 idx = GPOINTER_TO_INT (iter->user_data);
2164 assert (idx >= 0 && idx < list->line_cnt);
2168 g_value_init (value, G_TYPE_INT);
2169 g_value_set_int (value, idx + list->first_line + 1);
2173 g_value_init (value, G_TYPE_STRING);
2174 g_value_set_static_string (value, ds_cstr (&list->lines[idx]));
2179 tree_model_iter_children (GtkTreeModel *tree_model,
2181 GtkTreeIter *parent)
2187 tree_model_n_children (GtkTreeModel *model, GtkTreeIter *iter)
2189 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2191 return iter == NULL ? list->line_cnt : 0;
2195 tree_model_nth_child (GtkTreeModel *model, GtkTreeIter *iter,
2196 GtkTreeIter *parent, gint n)
2198 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2199 g_return_val_if_fail (IS_TEXT_IMPORT_MODEL (model), FALSE);
2203 return init_iter (list, n, iter);
2207 text_import_model_tree_model_init (gpointer iface_, gpointer data UNUSED)
2209 GtkTreeModelIface *iface = (GtkTreeModelIface *) iface_;
2211 iface->get_flags = tree_model_get_flags;
2212 iface->get_n_columns = tree_model_n_columns;
2213 iface->get_column_type = tree_model_column_type;
2214 iface->get_iter = tree_model_get_iter;
2215 iface->iter_next = tree_model_iter_next;
2216 iface->get_path = tree_model_get_path;
2217 iface->get_value = tree_model_get_value;
2219 iface->iter_children = tree_model_iter_children;
2220 iface->iter_has_child = tree_model_iter_has_child;
2221 iface->iter_n_children = tree_model_n_children;
2222 iface->iter_nth_child = tree_model_nth_child;
2223 iface->iter_parent = tree_model_iter_parent;
2227 text_import_model_iter_to_row (const GtkTreeIter *iter)
2229 assert (iter->stamp == TREE_MODEL_STAMP);
2230 return GPOINTER_TO_INT (iter->user_data);