1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2008, 2009, 2010, 2011, 2012 Free Software Foundation
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19 #include "ui/gui/text-data-import-dialog.h"
22 #include <gtk-contrib/psppire-sheet.h>
28 #include "data/data-in.h"
29 #include "data/data-out.h"
30 #include "data/format-guesser.h"
31 #include "data/value-labels.h"
32 #include "language/data-io/data-parser.h"
33 #include "language/lexer/lexer.h"
34 #include "libpspp/assertion.h"
35 #include "libpspp/i18n.h"
36 #include "libpspp/message.h"
37 #include "ui/gui/checkbox-treeview.h"
38 #include "ui/gui/dialog-common.h"
39 #include "ui/gui/executor.h"
40 #include "ui/gui/helper.h"
41 #include "ui/gui/builder-wrapper.h"
42 #include "ui/gui/psppire-data-window.h"
43 #include "ui/gui/psppire-dialog.h"
44 #include "ui/gui/psppire-var-sheet.h"
45 #include "ui/gui/psppire-var-store.h"
46 #include "ui/gui/psppire-scanf.h"
47 #include "ui/syntax-gen.h"
50 #include "gl/xalloc.h"
53 #define _(msgid) gettext (msgid)
54 #define N_(msgid) msgid
57 /* TextImportModel, a GtkTreeModel used by the text data import
61 TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER, /* 1-based line number in file */
62 TEXT_IMPORT_MODEL_COLUMN_LINE, /* The line from the file. */
64 typedef struct TextImportModel TextImportModel;
65 typedef struct TextImportModelClass TextImportModelClass;
67 TextImportModel *text_import_model_new (struct string *lines, size_t line_cnt,
69 gint text_import_model_iter_to_row (const GtkTreeIter *);
71 struct import_assistant;
73 /* The file to be imported. */
76 char *file_name; /* File name. */
77 unsigned long int total_lines; /* Number of lines in file. */
78 bool total_is_exact; /* Is total_lines exact (or an estimate)? */
80 /* The first several lines of the file. */
84 static bool init_file (struct import_assistant *, GtkWindow *parent);
85 static void destroy_file (struct import_assistant *);
87 /* The main body of the GTK+ assistant and related data. */
91 GtkAssistant *assistant;
93 GtkWidget *paste_button;
94 GtkWidget *reset_button;
98 GtkCellRenderer *prop_renderer;
99 GtkCellRenderer *fixed_renderer;
101 static void init_assistant (struct import_assistant *, GtkWindow *);
102 static void destroy_assistant (struct import_assistant *);
103 static GtkWidget *add_page_to_assistant (struct import_assistant *,
105 GtkAssistantPageType);
107 /* The introduction page of the assistant. */
111 GtkWidget *all_cases_button;
112 GtkWidget *n_cases_button;
113 GtkWidget *n_cases_spin;
114 GtkWidget *percent_button;
115 GtkWidget *percent_spin;
117 static void init_intro_page (struct import_assistant *);
118 static void reset_intro_page (struct import_assistant *);
120 /* Page where the user chooses the first line of data. */
121 struct first_line_page
123 int skip_lines; /* Number of initial lines to skip? */
124 bool variable_names; /* Variable names above first line of data? */
127 GtkTreeView *tree_view;
128 GtkWidget *variable_names_cb;
130 static void init_first_line_page (struct import_assistant *);
131 static void reset_first_line_page (struct import_assistant *);
133 /* Page where the user chooses field separators. */
134 struct separators_page
136 /* How to break lines into columns. */
137 struct string separators; /* Field separators. */
138 struct string quotes; /* Quote characters. */
139 bool escape; /* Doubled quotes yield a quote mark? */
141 /* The columns produced thereby. */
142 struct column *columns; /* Information about each column. */
143 size_t column_cnt; /* Number of columns. */
146 GtkWidget *custom_cb;
147 GtkWidget *custom_entry;
149 GtkWidget *quote_combo;
150 GtkEntry *quote_entry;
151 GtkWidget *escape_cb;
152 GtkTreeView *fields_tree_view;
154 /* The columns that the separators divide the data into. */
157 /* Variable name for this column. This is the variable name
158 used on the separators page; it can be overridden by the
159 user on the formats page. */
162 /* Maximum length of any row in this column. */
165 /* Contents of this column: contents[row] is the contents for
168 A null substring indicates a missing column for that row
169 (because the line contains an insufficient number of
172 contents[] elements may be substrings of the lines[]
173 strings that represent the whole lines of the file, to
174 save memory. Other elements are dynamically allocated
175 with ss_alloc_substring. */
176 struct substring *contents;
178 static void init_separators_page (struct import_assistant *);
179 static void destroy_separators_page (struct import_assistant *);
180 static void prepare_separators_page (struct import_assistant *);
181 static void reset_separators_page (struct import_assistant *);
183 /* Page where the user verifies and adjusts input formats. */
186 struct dictionary *dict;
189 GtkTreeView *data_tree_view;
190 PsppireDict *psppire_dict;
191 struct variable **modified_vars;
192 size_t modified_var_cnt;
194 static void init_formats_page (struct import_assistant *);
195 static void destroy_formats_page (struct import_assistant *);
196 static void prepare_formats_page (struct import_assistant *);
197 static void reset_formats_page (struct import_assistant *);
199 struct import_assistant
202 struct assistant asst;
203 struct intro_page intro;
204 struct first_line_page first_line;
205 struct separators_page separators;
206 struct formats_page formats;
209 static void apply_dict (const struct dictionary *, struct string *);
210 static char *generate_syntax (const struct import_assistant *);
212 static gboolean get_tooltip_location (GtkWidget *widget, gint wx, gint wy,
213 const struct import_assistant *,
214 size_t *row, size_t *column);
215 static void make_tree_view (const struct import_assistant *ia,
217 GtkTreeView **tree_view);
218 static void add_line_number_column (const struct import_assistant *,
220 static gint get_monospace_width (GtkTreeView *, GtkCellRenderer *,
222 static gint get_string_width (GtkTreeView *, GtkCellRenderer *,
224 static GtkTreeViewColumn *make_data_column (struct import_assistant *,
225 GtkTreeView *, bool input,
227 static GtkTreeView *create_data_tree_view (bool input, GtkContainer *parent,
228 struct import_assistant *);
229 static char *escape_underscores (const char *in);
230 static void push_watch_cursor (struct import_assistant *);
231 static void pop_watch_cursor (struct import_assistant *);
233 /* Pops up the Text Data Import assistant. */
235 text_data_import_assistant (PsppireDataWindow *dw)
237 GtkWindow *parent_window = GTK_WINDOW (dw);
238 struct import_assistant *ia;
240 ia = xzalloc (sizeof *ia);
241 if (!init_file (ia, parent_window))
247 init_assistant (ia, parent_window);
248 init_intro_page (ia);
249 init_first_line_page (ia);
250 init_separators_page (ia);
251 init_formats_page (ia);
253 gtk_widget_show_all (GTK_WIDGET (ia->asst.assistant));
255 ia->asst.main_loop = g_main_loop_new (NULL, false);
256 g_main_loop_run (ia->asst.main_loop);
257 g_main_loop_unref (ia->asst.main_loop);
259 switch (ia->asst.response)
261 case GTK_RESPONSE_APPLY:
262 free (execute_syntax_string (dw, generate_syntax (ia)));
264 case PSPPIRE_RESPONSE_PASTE:
265 free (paste_syntax_to_window (generate_syntax (ia)));
271 destroy_formats_page (ia);
272 destroy_separators_page (ia);
273 destroy_assistant (ia);
278 /* Emits PSPP syntax to S that applies the dictionary attributes
279 (such as missing values and value labels) of the variables in
282 apply_dict (const struct dictionary *dict, struct string *s)
284 size_t var_cnt = dict_get_var_cnt (dict);
287 for (i = 0; i < var_cnt; i++)
289 struct variable *var = dict_get_var (dict, i);
290 const char *name = var_get_name (var);
291 enum val_type type = var_get_type (var);
292 int width = var_get_width (var);
293 enum measure measure = var_get_measure (var);
294 enum alignment alignment = var_get_alignment (var);
295 const struct fmt_spec *format = var_get_print_format (var);
297 if (var_has_missing_values (var))
299 const struct missing_values *mv = var_get_missing_values (var);
302 syntax_gen_pspp (s, "MISSING VALUES %ss (", name);
303 for (j = 0; j < mv_n_values (mv); j++)
306 ds_put_cstr (s, ", ");
307 syntax_gen_value (s, mv_get_value (mv, j), 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 const struct val_lab **labels = val_labs_sorted (vls);
324 size_t n_labels = val_labs_count (vls);
327 syntax_gen_pspp (s, "VALUE LABELS %ss", name);
328 for (i = 0; i < n_labels; i++)
330 const struct val_lab *vl = labels[i];
331 ds_put_cstr (s, "\n ");
332 syntax_gen_value (s, &vl->value, width, format);
333 ds_put_byte (s, ' ');
334 syntax_gen_string (s, ss_cstr (val_lab_get_escaped_label (vl)));
337 ds_put_cstr (s, ".\n");
339 if (var_has_label (var))
340 syntax_gen_pspp (s, "VARIABLE LABELS %ss %sq.\n",
341 name, var_get_label (var));
342 if (measure != var_default_measure (type))
343 syntax_gen_pspp (s, "VARIABLE LEVEL %ss (%ss).\n",
345 (measure == MEASURE_NOMINAL ? "NOMINAL"
346 : measure == MEASURE_ORDINAL ? "ORDINAL"
348 if (alignment != var_default_alignment (type))
349 syntax_gen_pspp (s, "VARIABLE ALIGNMENT %ss (%ss).\n",
351 (alignment == ALIGN_LEFT ? "LEFT"
352 : alignment == ALIGN_CENTRE ? "CENTER"
354 if (var_get_display_width (var) != var_default_display_width (width))
355 syntax_gen_pspp (s, "VARIABLE WIDTH %ss (%d).\n",
356 name, var_get_display_width (var));
360 /* Generates and returns PSPP syntax to execute the import
361 operation described by IA. The caller must free the syntax
364 generate_syntax (const struct import_assistant *ia)
366 struct string s = DS_EMPTY_INITIALIZER;
375 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (
376 ia->intro.n_cases_button)))
377 ds_put_format (&s, " /IMPORTCASES=FIRST %d\n",
378 gtk_spin_button_get_value_as_int (
379 GTK_SPIN_BUTTON (ia->intro.n_cases_spin)));
380 else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (
381 ia->intro.percent_button)))
382 ds_put_format (&s, " /IMPORTCASES=PERCENT %d\n",
383 gtk_spin_button_get_value_as_int (
384 GTK_SPIN_BUTTON (ia->intro.percent_spin)));
386 ds_put_cstr (&s, " /IMPORTCASES=ALL\n");
388 " /ARRANGEMENT=DELIMITED\n"
390 if (ia->first_line.skip_lines > 0)
391 ds_put_format (&s, " /FIRSTCASE=%d\n", ia->first_line.skip_lines + 1);
392 ds_put_cstr (&s, " /DELIMITERS=\"");
393 if (ds_find_byte (&ia->separators.separators, '\t') != SIZE_MAX)
394 ds_put_cstr (&s, "\\t");
395 if (ds_find_byte (&ia->separators.separators, '\\') != SIZE_MAX)
396 ds_put_cstr (&s, "\\\\");
397 for (i = 0; i < ds_length (&ia->separators.separators); i++)
399 char c = ds_at (&ia->separators.separators, i);
401 ds_put_cstr (&s, "\"\"");
402 else if (c != '\t' && c != '\\')
405 ds_put_cstr (&s, "\"\n");
406 if (!ds_is_empty (&ia->separators.quotes))
407 syntax_gen_pspp (&s, " /QUALIFIER=%sq\n", ds_cstr (&ia->separators.quotes));
408 if (!ds_is_empty (&ia->separators.quotes) && ia->separators.escape)
409 ds_put_cstr (&s, " /ESCAPE\n");
410 ds_put_cstr (&s, " /VARIABLES=\n");
412 var_cnt = dict_get_var_cnt (ia->formats.dict);
413 for (i = 0; i < var_cnt; i++)
415 struct variable *var = dict_get_var (ia->formats.dict, i);
416 char format_string[FMT_STRING_LEN_MAX + 1];
417 fmt_to_string (var_get_print_format (var), format_string);
418 ds_put_format (&s, " %s %s%s\n",
419 var_get_name (var), format_string,
420 i == var_cnt - 1 ? "." : "");
423 apply_dict (ia->formats.dict, &s);
428 /* Choosing a file and reading it. */
430 static char *choose_file (GtkWindow *parent_window);
432 /* Obtains the file to import from the user and initializes IA's
433 file substructure. PARENT_WINDOW must be the window to use
434 as the file chooser window's parent.
436 Returns true if successful, false if the file name could not
437 be obtained or the file could not be read. */
439 init_file (struct import_assistant *ia, GtkWindow *parent_window)
441 struct file *file = &ia->file;
442 enum { MAX_PREVIEW_LINES = 1000 }; /* Max number of lines to read. */
443 enum { MAX_LINE_LEN = 16384 }; /* Max length of an acceptable line. */
446 file->file_name = choose_file (parent_window);
447 if (file->file_name == NULL)
450 stream = fopen (file->file_name, "r");
453 msg (ME, _("Could not open `%s': %s"),
454 file->file_name, strerror (errno));
458 file->lines = xnmalloc (MAX_PREVIEW_LINES, sizeof *file->lines);
459 for (; file->line_cnt < MAX_PREVIEW_LINES; file->line_cnt++)
461 struct string *line = &file->lines[file->line_cnt];
463 ds_init_empty (line);
464 if (!ds_read_line (line, stream, MAX_LINE_LEN))
468 else if (ferror (stream))
469 msg (ME, _("Error reading `%s': %s"),
470 file->file_name, strerror (errno));
472 msg (ME, _("Failed to read `%s', because it contains a line "
473 "over %d bytes long and therefore appears not to be "
475 file->file_name, MAX_LINE_LEN);
480 ds_chomp_byte (line, '\n');
481 ds_chomp_byte (line, '\r');
484 if (file->line_cnt == 0)
486 msg (ME, _("`%s' is empty."), file->file_name);
492 /* Estimate the number of lines in the file. */
493 if (file->line_cnt < MAX_PREVIEW_LINES)
494 file->total_lines = file->line_cnt;
498 off_t position = ftello (stream);
499 if (fstat (fileno (stream), &s) == 0 && position > 0)
500 file->total_lines = (double) file->line_cnt / position * s.st_size;
502 file->total_lines = 0;
508 /* Frees IA's file substructure. */
510 destroy_file (struct import_assistant *ia)
512 struct file *f = &ia->file;
515 for (i = 0; i < f->line_cnt; i++)
516 ds_destroy (&f->lines[i]);
518 g_free (f->file_name);
521 /* Obtains the file to read from the user and returns the name of
522 the file as a string that must be freed with g_free if
523 successful, otherwise a null pointer. PARENT_WINDOW must be
524 the window to use as the file chooser window's parent. */
526 choose_file (GtkWindow *parent_window)
529 GtkFileFilter *filter = NULL;
531 GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Import Delimited Text Data"),
533 GTK_FILE_CHOOSER_ACTION_OPEN,
534 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
535 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
538 g_object_set (dialog, "local-only", FALSE, NULL);
540 filter = gtk_file_filter_new ();
541 gtk_file_filter_set_name (filter, _("Text files"));
542 gtk_file_filter_add_mime_type (filter, "text/*");
543 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
545 filter = gtk_file_filter_new ();
546 gtk_file_filter_set_name (filter, _("Text (*.txt) Files"));
547 gtk_file_filter_add_pattern (filter, "*.txt");
548 gtk_file_filter_add_pattern (filter, "*.TXT");
549 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
551 filter = gtk_file_filter_new ();
552 gtk_file_filter_set_name (filter, _("Plain Text (ASCII) Files"));
553 gtk_file_filter_add_mime_type (filter, "text/plain");
554 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
556 filter = gtk_file_filter_new ();
557 gtk_file_filter_set_name (filter, _("Comma Separated Value Files"));
558 gtk_file_filter_add_mime_type (filter, "text/csv");
559 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
561 /* I've never encountered one of these, but it's listed here:
562 http://www.iana.org/assignments/media-types/text/tab-separated-values */
563 filter = gtk_file_filter_new ();
564 gtk_file_filter_set_name (filter, _("Tab Separated Value Files"));
565 gtk_file_filter_add_mime_type (filter, "text/tab-separated-values");
566 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
568 filter = gtk_file_filter_new ();
569 gtk_file_filter_set_name (filter, _("All Files"));
570 gtk_file_filter_add_pattern (filter, "*");
571 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
573 switch (gtk_dialog_run (GTK_DIALOG (dialog)))
575 case GTK_RESPONSE_ACCEPT:
576 file_name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
582 gtk_widget_destroy (dialog);
589 static void close_assistant (struct import_assistant *, int response);
590 static void on_prepare (GtkAssistant *assistant, GtkWidget *page,
591 struct import_assistant *);
592 static void on_cancel (GtkAssistant *assistant, struct import_assistant *);
593 static void on_close (GtkAssistant *assistant, struct import_assistant *);
594 static void on_paste (GtkButton *button, struct import_assistant *);
595 static void on_reset (GtkButton *button, struct import_assistant *);
596 static void close_assistant (struct import_assistant *, int response);
598 /* Initializes IA's asst substructure. PARENT_WINDOW must be the
599 window to use as the assistant window's parent. */
601 init_assistant (struct import_assistant *ia, GtkWindow *parent_window)
603 struct assistant *a = &ia->asst;
605 a->builder = builder_new ("text-data-import.ui");
606 a->assistant = GTK_ASSISTANT (gtk_assistant_new ());
607 g_signal_connect (a->assistant, "prepare", G_CALLBACK (on_prepare), ia);
608 g_signal_connect (a->assistant, "cancel", G_CALLBACK (on_cancel), ia);
609 g_signal_connect (a->assistant, "close", G_CALLBACK (on_close), ia);
610 a->paste_button = gtk_button_new_from_stock (GTK_STOCK_PASTE);
611 gtk_assistant_add_action_widget (a->assistant, a->paste_button);
612 g_signal_connect (a->paste_button, "clicked", G_CALLBACK (on_paste), ia);
613 a->reset_button = gtk_button_new_from_stock ("pspp-stock-reset");
614 gtk_assistant_add_action_widget (a->assistant, a->reset_button);
615 g_signal_connect (a->reset_button, "clicked", G_CALLBACK (on_reset), ia);
616 gtk_window_set_title (GTK_WINDOW (a->assistant),
617 _("Importing Delimited Text Data"));
618 gtk_window_set_transient_for (GTK_WINDOW (a->assistant), parent_window);
619 gtk_window_set_icon_name (GTK_WINDOW (a->assistant), "pspp");
621 a->prop_renderer = gtk_cell_renderer_text_new ();
622 g_object_ref_sink (a->prop_renderer);
623 a->fixed_renderer = gtk_cell_renderer_text_new ();
624 g_object_ref_sink (a->fixed_renderer);
625 g_object_set (G_OBJECT (a->fixed_renderer),
626 "family", "Monospace",
630 /* Frees IA's asst substructure. */
632 destroy_assistant (struct import_assistant *ia)
634 struct assistant *a = &ia->asst;
636 g_object_unref (a->prop_renderer);
637 g_object_unref (a->fixed_renderer);
638 g_object_unref (a->builder);
641 /* Appends a page of the given TYPE, with PAGE as its content, to
642 the GtkAssistant encapsulated by IA. Returns the GtkWidget
643 that represents the page. */
645 add_page_to_assistant (struct import_assistant *ia,
646 GtkWidget *page, GtkAssistantPageType type)
652 title = gtk_window_get_title (GTK_WINDOW (page));
653 title_copy = xstrdup (title ? title : "");
655 content = gtk_bin_get_child (GTK_BIN (page));
657 g_object_ref (content);
658 gtk_container_remove (GTK_CONTAINER (page), content);
660 gtk_widget_destroy (page);
662 gtk_assistant_append_page (ia->asst.assistant, content);
663 gtk_assistant_set_page_type (ia->asst.assistant, content, type);
664 gtk_assistant_set_page_title (ia->asst.assistant, content, title_copy);
665 gtk_assistant_set_page_complete (ia->asst.assistant, content, true);
672 /* Called just before PAGE is displayed as the current page of
673 ASSISTANT, this updates IA content according to the new
676 on_prepare (GtkAssistant *assistant, GtkWidget *page,
677 struct import_assistant *ia)
680 if (gtk_assistant_get_page_type (assistant, page)
681 == GTK_ASSISTANT_PAGE_CONFIRM)
682 gtk_widget_grab_focus (assistant->apply);
684 gtk_widget_grab_focus (assistant->forward);
686 if (page == ia->separators.page)
687 prepare_separators_page (ia);
688 else if (page == ia->formats.page)
689 prepare_formats_page (ia);
691 gtk_widget_show (ia->asst.reset_button);
692 if (page == ia->formats.page)
693 gtk_widget_show (ia->asst.paste_button);
695 gtk_widget_hide (ia->asst.paste_button);
698 /* Called when the Cancel button in the assistant is clicked. */
700 on_cancel (GtkAssistant *assistant, struct import_assistant *ia)
702 close_assistant (ia, GTK_RESPONSE_CANCEL);
705 /* Called when the Apply button on the last page of the assistant
708 on_close (GtkAssistant *assistant, struct import_assistant *ia)
710 close_assistant (ia, GTK_RESPONSE_APPLY);
713 /* Called when the Paste button on the last page of the assistant
716 on_paste (GtkButton *button, struct import_assistant *ia)
718 close_assistant (ia, PSPPIRE_RESPONSE_PASTE);
721 /* Called when the Reset button is clicked. */
723 on_reset (GtkButton *button, struct import_assistant *ia)
725 gint page_num = gtk_assistant_get_current_page (ia->asst.assistant);
726 GtkWidget *page = gtk_assistant_get_nth_page (ia->asst.assistant, page_num);
728 if (page == ia->intro.page)
729 reset_intro_page (ia);
730 else if (page == ia->first_line.page)
731 reset_first_line_page (ia);
732 else if (page == ia->separators.page)
733 reset_separators_page (ia);
734 else if (page == ia->formats.page)
735 reset_formats_page (ia);
738 /* Causes the assistant to close, returning RESPONSE for
739 interpretation by text_data_import_assistant. */
741 close_assistant (struct import_assistant *ia, int response)
743 ia->asst.response = response;
744 g_main_loop_quit (ia->asst.main_loop);
745 gtk_widget_hide (GTK_WIDGET (ia->asst.assistant));
748 /* The "intro" page of the assistant. */
750 static void on_intro_amount_changed (struct import_assistant *);
752 /* Initializes IA's intro substructure. */
754 init_intro_page (struct import_assistant *ia)
756 GtkBuilder *builder = ia->asst.builder;
757 struct intro_page *p = &ia->intro;
759 GtkWidget *hbox_n_cases ;
760 GtkWidget *hbox_percent ;
764 p->n_cases_spin = gtk_spin_button_new_with_range (0, INT_MAX, 100);
766 hbox_n_cases = psppire_scanf_new (_("Only the first %4d cases"), &p->n_cases_spin);
768 table = get_widget_assert (builder, "button-table");
770 gtk_table_attach_defaults (GTK_TABLE (table), hbox_n_cases,
774 p->percent_spin = gtk_spin_button_new_with_range (0, 100, 10);
776 hbox_percent = psppire_scanf_new (_("Only the first %3d %% of file (approximately)"), &p->percent_spin);
778 gtk_table_attach_defaults (GTK_TABLE (table), hbox_percent,
782 p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Intro"),
783 GTK_ASSISTANT_PAGE_INTRO);
785 p->all_cases_button = get_widget_assert (builder, "import-all-cases");
787 p->n_cases_button = get_widget_assert (builder, "import-n-cases");
789 p->percent_button = get_widget_assert (builder, "import-percent");
791 g_signal_connect_swapped (p->all_cases_button, "toggled",
792 G_CALLBACK (on_intro_amount_changed), ia);
793 g_signal_connect_swapped (p->n_cases_button, "toggled",
794 G_CALLBACK (on_intro_amount_changed), ia);
795 g_signal_connect_swapped (p->percent_button, "toggled",
796 G_CALLBACK (on_intro_amount_changed), ia);
798 on_intro_amount_changed (ia);
801 ds_put_cstr (&s, _("This assistant will guide you through the process of "
802 "importing data into PSPP from a text file with one line "
803 "per case, in which fields are separated by tabs, "
804 "commas, or other delimiters.\n\n"));
805 if (ia->file.total_is_exact)
807 &s, ngettext ("The selected file contains %zu line of text. ",
808 "The selected file contains %zu lines of text. ",
811 else if (ia->file.total_lines > 0)
815 "The selected file contains approximately %lu line of text. ",
816 "The selected file contains approximately %lu lines of text. ",
817 ia->file.total_lines),
818 ia->file.total_lines);
821 "Only the first %zu line of the file will be shown for "
822 "preview purposes in the following screens. ",
823 "Only the first %zu lines of the file will be shown for "
824 "preview purposes in the following screens. ",
828 ds_put_cstr (&s, _("You may choose below how much of the file should "
829 "actually be imported."));
830 gtk_label_set_text (GTK_LABEL (get_widget_assert (builder, "intro-label")),
835 /* Resets IA's intro page to its initial state. */
837 reset_intro_page (struct import_assistant *ia)
839 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->intro.all_cases_button),
843 /* Called when one of the radio buttons is clicked. */
845 on_intro_amount_changed (struct import_assistant *ia)
847 struct intro_page *p = &ia->intro;
849 gtk_widget_set_sensitive (p->n_cases_spin,
850 gtk_toggle_button_get_active (
851 GTK_TOGGLE_BUTTON (p->n_cases_button)));
853 gtk_widget_set_sensitive (p->percent_spin,
854 gtk_toggle_button_get_active (
855 GTK_TOGGLE_BUTTON (p->percent_button)));
858 /* The "first line" page of the assistant. */
860 static GtkTreeView *create_lines_tree_view (GtkContainer *parent_window,
861 struct import_assistant *);
862 static void on_first_line_change (GtkTreeSelection *,
863 struct import_assistant *);
864 static void on_variable_names_cb_toggle (GtkToggleButton *,
865 struct import_assistant *);
866 static void set_first_line (struct import_assistant *);
867 static void get_first_line (struct import_assistant *);
869 /* Initializes IA's first_line substructure. */
871 init_first_line_page (struct import_assistant *ia)
873 struct first_line_page *p = &ia->first_line;
874 GtkBuilder *builder = ia->asst.builder;
876 p->page = add_page_to_assistant (ia, get_widget_assert (builder, "FirstLine"),
877 GTK_ASSISTANT_PAGE_CONTENT);
878 gtk_widget_destroy (get_widget_assert (builder, "first-line"));
879 p->tree_view = create_lines_tree_view (
880 GTK_CONTAINER (get_widget_assert (builder, "first-line-scroller")), ia);
881 p->variable_names_cb = get_widget_assert (builder, "variable-names");
882 gtk_tree_selection_set_mode (
883 gtk_tree_view_get_selection (GTK_TREE_VIEW (p->tree_view)),
884 GTK_SELECTION_BROWSE);
886 g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (p->tree_view)),
887 "changed", G_CALLBACK (on_first_line_change), ia);
888 g_signal_connect (p->variable_names_cb, "toggled",
889 G_CALLBACK (on_variable_names_cb_toggle), ia);
892 /* Resets the first_line page to its initial content. */
894 reset_first_line_page (struct import_assistant *ia)
896 ia->first_line.skip_lines = 0;
897 ia->first_line.variable_names = false;
901 /* Creates and returns a tree view that contains each of the
902 lines in IA's file as a row. */
904 create_lines_tree_view (GtkContainer *parent, struct import_assistant *ia)
906 GtkTreeView *tree_view;
907 GtkTreeViewColumn *column;
908 size_t max_line_length;
909 gint content_width, header_width;
911 const gchar *title = _("Text");
913 make_tree_view (ia, 0, &tree_view);
915 column = gtk_tree_view_column_new_with_attributes
917 title, ia->asst.fixed_renderer,
918 "text", TEXT_IMPORT_MODEL_COLUMN_LINE,
921 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
924 for (i = 0; i < ia->file.line_cnt; i++)
926 size_t w = ds_length (&ia->file.lines[i]);
927 max_line_length = MAX (max_line_length, w);
930 content_width = get_monospace_width (tree_view, ia->asst.fixed_renderer,
932 header_width = get_string_width (tree_view, ia->asst.prop_renderer, title);
933 gtk_tree_view_column_set_fixed_width (column, MAX (content_width,
935 gtk_tree_view_append_column (tree_view, column);
937 gtk_tree_view_set_fixed_height_mode (tree_view, true);
939 gtk_container_add (parent, GTK_WIDGET (tree_view));
940 gtk_widget_show (GTK_WIDGET (tree_view));
945 /* Called when the line selected in the first_line tree view
948 on_first_line_change (GtkTreeSelection *selection UNUSED,
949 struct import_assistant *ia)
954 /* Called when the checkbox that indicates whether variable
955 names are in the row above the first line is toggled. */
957 on_variable_names_cb_toggle (GtkToggleButton *variable_names_cb UNUSED,
958 struct import_assistant *ia)
963 /* Sets the widgets to match IA's first_line substructure. */
965 set_first_line (struct import_assistant *ia)
969 path = gtk_tree_path_new_from_indices (ia->first_line.skip_lines, -1);
970 gtk_tree_view_set_cursor (GTK_TREE_VIEW (ia->first_line.tree_view),
972 gtk_tree_path_free (path);
974 gtk_toggle_button_set_active (
975 GTK_TOGGLE_BUTTON (ia->first_line.variable_names_cb),
976 ia->first_line.variable_names);
977 gtk_widget_set_sensitive (ia->first_line.variable_names_cb,
978 ia->first_line.skip_lines > 0);
981 /* Sets IA's first_line substructure to match the widgets. */
983 get_first_line (struct import_assistant *ia)
985 GtkTreeSelection *selection;
989 selection = gtk_tree_view_get_selection (ia->first_line.tree_view);
990 if (gtk_tree_selection_get_selected (selection, &model, &iter))
992 GtkTreePath *path = gtk_tree_model_get_path (model, &iter);
993 int row = gtk_tree_path_get_indices (path)[0];
994 gtk_tree_path_free (path);
996 ia->first_line.skip_lines = row;
997 ia->first_line.variable_names =
998 (ia->first_line.skip_lines > 0
999 && gtk_toggle_button_get_active (
1000 GTK_TOGGLE_BUTTON (ia->first_line.variable_names_cb)));
1002 gtk_widget_set_sensitive (ia->first_line.variable_names_cb,
1003 ia->first_line.skip_lines > 0);
1006 /* The "separators" page of the assistant. */
1008 static void revise_fields_preview (struct import_assistant *ia);
1009 static void choose_likely_separators (struct import_assistant *ia);
1010 static void find_commonest_chars (unsigned long int histogram[UCHAR_MAX + 1],
1011 const char *targets, const char *def,
1012 struct string *result);
1013 static void clear_fields (struct import_assistant *ia);
1014 static void revise_fields_preview (struct import_assistant *);
1015 static void set_separators (struct import_assistant *);
1016 static void get_separators (struct import_assistant *);
1017 static void on_separators_custom_entry_notify (GObject *UNUSED,
1019 struct import_assistant *);
1020 static void on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
1021 struct import_assistant *);
1022 static void on_quote_combo_change (GtkComboBox *combo,
1023 struct import_assistant *);
1024 static void on_quote_cb_toggle (GtkToggleButton *quote_cb,
1025 struct import_assistant *);
1026 static void on_separator_toggle (GtkToggleButton *, struct import_assistant *);
1027 static void render_input_cell (GtkTreeViewColumn *tree_column,
1028 GtkCellRenderer *cell,
1029 GtkTreeModel *model, GtkTreeIter *iter,
1031 static gboolean on_query_input_tooltip (GtkWidget *widget, gint wx, gint wy,
1032 gboolean keyboard_mode UNUSED,
1033 GtkTooltip *tooltip,
1034 struct import_assistant *);
1036 /* A common field separator and its identifying name. */
1039 const char *name; /* Name (for use with get_widget_assert). */
1040 int c; /* Separator character. */
1043 /* All the separators in the dialog box. */
1044 static const struct separator separators[] =
1056 #define SEPARATOR_CNT (sizeof separators / sizeof *separators)
1059 set_quote_list (GtkComboBoxEntry *cb)
1061 GtkListStore *list = gtk_list_store_new (1, G_TYPE_STRING);
1064 const gchar *seperator[3] = {"'\"", "\'", "\""};
1066 for (i = 0; i < 3; i++)
1068 const gchar *s = seperator[i];
1070 /* Add a new row to the model */
1071 gtk_list_store_append (list, &iter);
1072 gtk_list_store_set (list, &iter,
1078 gtk_combo_box_set_model (GTK_COMBO_BOX (cb), GTK_TREE_MODEL (list));
1080 gtk_combo_box_entry_set_text_column (cb, 0);
1083 /* Initializes IA's separators substructure. */
1085 init_separators_page (struct import_assistant *ia)
1087 GtkBuilder *builder = ia->asst.builder;
1088 struct separators_page *p = &ia->separators;
1091 choose_likely_separators (ia);
1093 p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Separators"),
1094 GTK_ASSISTANT_PAGE_CONTENT);
1095 p->custom_cb = get_widget_assert (builder, "custom-cb");
1096 p->custom_entry = get_widget_assert (builder, "custom-entry");
1097 p->quote_combo = get_widget_assert (builder, "quote-combo");
1098 p->quote_entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (p->quote_combo)));
1099 p->quote_cb = get_widget_assert (builder, "quote-cb");
1100 p->escape_cb = get_widget_assert (builder, "escape");
1102 set_separators (ia);
1103 set_quote_list (GTK_COMBO_BOX_ENTRY (p->quote_combo));
1104 p->fields_tree_view = GTK_TREE_VIEW (get_widget_assert (builder, "fields"));
1105 g_signal_connect (p->quote_combo, "changed",
1106 G_CALLBACK (on_quote_combo_change), ia);
1107 g_signal_connect (p->quote_cb, "toggled",
1108 G_CALLBACK (on_quote_cb_toggle), ia);
1109 g_signal_connect (p->custom_entry, "notify::text",
1110 G_CALLBACK (on_separators_custom_entry_notify), ia);
1111 g_signal_connect (p->custom_cb, "toggled",
1112 G_CALLBACK (on_separators_custom_cb_toggle), ia);
1113 for (i = 0; i < SEPARATOR_CNT; i++)
1114 g_signal_connect (get_widget_assert (builder, separators[i].name),
1115 "toggled", G_CALLBACK (on_separator_toggle), ia);
1116 g_signal_connect (p->escape_cb, "toggled",
1117 G_CALLBACK (on_separator_toggle), ia);
1120 /* Frees IA's separators substructure. */
1122 destroy_separators_page (struct import_assistant *ia)
1124 struct separators_page *s = &ia->separators;
1126 ds_destroy (&s->separators);
1127 ds_destroy (&s->quotes);
1131 /* Called just before the separators page becomes visible in the
1134 prepare_separators_page (struct import_assistant *ia)
1136 revise_fields_preview (ia);
1139 /* Called when the Reset button is clicked on the separators
1140 page, resets the separators to the defaults. */
1142 reset_separators_page (struct import_assistant *ia)
1144 choose_likely_separators (ia);
1145 set_separators (ia);
1148 /* Frees and clears the column data in IA's separators
1151 clear_fields (struct import_assistant *ia)
1153 struct separators_page *s = &ia->separators;
1155 if (s->column_cnt > 0)
1160 for (row = 0; row < ia->file.line_cnt; row++)
1162 const struct string *line = &ia->file.lines[row];
1163 const char *line_start = ds_data (line);
1164 const char *line_end = ds_end (line);
1166 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1168 char *s = ss_data (col->contents[row]);
1169 if (!(s >= line_start && s <= line_end))
1170 ss_dealloc (&col->contents[row]);
1174 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1177 free (col->contents);
1186 /* Breaks the file data in IA into columns based on the
1187 separators set in IA's separators substructure. */
1189 split_fields (struct import_assistant *ia)
1191 struct separators_page *s = &ia->separators;
1192 size_t columns_allocated;
1198 /* Is space in the set of separators? */
1199 space_sep = ss_find_byte (ds_ss (&s->separators), ' ') != SIZE_MAX;
1201 /* Split all the lines, not just those from
1202 ia->first_line.skip_lines on, so that we split the line that
1203 contains variables names if ia->first_line.variable_names is
1205 columns_allocated = 0;
1206 for (row = 0; row < ia->file.line_cnt; row++)
1208 struct string *line = &ia->file.lines[row];
1209 struct substring text = ds_ss (line);
1212 for (column_idx = 0; ; column_idx++)
1214 struct substring field;
1215 struct column *column;
1218 ss_ltrim (&text, ss_cstr (" "));
1219 if (ss_is_empty (text))
1221 if (column_idx != 0)
1225 else if (!ds_is_empty (&s->quotes)
1226 && ds_find_byte (&s->quotes, text.string[0]) != SIZE_MAX)
1228 int quote = ss_get_byte (&text);
1230 ss_get_until (&text, quote, &field);
1237 while ((c = ss_get_byte (&text)) != EOF)
1239 ds_put_byte (&s, c);
1240 else if (ss_match_byte (&text, quote))
1241 ds_put_byte (&s, quote);
1248 ss_get_bytes (&text, ss_cspan (text, ds_ss (&s->separators)),
1251 if (column_idx >= s->column_cnt)
1253 struct column *column;
1255 if (s->column_cnt >= columns_allocated)
1256 s->columns = x2nrealloc (s->columns, &columns_allocated,
1257 sizeof *s->columns);
1258 column = &s->columns[s->column_cnt++];
1259 column->name = NULL;
1261 column->contents = xcalloc (ia->file.line_cnt,
1262 sizeof *column->contents);
1264 column = &s->columns[column_idx];
1265 column->contents[row] = field;
1266 if (ss_length (field) > column->width)
1267 column->width = ss_length (field);
1270 ss_ltrim (&text, ss_cstr (" "));
1271 if (ss_is_empty (text))
1273 if (ss_find_byte (ds_ss (&s->separators), ss_first (text))
1275 ss_advance (&text, 1);
1280 /* Chooses a name for each column on the separators page */
1282 choose_column_names (struct import_assistant *ia)
1284 const struct first_line_page *f = &ia->first_line;
1285 struct separators_page *s = &ia->separators;
1286 struct dictionary *dict;
1287 unsigned long int generated_name_count = 0;
1291 dict = dict_create (get_default_encoding ());
1292 name_row = f->variable_names && f->skip_lines ? f->skip_lines : 0;
1293 for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
1297 hint = name_row ? ss_xstrdup (col->contents[name_row - 1]) : NULL;
1298 name = dict_make_unique_var_name (dict, hint, &generated_name_count);
1302 dict_create_var_assert (dict, name, 0);
1304 dict_destroy (dict);
1307 /* Picks the most likely separator and quote characters based on
1310 choose_likely_separators (struct import_assistant *ia)
1312 unsigned long int histogram[UCHAR_MAX + 1] = { 0 };
1315 /* Construct a histogram of all the characters used in the
1317 for (row = 0; row < ia->file.line_cnt; row++)
1319 struct substring line = ds_ss (&ia->file.lines[row]);
1320 size_t length = ss_length (line);
1322 for (i = 0; i < length; i++)
1323 histogram[(unsigned char) line.string[i]]++;
1326 find_commonest_chars (histogram, "\"'", "", &ia->separators.quotes);
1327 find_commonest_chars (histogram, ",;:/|!\t-", ",",
1328 &ia->separators.separators);
1329 ia->separators.escape = true;
1332 /* Chooses the most common character among those in TARGETS,
1333 based on the frequency data in HISTOGRAM, and stores it in
1334 RESULT. If there is a tie for the most common character among
1335 those in TARGETS, the earliest character is chosen. If none
1336 of the TARGETS appear at all, then DEF is used as a
1339 find_commonest_chars (unsigned long int histogram[UCHAR_MAX + 1],
1340 const char *targets, const char *def,
1341 struct string *result)
1343 unsigned char max = 0;
1344 unsigned long int max_count = 0;
1346 for (; *targets != '\0'; targets++)
1348 unsigned char c = *targets;
1349 unsigned long int count = histogram[c];
1350 if (count > max_count)
1359 ds_put_byte (result, max);
1362 ds_assign_cstr (result, def);
1365 /* Revises the contents of the fields tree view based on the
1366 currently chosen set of separators. */
1368 revise_fields_preview (struct import_assistant *ia)
1372 push_watch_cursor (ia);
1374 w = GTK_WIDGET (ia->separators.fields_tree_view);
1375 gtk_widget_destroy (w);
1376 get_separators (ia);
1378 choose_column_names (ia);
1379 ia->separators.fields_tree_view = create_data_tree_view (
1381 GTK_CONTAINER (get_widget_assert (ia->asst.builder, "fields-scroller")),
1384 pop_watch_cursor (ia);
1387 /* Sets the widgets to match IA's separators substructure. */
1389 set_separators (struct import_assistant *ia)
1391 struct separators_page *s = &ia->separators;
1393 struct string custom;
1398 ds_init_empty (&custom);
1400 for (i = 0; i < ds_length (&s->separators); i++)
1402 unsigned char c = ds_at (&s->separators, i);
1405 for (j = 0; j < SEPARATOR_CNT; j++)
1407 const struct separator *s = &separators[j];
1415 ds_put_byte (&custom, c);
1419 for (i = 0; i < SEPARATOR_CNT; i++)
1421 const struct separator *s = &separators[i];
1422 GtkWidget *button = get_widget_assert (ia->asst.builder, s->name);
1423 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
1424 (seps & (1u << i)) != 0);
1426 any_custom = !ds_is_empty (&custom);
1427 gtk_entry_set_text (GTK_ENTRY (s->custom_entry), ds_cstr (&custom));
1428 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->custom_cb),
1430 gtk_widget_set_sensitive (s->custom_entry, any_custom);
1431 ds_destroy (&custom);
1433 any_quotes = !ds_is_empty (&s->quotes);
1435 gtk_entry_set_text (s->quote_entry,
1436 any_quotes ? ds_cstr (&s->quotes) : "\"");
1437 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->quote_cb),
1439 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->escape_cb),
1441 gtk_widget_set_sensitive (s->quote_combo, any_quotes);
1442 gtk_widget_set_sensitive (s->escape_cb, any_quotes);
1445 /* Sets IA's separators substructure to match the widgets. */
1447 get_separators (struct import_assistant *ia)
1449 struct separators_page *s = &ia->separators;
1452 ds_clear (&s->separators);
1453 for (i = 0; i < SEPARATOR_CNT; i++)
1455 const struct separator *sep = &separators[i];
1456 GtkWidget *button = get_widget_assert (ia->asst.builder, sep->name);
1457 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
1458 ds_put_byte (&s->separators, sep->c);
1461 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->custom_cb)))
1462 ds_put_cstr (&s->separators,
1463 gtk_entry_get_text (GTK_ENTRY (s->custom_entry)));
1465 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->quote_cb)))
1467 gchar *text = gtk_combo_box_get_active_text (
1468 GTK_COMBO_BOX (s->quote_combo));
1469 ds_assign_cstr (&s->quotes, text);
1473 ds_clear (&s->quotes);
1474 s->escape = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->escape_cb));
1477 /* Called when the user changes the entry field for custom
1480 on_separators_custom_entry_notify (GObject *gobject UNUSED,
1481 GParamSpec *arg1 UNUSED,
1482 struct import_assistant *ia)
1484 revise_fields_preview (ia);
1487 /* Called when the user toggles the checkbox that enables custom
1490 on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
1491 struct import_assistant *ia)
1493 bool is_active = gtk_toggle_button_get_active (custom_cb);
1494 gtk_widget_set_sensitive (ia->separators.custom_entry, is_active);
1495 revise_fields_preview (ia);
1498 /* Called when the user changes the selection in the combo box
1499 that selects a quote character. */
1501 on_quote_combo_change (GtkComboBox *combo, struct import_assistant *ia)
1503 revise_fields_preview (ia);
1506 /* Called when the user toggles the checkbox that enables
1509 on_quote_cb_toggle (GtkToggleButton *quote_cb, struct import_assistant *ia)
1511 bool is_active = gtk_toggle_button_get_active (quote_cb);
1512 gtk_widget_set_sensitive (ia->separators.quote_combo, is_active);
1513 gtk_widget_set_sensitive (ia->separators.escape_cb, is_active);
1514 revise_fields_preview (ia);
1517 /* Called when the user toggles one of the separators
1520 on_separator_toggle (GtkToggleButton *toggle UNUSED,
1521 struct import_assistant *ia)
1523 revise_fields_preview (ia);
1526 /* Called to render one of the cells in the fields preview tree
1529 render_input_cell (GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1530 GtkTreeModel *model, GtkTreeIter *iter,
1533 struct import_assistant *ia = ia_;
1534 struct substring field;
1538 column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1540 row = text_import_model_iter_to_row (iter) + ia->first_line.skip_lines;
1541 field = ia->separators.columns[column].contents[row];
1542 if (field.string != NULL)
1544 GValue text = {0, };
1545 g_value_init (&text, G_TYPE_STRING);
1546 g_value_take_string (&text, ss_xstrdup (field));
1547 g_object_set_property (G_OBJECT (cell), "text", &text);
1548 g_value_unset (&text);
1549 g_object_set (cell, "background-set", FALSE, (void *) NULL);
1554 "background", "red",
1555 "background-set", TRUE,
1559 /* Called to render a tooltip on one of the cells in the fields
1560 preview tree view. */
1562 on_query_input_tooltip (GtkWidget *widget, gint wx, gint wy,
1563 gboolean keyboard_mode UNUSED,
1564 GtkTooltip *tooltip, struct import_assistant *ia)
1568 if (!get_tooltip_location (widget, wx, wy, ia, &row, &column))
1571 if (ia->separators.columns[column].contents[row].string != NULL)
1574 gtk_tooltip_set_text (tooltip,
1575 _("This input line has too few separators "
1576 "to fill in this field."));
1580 /* The "formats" page of the assistant. */
1582 static void on_variable_change (PsppireDict *dict, int idx,
1583 struct import_assistant *);
1584 static void clear_modified_vars (struct import_assistant *);
1586 /* Initializes IA's formats substructure. */
1588 init_formats_page (struct import_assistant *ia)
1590 GtkBuilder *builder = ia->asst.builder;
1591 struct formats_page *p = &ia->formats;
1593 p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Formats"),
1594 GTK_ASSISTANT_PAGE_CONFIRM);
1595 p->data_tree_view = GTK_TREE_VIEW (get_widget_assert (builder, "data"));
1596 p->modified_vars = NULL;
1597 p->modified_var_cnt = 0;
1601 /* Frees IA's formats substructure. */
1603 destroy_formats_page (struct import_assistant *ia)
1605 struct formats_page *p = &ia->formats;
1607 if (p->psppire_dict != NULL)
1609 /* This destroys p->dict also. */
1610 g_object_unref (p->psppire_dict);
1612 clear_modified_vars (ia);
1615 /* Called just before the formats page of the assistant is
1618 prepare_formats_page (struct import_assistant *ia)
1620 struct dictionary *dict;
1621 PsppireDict *psppire_dict;
1622 PsppireVarStore *var_store;
1623 GtkBin *vars_scroller;
1624 GtkWidget *old_var_sheet;
1625 PsppireVarSheet *var_sheet;
1626 struct separators_page *s = &ia->separators;
1627 struct formats_page *p = &ia->formats;
1628 struct fmt_guesser *fg;
1629 unsigned long int number = 0;
1632 push_watch_cursor (ia);
1634 dict = dict_create (get_default_encoding ());
1635 fg = fmt_guesser_create ();
1636 for (column_idx = 0; column_idx < s->column_cnt; column_idx++)
1638 struct variable *modified_var;
1640 modified_var = (column_idx < p->modified_var_cnt
1641 ? p->modified_vars[column_idx] : NULL);
1642 if (modified_var == NULL)
1644 struct column *column = &s->columns[column_idx];
1645 struct variable *var;
1646 struct fmt_spec format;
1650 /* Choose variable name. */
1651 name = dict_make_unique_var_name (dict, column->name, &number);
1653 /* Choose variable format. */
1654 fmt_guesser_clear (fg);
1655 for (row = ia->first_line.skip_lines; row < ia->file.line_cnt; row++)
1656 fmt_guesser_add (fg, column->contents[row]);
1657 fmt_guesser_guess (fg, &format);
1658 fmt_fix_input (&format);
1660 /* Create variable. */
1661 var = dict_create_var_assert (dict, name, fmt_var_width (&format));
1662 var_set_both_formats (var, &format);
1670 name = dict_make_unique_var_name (dict, var_get_name (modified_var),
1672 dict_clone_var_as_assert (dict, modified_var, name);
1676 fmt_guesser_destroy (fg);
1678 psppire_dict = psppire_dict_new_from_dict (dict);
1679 g_signal_connect (psppire_dict, "variable_changed",
1680 G_CALLBACK (on_variable_change), ia);
1681 ia->formats.dict = dict;
1682 ia->formats.psppire_dict = psppire_dict;
1684 /* XXX: PsppireVarStore doesn't hold a reference to
1685 psppire_dict for now, but it should. After it does, we
1686 should g_object_ref the psppire_dict here, since we also
1687 hold a reference via ia->formats.dict. */
1688 var_store = psppire_var_store_new (psppire_dict);
1689 g_object_set (var_store,
1690 "format-type", PSPPIRE_VAR_STORE_INPUT_FORMATS,
1692 var_sheet = PSPPIRE_VAR_SHEET (psppire_var_sheet_new ());
1693 g_object_set (var_sheet,
1695 "may-create-vars", FALSE,
1698 vars_scroller = GTK_BIN (get_widget_assert (ia->asst.builder, "vars-scroller"));
1699 old_var_sheet = gtk_bin_get_child (vars_scroller);
1700 if (old_var_sheet != NULL)
1701 gtk_widget_destroy (old_var_sheet);
1702 gtk_container_add (GTK_CONTAINER (vars_scroller), GTK_WIDGET (var_sheet));
1703 gtk_widget_show (GTK_WIDGET (var_sheet));
1705 gtk_widget_destroy (GTK_WIDGET (ia->formats.data_tree_view));
1706 ia->formats.data_tree_view = create_data_tree_view (
1708 GTK_CONTAINER (get_widget_assert (ia->asst.builder, "data-scroller")),
1711 pop_watch_cursor (ia);
1714 /* Clears the set of user-modified variables from IA's formats
1715 substructure. This discards user modifications to variable
1716 formats, thereby causing formats to revert to their
1719 clear_modified_vars (struct import_assistant *ia)
1721 struct formats_page *p = &ia->formats;
1724 for (i = 0; i < p->modified_var_cnt; i++)
1725 var_destroy (p->modified_vars[i]);
1726 free (p->modified_vars);
1727 p->modified_vars = NULL;
1728 p->modified_var_cnt = 0;
1731 /* Resets the formats page to its defaults, discarding user
1734 reset_formats_page (struct import_assistant *ia)
1736 clear_modified_vars (ia);
1737 prepare_formats_page (ia);
1740 /* Called when the user changes one of the variables in the
1743 on_variable_change (PsppireDict *dict, int dict_idx,
1744 struct import_assistant *ia)
1746 struct formats_page *p = &ia->formats;
1747 GtkTreeView *tv = ia->formats.data_tree_view;
1748 gint column_idx = dict_idx + 1;
1750 push_watch_cursor (ia);
1752 /* Remove previous column and replace with new column. */
1753 gtk_tree_view_remove_column (tv, gtk_tree_view_get_column (tv, column_idx));
1754 gtk_tree_view_insert_column (tv, make_data_column (ia, tv, false, dict_idx),
1757 /* Save a copy of the modified variable in modified_vars, so
1758 that its attributes will be preserved if we back up to the
1759 previous page with the Prev button and then come back
1761 if (dict_idx >= p->modified_var_cnt)
1764 p->modified_vars = xnrealloc (p->modified_vars, dict_idx + 1,
1765 sizeof *p->modified_vars);
1766 for (i = 0; i <= dict_idx; i++)
1767 p->modified_vars[i] = NULL;
1768 p->modified_var_cnt = dict_idx + 1;
1770 if (p->modified_vars[dict_idx])
1771 var_destroy (p->modified_vars[dict_idx]);
1772 p->modified_vars[dict_idx]
1773 = var_clone (psppire_dict_get_variable (dict, dict_idx));
1775 pop_watch_cursor (ia);
1778 /* Parses the contents of the field at (ROW,COLUMN) according to
1779 its variable format. If OUTPUTP is non-null, then *OUTPUTP
1780 receives the formatted output for that field (which must be
1781 freed with free). If TOOLTIPP is non-null, then *TOOLTIPP
1782 receives a message suitable for use in a tooltip, if one is
1783 needed, or a null pointer otherwise. Returns true if a
1784 tooltip message is needed, otherwise false. */
1786 parse_field (struct import_assistant *ia,
1787 size_t row, size_t column,
1788 char **outputp, char **tooltipp)
1790 struct substring field;
1792 struct variable *var;
1793 const struct fmt_spec *in;
1794 struct fmt_spec out;
1798 field = ia->separators.columns[column].contents[row];
1799 var = dict_get_var (ia->formats.dict, column);
1800 value_init (&val, var_get_width (var));
1801 in = var_get_print_format (var);
1802 out = fmt_for_output_from_input (in);
1804 if (field.string != NULL)
1808 error = data_in (field, C_ENCODING, in->type, &val, var_get_width (var),
1809 dict_get_encoding (ia->formats.dict));
1812 tooltip = xasprintf (_("Cannot parse field content `%.*s' as "
1814 (int) field.length, field.string,
1815 fmt_name (in->type), error);
1821 tooltip = xstrdup (_("This input line has too few separators "
1822 "to fill in this field."));
1823 value_set_missing (&val, var_get_width (var));
1825 if (outputp != NULL)
1827 *outputp = data_out (&val, dict_get_encoding (ia->formats.dict), &out);
1829 value_destroy (&val, var_get_width (var));
1831 ok = tooltip == NULL;
1832 if (tooltipp != NULL)
1833 *tooltipp = tooltip;
1839 /* Called to render one of the cells in the data preview tree
1842 render_output_cell (GtkTreeViewColumn *tree_column,
1843 GtkCellRenderer *cell,
1844 GtkTreeModel *model,
1848 struct import_assistant *ia = ia_;
1850 GValue gvalue = { 0, };
1853 ok = parse_field (ia,
1854 (text_import_model_iter_to_row (iter)
1855 + ia->first_line.skip_lines),
1856 GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1860 g_value_init (&gvalue, G_TYPE_STRING);
1861 g_value_take_string (&gvalue, output);
1862 g_object_set_property (G_OBJECT (cell), "text", &gvalue);
1863 g_value_unset (&gvalue);
1866 g_object_set (cell, "background-set", FALSE, (void *) NULL);
1869 "background", "red",
1870 "background-set", TRUE,
1874 /* Called to render a tooltip for one of the cells in the data
1875 preview tree view. */
1877 on_query_output_tooltip (GtkWidget *widget, gint wx, gint wy,
1878 gboolean keyboard_mode UNUSED,
1879 GtkTooltip *tooltip, struct import_assistant *ia)
1884 if (!get_tooltip_location (widget, wx, wy, ia, &row, &column))
1887 if (parse_field (ia, row, column, NULL, &text))
1890 gtk_tooltip_set_text (tooltip, text);
1895 /* Utility functions used by multiple pages of the assistant. */
1898 get_tooltip_location (GtkWidget *widget, gint wx, gint wy,
1899 const struct import_assistant *ia,
1900 size_t *row, size_t *column)
1902 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
1906 GtkTreeViewColumn *tree_column;
1907 GtkTreeModel *tree_model;
1910 /* Check that WIDGET is really visible on the screen before we
1911 do anything else. This is a bug fix for a sticky situation:
1912 when text_data_import_assistant() returns, it frees the data
1913 necessary to compose the tool tip message, but there may be
1914 a tool tip under preparation at that point (even if there is
1915 no visible tool tip) that will call back into us a little
1916 bit later. Perhaps the correct solution to this problem is
1917 to make the data related to the tool tips part of a GObject
1918 that only gets destroyed when all references are released,
1919 but this solution appears to be effective too. */
1920 if (!gtk_widget_get_mapped (widget))
1923 gtk_tree_view_convert_widget_to_bin_window_coords (tree_view,
1925 if (!gtk_tree_view_get_path_at_pos (tree_view, bx, by,
1926 &path, &tree_column, NULL, NULL))
1929 *column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
1932 tree_model = gtk_tree_view_get_model (tree_view);
1933 ok = gtk_tree_model_get_iter (tree_model, &iter, path);
1934 gtk_tree_path_free (path);
1938 *row = text_import_model_iter_to_row (&iter) + ia->first_line.skip_lines;
1943 make_tree_view (const struct import_assistant *ia,
1945 GtkTreeView **tree_view)
1947 GtkTreeModel *model;
1949 *tree_view = GTK_TREE_VIEW (gtk_tree_view_new ());
1950 model = GTK_TREE_MODEL (text_import_model_new (
1951 ia->file.lines + first_line,
1952 ia->file.line_cnt - first_line, first_line));
1953 gtk_tree_view_set_model (*tree_view, model);
1955 add_line_number_column (ia, *tree_view);
1959 add_line_number_column (const struct import_assistant *ia,
1960 GtkTreeView *treeview)
1962 GtkTreeViewColumn *column;
1964 column = gtk_tree_view_column_new_with_attributes (
1965 _("Line"), ia->asst.prop_renderer,
1966 "text", TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER,
1968 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
1969 gtk_tree_view_column_set_fixed_width (
1970 column, get_monospace_width (treeview, ia->asst.prop_renderer, 5));
1971 gtk_tree_view_append_column (treeview, column);
1975 get_monospace_width (GtkTreeView *treeview, GtkCellRenderer *renderer,
1982 ds_put_byte_multiple (&s, '0', char_cnt);
1983 ds_put_byte (&s, ' ');
1984 width = get_string_width (treeview, renderer, ds_cstr (&s));
1991 get_string_width (GtkTreeView *treeview, GtkCellRenderer *renderer,
1995 g_object_set (G_OBJECT (renderer), "text", string, (void *) NULL);
1996 gtk_cell_renderer_get_size (renderer, GTK_WIDGET (treeview),
1997 NULL, NULL, NULL, &width, NULL);
2001 static GtkTreeViewColumn *
2002 make_data_column (struct import_assistant *ia, GtkTreeView *tree_view,
2003 bool input, gint dict_idx)
2005 struct variable *var = NULL;
2006 struct column *column = NULL;
2008 gint content_width, header_width;
2009 GtkTreeViewColumn *tree_column;
2013 column = &ia->separators.columns[dict_idx];
2015 var = dict_get_var (ia->formats.dict, dict_idx);
2017 name = escape_underscores (input ? column->name : var_get_name (var));
2018 char_cnt = input ? column->width : var_get_print_format (var)->w;
2019 content_width = get_monospace_width (tree_view, ia->asst.fixed_renderer,
2021 header_width = get_string_width (tree_view, ia->asst.prop_renderer,
2024 tree_column = gtk_tree_view_column_new ();
2025 g_object_set_data (G_OBJECT (tree_column), "column-number",
2026 GINT_TO_POINTER (dict_idx));
2027 gtk_tree_view_column_set_title (tree_column, name);
2028 gtk_tree_view_column_pack_start (tree_column, ia->asst.fixed_renderer,
2030 gtk_tree_view_column_set_cell_data_func (
2031 tree_column, ia->asst.fixed_renderer,
2032 input ? render_input_cell : render_output_cell, ia, NULL);
2033 gtk_tree_view_column_set_sizing (tree_column, GTK_TREE_VIEW_COLUMN_FIXED);
2034 gtk_tree_view_column_set_fixed_width (tree_column, MAX (content_width,
2042 static GtkTreeView *
2043 create_data_tree_view (bool input, GtkContainer *parent,
2044 struct import_assistant *ia)
2046 GtkTreeView *tree_view;
2049 make_tree_view (ia, ia->first_line.skip_lines, &tree_view);
2050 gtk_tree_selection_set_mode (gtk_tree_view_get_selection (tree_view),
2051 GTK_SELECTION_NONE);
2053 for (i = 0; i < ia->separators.column_cnt; i++)
2054 gtk_tree_view_append_column (tree_view,
2055 make_data_column (ia, tree_view, input, i));
2057 g_object_set (G_OBJECT (tree_view), "has-tooltip", TRUE, (void *) NULL);
2058 g_signal_connect (tree_view, "query-tooltip",
2059 G_CALLBACK (input ? on_query_input_tooltip
2060 : on_query_output_tooltip), ia);
2061 gtk_tree_view_set_fixed_height_mode (tree_view, true);
2063 gtk_container_add (parent, GTK_WIDGET (tree_view));
2064 gtk_widget_show (GTK_WIDGET (tree_view));
2070 escape_underscores (const char *in)
2072 char *out = xmalloc (2 * strlen (in) + 1);
2076 for (; *in != '\0'; in++)
2087 /* TextImportModel, a GtkTreeModel implementation used by some
2088 pages of the assistant. */
2090 #define G_TYPE_TEXT_IMPORT_MODEL (text_import_model_get_type ())
2091 #define TEXT_IMPORT_MODEL(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), G_TYPE_TEXT_IMPORT_MODEL, TextImportModel))
2092 #define TEXT_IMPORT_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), G_TYPE_TEXT_IMPORT_MODEL, TextImportModelClass))
2093 #define IS_TEXT_IMPORT_MODEL(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), G_TYPE_TEXT_IMPORT_MODEL))
2094 #define IS_TEXT_IMPORT_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), G_TYPE_TEXT_IMPORT_MODEL))
2095 #define TEXT_IMPORT_MODEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), G_TYPE_TEXT_IMPORT_MODEL, TextImportModelClass))
2097 /* Random number used in 'stamp' member of GtkTreeIter. */
2098 #define TREE_MODEL_STAMP 0x7efd67d3
2100 struct TextImportModel
2103 struct string *lines;
2108 struct TextImportModelClass
2110 GObjectClass parent_class;
2113 GType text_import_model_get_type (void);
2114 static void text_import_model_tree_model_init (gpointer iface, gpointer data);
2117 text_import_model_get_type (void)
2119 static GType object_type = 0;
2123 static const GTypeInfo object_info = {
2124 sizeof (TextImportModelClass),
2125 (GBaseInitFunc) NULL,
2126 (GBaseFinalizeFunc) NULL,
2127 NULL, /* class_init */
2128 NULL, /* class_finalize */
2129 NULL, /* class_data */
2130 sizeof (TextImportModel),
2131 0, /* n_preallocs */
2132 NULL, /* instance_init */
2135 static const GInterfaceInfo tree_model_info = {
2136 text_import_model_tree_model_init,
2141 object_type = g_type_register_static (G_TYPE_OBJECT,
2145 g_type_add_interface_static (object_type, GTK_TYPE_TREE_MODEL,
2155 /* Creates and returns a new TextImportModel that contains the
2156 LINE_CNT lines in LINES. The lines before FIRST_LINE in LINES
2157 are not part of the model, but they are included in the line
2158 numbers in the TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER column.
2160 The caller retains responsibility for freeing LINES and must
2161 ensure that its lifetime and that of the strings that it
2162 contains exceeds that of the TextImportModel. */
2164 text_import_model_new (struct string *lines, size_t line_cnt,
2167 TextImportModel *new_text_import_model
2168 = g_object_new (G_TYPE_TEXT_IMPORT_MODEL, NULL);
2169 new_text_import_model->lines = lines;
2170 new_text_import_model->line_cnt = line_cnt;
2171 new_text_import_model->first_line = first_line;
2172 return new_text_import_model;
2177 tree_model_iter_has_child (GtkTreeModel *tree_model,
2184 tree_model_iter_parent (GtkTreeModel *tree_model,
2191 static GtkTreeModelFlags
2192 tree_model_get_flags (GtkTreeModel *model)
2194 g_return_val_if_fail (IS_TEXT_IMPORT_MODEL (model), (GtkTreeModelFlags) 0);
2196 return GTK_TREE_MODEL_LIST_ONLY | GTK_TREE_MODEL_ITERS_PERSIST;
2201 tree_model_n_columns (GtkTreeModel *model)
2207 tree_model_column_type (GtkTreeModel *model, gint index)
2209 return (index == TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER ? G_TYPE_INT
2210 : index == TEXT_IMPORT_MODEL_COLUMN_LINE ? G_TYPE_STRING
2215 init_iter (TextImportModel *list, gint idx, GtkTreeIter *iter)
2217 if (idx < 0 || idx >= list->line_cnt)
2220 iter->user_data = GINT_TO_POINTER (-1);
2225 iter->stamp = TREE_MODEL_STAMP;
2226 iter->user_data = GINT_TO_POINTER (idx);
2232 tree_model_get_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreePath *path)
2234 gint *indices, depth;
2236 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2238 g_return_val_if_fail (path, FALSE);
2240 indices = gtk_tree_path_get_indices (path);
2241 depth = gtk_tree_path_get_depth (path);
2243 g_return_val_if_fail (depth == 1, FALSE);
2245 return init_iter (list, indices[0], iter);
2250 tree_model_iter_next (GtkTreeModel *model, GtkTreeIter *iter)
2252 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2255 assert (iter->stamp == TREE_MODEL_STAMP);
2257 idx = GPOINTER_TO_INT (iter->user_data);
2258 return init_iter (list, idx == -1 ? -1 : idx + 1, iter);
2261 static GtkTreePath *
2262 tree_model_get_path (GtkTreeModel *model, GtkTreeIter *iter)
2266 g_return_val_if_fail (iter->stamp == TREE_MODEL_STAMP, FALSE);
2268 path = gtk_tree_path_new ();
2269 gtk_tree_path_append_index (path, GPOINTER_TO_INT (iter->user_data));
2275 tree_model_get_value (GtkTreeModel *model, GtkTreeIter *iter,
2276 gint column, GValue *value)
2278 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2281 g_return_if_fail (iter->stamp == TREE_MODEL_STAMP);
2283 idx = GPOINTER_TO_INT (iter->user_data);
2284 assert (idx >= 0 && idx < list->line_cnt);
2288 g_value_init (value, G_TYPE_INT);
2289 g_value_set_int (value, idx + list->first_line + 1);
2293 g_value_init (value, G_TYPE_STRING);
2294 g_value_set_static_string (value, ds_cstr (&list->lines[idx]));
2299 tree_model_iter_children (GtkTreeModel *tree_model,
2301 GtkTreeIter *parent)
2307 tree_model_n_children (GtkTreeModel *model, GtkTreeIter *iter)
2309 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2311 return iter == NULL ? list->line_cnt : 0;
2315 tree_model_nth_child (GtkTreeModel *model, GtkTreeIter *iter,
2316 GtkTreeIter *parent, gint n)
2318 TextImportModel *list = TEXT_IMPORT_MODEL (model);
2319 g_return_val_if_fail (IS_TEXT_IMPORT_MODEL (model), FALSE);
2323 return init_iter (list, n, iter);
2327 text_import_model_tree_model_init (gpointer iface_, gpointer data UNUSED)
2329 GtkTreeModelIface *iface = (GtkTreeModelIface *) iface_;
2331 iface->get_flags = tree_model_get_flags;
2332 iface->get_n_columns = tree_model_n_columns;
2333 iface->get_column_type = tree_model_column_type;
2334 iface->get_iter = tree_model_get_iter;
2335 iface->iter_next = tree_model_iter_next;
2336 iface->get_path = tree_model_get_path;
2337 iface->get_value = tree_model_get_value;
2339 iface->iter_children = tree_model_iter_children;
2340 iface->iter_has_child = tree_model_iter_has_child;
2341 iface->iter_n_children = tree_model_n_children;
2342 iface->iter_nth_child = tree_model_nth_child;
2343 iface->iter_parent = tree_model_iter_parent;
2347 text_import_model_iter_to_row (const GtkTreeIter *iter)
2349 assert (iter->stamp == TREE_MODEL_STAMP);
2350 return GPOINTER_TO_INT (iter->user_data);
2353 /* Increments the "watch cursor" level, setting the cursor for
2354 the assistant window to a watch face to indicate to the user
2355 that the ongoing operation may take some time. */
2357 push_watch_cursor (struct import_assistant *ia)
2359 if (++ia->asst.watch_cursor == 1)
2361 GtkWidget *widget = GTK_WIDGET (ia->asst.assistant);
2362 GdkDisplay *display = gtk_widget_get_display (widget);
2363 GdkCursor *cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
2364 gdk_window_set_cursor (widget->window, cursor);
2365 gdk_cursor_unref (cursor);
2366 gdk_display_flush (display);
2370 /* Decrements the "watch cursor" level. If the level reaches
2371 zero, the cursor is reset to its default shape. */
2373 pop_watch_cursor (struct import_assistant *ia)
2375 if (--ia->asst.watch_cursor == 0)
2377 GtkWidget *widget = GTK_WIDGET (ia->asst.assistant);
2378 gdk_window_set_cursor (widget->window, NULL);