1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2015, 2016, 2017, 2018, 2020 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/>. */
21 #include "data/casereader.h"
22 #include "data/data-in.h"
23 #include "data/data-out.h"
24 #include "data/dictionary.h"
25 #include "data/format-guesser.h"
26 #include "data/format.h"
27 #include "data/gnumeric-reader.h"
28 #include "data/ods-reader.h"
29 #include "data/spreadsheet-reader.h"
30 #include "data/value-labels.h"
31 #include "data/casereader-provider.h"
33 #include "libpspp/i18n.h"
34 #include "libpspp/line-reader.h"
35 #include "libpspp/message.h"
36 #include "libpspp/hmap.h"
37 #include "libpspp/hash-functions.h"
38 #include "libpspp/str.h"
40 #include "builder-wrapper.h"
42 #include "psppire-data-sheet.h"
43 #include "psppire-data-store.h"
44 #include "psppire-dialog.h"
45 #include "psppire-delimited-text.h"
46 #include "psppire-dict.h"
47 #include "psppire-encoding-selector.h"
48 #include "psppire-import-assistant.h"
49 #include "psppire-scanf.h"
50 #include "psppire-spreadsheet-model.h"
51 #include "psppire-text-file.h"
52 #include "psppire-variable-sheet.h"
54 #include "ui/syntax-gen.h"
57 #define _(msgid) gettext (msgid)
58 #define N_(msgid) msgid
60 enum { MAX_LINE_LEN = 16384 }; /* Max length of an acceptable line. */
63 /* Chooses a name for each column on the separators page */
64 static void choose_column_names (PsppireImportAssistant *ia);
66 static void intro_page_create (PsppireImportAssistant *ia);
67 static void first_line_page_create (PsppireImportAssistant *ia);
69 static void separators_page_create (PsppireImportAssistant *ia);
70 static void formats_page_create (PsppireImportAssistant *ia);
72 static void psppire_import_assistant_init (PsppireImportAssistant *act);
73 static void psppire_import_assistant_class_init (PsppireImportAssistantClass *class);
75 G_DEFINE_TYPE (PsppireImportAssistant, psppire_import_assistant, GTK_TYPE_ASSISTANT);
85 psppire_import_assistant_set_property (GObject *object,
90 // PsppireImportAssistant *act = PSPPIRE_IMPORT_ASSISTANT (object);
95 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
102 psppire_import_assistant_get_property (GObject *object,
107 // PsppireImportAssistant *assistant = PSPPIRE_IMPORT_ASSISTANT (object);
112 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
117 static GObjectClass * parent_class = NULL;
121 psppire_import_assistant_finalize (GObject *object)
123 PsppireImportAssistant *ia = PSPPIRE_IMPORT_ASSISTANT (object);
126 spreadsheet_unref (ia->spreadsheet);
128 ds_destroy (&ia->quotes);
130 dict_unref (ia->dict);
131 dict_unref (ia->casereader_dict);
133 g_object_unref (ia->builder);
136 g_main_loop_unref (ia->main_loop);
138 if (G_OBJECT_CLASS (parent_class)->finalize)
139 G_OBJECT_CLASS (parent_class)->finalize (object);
144 psppire_import_assistant_class_init (PsppireImportAssistantClass *class)
146 GObjectClass *object_class = G_OBJECT_CLASS (class);
148 parent_class = g_type_class_peek_parent (class);
150 object_class->set_property = psppire_import_assistant_set_property;
151 object_class->get_property = psppire_import_assistant_get_property;
153 object_class->finalize = psppire_import_assistant_finalize;
157 /* Causes the assistant to close, returning RESPONSE for
158 interpretation by text_data_import_assistant. */
160 close_assistant (PsppireImportAssistant *ia, int response)
162 ia->response = response;
163 g_main_loop_quit (ia->main_loop);
164 gtk_widget_hide (GTK_WIDGET (ia));
168 /* Called when the Paste button on the last page of the assistant
171 on_paste (GtkButton *button, PsppireImportAssistant *ia)
173 close_assistant (ia, PSPPIRE_RESPONSE_PASTE);
177 /* Revises the contents of the fields tree view based on the
178 currently chosen set of separators. */
180 revise_fields_preview (PsppireImportAssistant *ia)
182 choose_column_names (ia);
188 const char *name; /* Name (for use with get_widget_assert). */
189 gunichar c; /* Separator character. */
192 /* All the separators in the dialog box. */
193 static const struct separator separators[] =
206 #define SEPARATOR_CNT (sizeof separators / sizeof *separators)
208 struct separator_count_node
210 struct hmap_node node;
211 int occurance; /* The number of times the separator occurs in a line */
212 int quantity; /* The number of lines with this occurance */
216 /* Picks the most likely separator and quote characters based on
219 choose_likely_separators (PsppireImportAssistant *ia)
222 g_object_get (ia->delimiters_model, "first-line", &first_line, NULL);
228 struct hmap count_map[SEPARATOR_CNT];
229 for (j = 0; j < SEPARATOR_CNT; ++j)
230 hmap_init (count_map + j);
232 GtkTreePath *p = gtk_tree_path_new_from_indices (first_line, -1);
234 for (valid = gtk_tree_model_get_iter (GTK_TREE_MODEL (ia->text_file), &iter, p);
236 valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (ia->text_file), &iter))
238 gchar *line_text = NULL;
239 gtk_tree_model_get (GTK_TREE_MODEL (ia->text_file), &iter, 1, &line_text, -1);
241 gint *counts = xzalloc (sizeof *counts * SEPARATOR_CNT);
243 struct substring cs = ss_cstr (line_text);
245 UINT32_MAX != ss_first_mb (cs);
248 ucs4_t character = ss_first_mb (cs);
251 for (s = 0; s < SEPARATOR_CNT; ++s)
253 if (character == separators[s].c)
259 for (j = 0; j < SEPARATOR_CNT; ++j)
263 struct separator_count_node *cn = NULL;
264 unsigned int hash = hash_int (counts[j], 0);
265 HMAP_FOR_EACH_WITH_HASH (cn, struct separator_count_node, node, hash, &count_map[j])
267 if (cn->occurance == counts[j])
273 struct separator_count_node *new_cn = xzalloc (sizeof *new_cn);
274 new_cn->occurance = counts[j];
275 new_cn->quantity = 1;
276 hmap_insert (&count_map[j], &new_cn->node, hash);
286 gtk_tree_path_free (p);
288 int most_frequent = -1;
290 for (j = 0; j < SEPARATOR_CNT; ++j)
292 struct separator_count_node *cn;
293 HMAP_FOR_EACH (cn, struct separator_count_node, node, &count_map[j])
295 if (largest < cn->quantity)
297 largest = cn->quantity;
301 hmap_destroy (&count_map[j]);
304 const char* sepname = separators[MAX (0,most_frequent)].name;
305 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (get_widget_assert (ia->builder, sepname)), TRUE);
309 repopulate_delimiter_columns (PsppireImportAssistant *ia)
311 /* Remove all the columns */
312 while (gtk_tree_view_get_n_columns (GTK_TREE_VIEW (ia->fields_tree_view)) > 0)
314 GtkTreeViewColumn *tvc = gtk_tree_view_get_column (GTK_TREE_VIEW (ia->fields_tree_view), 0);
315 gtk_tree_view_remove_column (GTK_TREE_VIEW (ia->fields_tree_view), tvc);
319 gtk_tree_model_get_n_columns (GTK_TREE_MODEL (ia->delimiters_model));
321 /* ... and put them back again. */
323 for (f = gtk_tree_view_get_n_columns (GTK_TREE_VIEW (ia->fields_tree_view));
326 GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
328 const gchar *title = NULL;
334 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->variable_names_cb)))
337 psppire_delimited_text_get_header_title
338 (PSPPIRE_DELIMITED_TEXT (ia->delimiters_model), f - 1);
344 GtkTreeViewColumn *column =
345 gtk_tree_view_column_new_with_attributes (title,
349 g_object_set (column,
351 "sizing", GTK_TREE_VIEW_COLUMN_AUTOSIZE,
354 gtk_tree_view_append_column (GTK_TREE_VIEW (ia->fields_tree_view), column);
359 reset_tree_view_model (PsppireImportAssistant *ia)
361 GtkTreeModel *tm = gtk_tree_view_get_model (GTK_TREE_VIEW (ia->fields_tree_view));
363 gtk_tree_view_set_model (GTK_TREE_VIEW (ia->fields_tree_view), NULL);
366 repopulate_delimiter_columns (ia);
368 gtk_tree_view_set_model (GTK_TREE_VIEW (ia->fields_tree_view), tm);
369 // gtk_tree_view_columns_autosize (GTK_TREE_VIEW (ia->fields_tree_view));
374 /* Called just before the separators page becomes visible in the
375 assistant, and when the Reset button is clicked. */
377 prepare_separators_page (PsppireImportAssistant *ia, GtkWidget *page)
379 gtk_tree_view_set_model (GTK_TREE_VIEW (ia->fields_tree_view),
380 GTK_TREE_MODEL (ia->delimiters_model));
382 g_signal_connect_swapped (GTK_TREE_MODEL (ia->delimiters_model), "notify::delimiters",
383 G_CALLBACK (reset_tree_view_model), ia);
386 repopulate_delimiter_columns (ia);
388 revise_fields_preview (ia);
389 choose_likely_separators (ia);
392 /* Resets IA's intro page to its initial state. */
394 reset_intro_page (PsppireImportAssistant *ia)
396 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->all_cases_button),
402 /* Clears the set of user-modified variables from IA's formats
403 substructure. This discards user modifications to variable
404 formats, thereby causing formats to revert to their
407 reset_formats_page (PsppireImportAssistant *ia, GtkWidget *page)
411 static void prepare_formats_page (PsppireImportAssistant *ia);
413 /* Called when the Reset button is clicked. */
415 on_reset (GtkButton *button, PsppireImportAssistant *ia)
417 gint pn = gtk_assistant_get_current_page (GTK_ASSISTANT (ia));
419 GtkWidget *page = gtk_assistant_get_nth_page (GTK_ASSISTANT (ia), pn);
421 page_func *on_reset = g_object_get_data (G_OBJECT (page), "on-reset");
430 next_page_func (gint old_page, gpointer data)
436 /* Called just before PAGE is displayed as the current page of
437 IMPORT_ASSISTANT, this updates IA content according to the new
440 on_prepare (GtkAssistant *assistant, GtkWidget *page, PsppireImportAssistant *ia)
442 gtk_widget_show (ia->reset_button);
443 gtk_widget_hide (ia->paste_button);
445 gint pn = gtk_assistant_get_current_page (assistant);
446 gint previous_page_index = ia->current_page;
448 if (previous_page_index >= 0)
450 GtkWidget *closing_page = gtk_assistant_get_nth_page (GTK_ASSISTANT (ia), previous_page_index);
452 if (pn > previous_page_index)
454 page_func *on_forward = g_object_get_data (G_OBJECT (closing_page), "on-forward");
457 on_forward (ia, closing_page);
461 page_func *on_back = g_object_get_data (G_OBJECT (closing_page), "on-back");
464 on_back (ia, closing_page);
469 GtkWidget *new_page = gtk_assistant_get_nth_page (GTK_ASSISTANT (ia), pn);
471 page_func *on_entering = g_object_get_data (G_OBJECT (new_page), "on-entering");
474 on_entering (ia, new_page);
477 ia->current_page = pn;
480 /* Called when the Cancel button in the assistant is clicked. */
482 on_cancel (GtkAssistant *assistant, PsppireImportAssistant *ia)
484 close_assistant (ia, GTK_RESPONSE_CANCEL);
487 /* Called when the Apply button on the last page of the assistant
490 on_close (GtkAssistant *assistant, PsppireImportAssistant *ia)
492 close_assistant (ia, GTK_RESPONSE_APPLY);
497 add_page_to_assistant (PsppireImportAssistant *ia,
498 GtkWidget *page, GtkAssistantPageType type, const gchar *);
502 on_sheet_combo_changed (GtkComboBox *cb, PsppireImportAssistant *ia)
506 GtkTreeModel *model = gtk_combo_box_get_model (cb);
507 GtkBuilder *builder = ia->builder;
508 GtkWidget *range_entry = get_widget_assert (builder, "cell-range-entry");
510 gtk_combo_box_get_active_iter (cb, &iter);
511 gtk_tree_model_get (model, &iter, PSPPIRE_SPREADSHEET_MODEL_COL_RANGE, &range, -1);
512 gtk_entry_set_text (GTK_ENTRY (range_entry), range ? range : "");
516 /* Prepares IA's sheet_spec page. */
518 prepare_sheet_spec_page (PsppireImportAssistant *ia)
520 GtkBuilder *builder = ia->builder;
521 GtkWidget *sheet_entry = get_widget_assert (builder, "sheet-entry");
522 GtkWidget *readnames_checkbox = get_widget_assert (builder, "readnames-checkbox");
524 gtk_combo_box_set_model (GTK_COMBO_BOX (sheet_entry),
525 psppire_spreadsheet_model_new (ia->spreadsheet));
527 gtk_combo_box_set_active (GTK_COMBO_BOX (sheet_entry), 0);
529 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (readnames_checkbox), FALSE);
533 /* Initializes IA's sheet_spec substructure. */
535 sheet_spec_page_create (PsppireImportAssistant *ia)
537 GtkBuilder *builder = ia->builder;
538 GtkWidget *page = get_widget_assert (builder, "Spreadsheet-Importer");
540 GtkWidget *combo_box = get_widget_assert (builder, "sheet-entry");
541 GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
542 gtk_cell_layout_clear (GTK_CELL_LAYOUT (combo_box));
543 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), renderer, TRUE);
544 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), renderer,
548 g_signal_connect (combo_box, "changed", G_CALLBACK (on_sheet_combo_changed), ia);
550 add_page_to_assistant (ia, page,
551 GTK_ASSISTANT_PAGE_CONTENT, _("Importing Spreadsheet Data"));
553 g_object_set_data (G_OBJECT (page), "on-entering", prepare_sheet_spec_page);
557 on_chosen (PsppireImportAssistant *ia, GtkWidget *page)
559 GtkFileChooser *fc = GTK_FILE_CHOOSER (page);
560 gchar *f = gtk_file_chooser_get_filename (fc);
563 for(i = gtk_assistant_get_n_pages (GTK_ASSISTANT (ia)); i > 0; --i)
564 gtk_assistant_remove_page (GTK_ASSISTANT (ia), i);
566 gtk_assistant_set_page_complete (GTK_ASSISTANT(ia), GTK_WIDGET (fc), FALSE);
568 if (f && g_file_test (f, G_FILE_TEST_IS_REGULAR))
570 gtk_assistant_set_page_complete (GTK_ASSISTANT(ia), GTK_WIDGET (fc), TRUE);
573 spreadsheet_unref (ia->spreadsheet);
575 ia->spreadsheet = gnumeric_probe (f, FALSE);
577 if (!ia->spreadsheet)
578 ia->spreadsheet = ods_probe (f, FALSE);
582 sheet_spec_page_create (ia);
586 intro_page_create (ia);
587 first_line_page_create (ia);
588 separators_page_create (ia);
591 formats_page_create (ia);
597 /* This has to be done on a map signal callback,
598 because GtkFileChooserWidget resets everything when it is mapped. */
600 on_map (PsppireImportAssistant *ia, GtkWidget *page)
603 GtkFileChooser *fc = GTK_FILE_CHOOSER (page);
606 gtk_file_chooser_set_filename (fc, ia->file_name);
609 on_chosen (ia, page);
615 chooser_page_enter (PsppireImportAssistant *ia, GtkWidget *page)
620 chooser_page_leave (PsppireImportAssistant *ia, GtkWidget *page)
622 g_free (ia->file_name);
623 ia->file_name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (page));
624 gchar *encoding = psppire_encoding_selector_get_encoding (ia->encoding_selector);
626 if (!ia->spreadsheet)
628 ia->text_file = psppire_text_file_new (ia->file_name, encoding);
629 gtk_tree_view_set_model (GTK_TREE_VIEW (ia->first_line_tree_view),
630 GTK_TREE_MODEL (ia->text_file));
638 chooser_page_reset (PsppireImportAssistant *ia, GtkWidget *page)
640 GtkFileChooser *fc = GTK_FILE_CHOOSER (page);
642 gtk_file_chooser_set_filter (fc, ia->default_filter);
643 gtk_file_chooser_unselect_all (fc);
645 on_chosen (ia, page);
651 chooser_page_create (PsppireImportAssistant *ia)
653 GtkFileFilter *filter = NULL;
655 GtkWidget *chooser = gtk_file_chooser_widget_new (GTK_FILE_CHOOSER_ACTION_OPEN);
657 g_object_set_data (G_OBJECT (chooser), "on-forward", chooser_page_leave);
658 g_object_set_data (G_OBJECT (chooser), "on-reset", chooser_page_reset);
659 g_object_set_data (G_OBJECT (chooser), "on-entering",chooser_page_enter);
661 g_object_set (chooser, "local-only", FALSE, NULL);
664 ia->default_filter = gtk_file_filter_new ();
665 gtk_file_filter_set_name (ia->default_filter, _("All Files"));
666 gtk_file_filter_add_pattern (ia->default_filter, "*");
667 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), ia->default_filter);
669 filter = gtk_file_filter_new ();
670 gtk_file_filter_set_name (filter, _("Text Files"));
671 gtk_file_filter_add_mime_type (filter, "text/*");
672 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
674 filter = gtk_file_filter_new ();
675 gtk_file_filter_set_name (filter, _("Text (*.txt) Files"));
676 gtk_file_filter_add_pattern (filter, "*.txt");
677 gtk_file_filter_add_pattern (filter, "*.TXT");
678 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
680 filter = gtk_file_filter_new ();
681 gtk_file_filter_set_name (filter, _("Plain Text (ASCII) Files"));
682 gtk_file_filter_add_mime_type (filter, "text/plain");
683 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
685 filter = gtk_file_filter_new ();
686 gtk_file_filter_set_name (filter, _("Comma Separated Value Files"));
687 gtk_file_filter_add_mime_type (filter, "text/csv");
688 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
690 /* I've never encountered one of these, but it's listed here:
691 http://www.iana.org/assignments/media-types/text/tab-separated-values */
692 filter = gtk_file_filter_new ();
693 gtk_file_filter_set_name (filter, _("Tab Separated Value Files"));
694 gtk_file_filter_add_mime_type (filter, "text/tab-separated-values");
695 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
697 filter = gtk_file_filter_new ();
698 gtk_file_filter_set_name (filter, _("Gnumeric Spreadsheet Files"));
699 gtk_file_filter_add_mime_type (filter, "application/x-gnumeric");
700 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
702 filter = gtk_file_filter_new ();
703 gtk_file_filter_set_name (filter, _("OpenDocument Spreadsheet Files"));
704 gtk_file_filter_add_mime_type (filter, "application/vnd.oasis.opendocument.spreadsheet");
705 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
707 filter = gtk_file_filter_new ();
708 gtk_file_filter_set_name (filter, _("All Spreadsheet Files"));
709 gtk_file_filter_add_mime_type (filter, "application/x-gnumeric");
710 gtk_file_filter_add_mime_type (filter, "application/vnd.oasis.opendocument.spreadsheet");
711 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
713 ia->encoding_selector = psppire_encoding_selector_new ("Auto", TRUE);
714 gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (chooser), ia->encoding_selector);
716 add_page_to_assistant (ia, chooser,
717 GTK_ASSISTANT_PAGE_INTRO, _("Select File to Import"));
719 g_signal_connect_swapped (chooser, "selection-changed", G_CALLBACK (on_chosen), ia);
720 g_signal_connect_swapped (chooser, "map", G_CALLBACK (on_map), ia);
726 psppire_import_assistant_init (PsppireImportAssistant *ia)
728 ia->builder = builder_new ("text-data-import.ui");
730 ia->current_page = -1 ;
731 ia->file_name = NULL;
733 ia->spreadsheet = NULL;
735 ia->casereader_dict = NULL;
737 ia->main_loop = g_main_loop_new (NULL, TRUE);
739 g_signal_connect (ia, "prepare", G_CALLBACK (on_prepare), ia);
740 g_signal_connect (ia, "cancel", G_CALLBACK (on_cancel), ia);
741 g_signal_connect (ia, "close", G_CALLBACK (on_close), ia);
743 ia->paste_button = gtk_button_new_with_label (_("Paste"));
744 ia->reset_button = gtk_button_new_with_label (_("Reset"));
746 gtk_assistant_add_action_widget (GTK_ASSISTANT(ia), ia->paste_button);
748 g_signal_connect (ia->paste_button, "clicked", G_CALLBACK (on_paste), ia);
749 g_signal_connect (ia->reset_button, "clicked", G_CALLBACK (on_reset), ia);
751 gtk_assistant_add_action_widget (GTK_ASSISTANT(ia), ia->reset_button);
753 gtk_window_set_title (GTK_WINDOW (ia),
754 _("Importing Delimited Text Data"));
756 gtk_window_set_icon_name (GTK_WINDOW (ia), "pspp");
758 chooser_page_create (ia);
760 gtk_assistant_set_forward_page_func (GTK_ASSISTANT (ia), next_page_func, NULL, NULL);
762 gtk_window_maximize (GTK_WINDOW (ia));
766 /* Appends a page of the given TYPE, with PAGE as its content, to
767 the GtkAssistant encapsulated by IA. Returns the GtkWidget
768 that represents the page. */
770 add_page_to_assistant (PsppireImportAssistant *ia,
771 GtkWidget *page, GtkAssistantPageType type, const gchar *title)
773 GtkWidget *content = page;
775 gtk_assistant_append_page (GTK_ASSISTANT (ia), content);
776 gtk_assistant_set_page_type (GTK_ASSISTANT(ia), content, type);
777 gtk_assistant_set_page_title (GTK_ASSISTANT(ia), content, title);
778 gtk_assistant_set_page_complete (GTK_ASSISTANT(ia), content, TRUE);
784 /* Called when one of the radio buttons is clicked. */
786 on_intro_amount_changed (PsppireImportAssistant *p)
788 gtk_widget_set_sensitive (p->n_cases_spin,
789 gtk_toggle_button_get_active
790 (GTK_TOGGLE_BUTTON (p->n_cases_button)));
792 gtk_widget_set_sensitive (p->percent_spin,
793 gtk_toggle_button_get_active
794 (GTK_TOGGLE_BUTTON (p->percent_button)));
798 on_treeview_selection_change (PsppireImportAssistant *ia)
800 GtkTreeSelection *selection =
801 gtk_tree_view_get_selection (GTK_TREE_VIEW (ia->first_line_tree_view));
802 GtkTreeModel *model = NULL;
804 if (gtk_tree_selection_get_selected (selection, &model, &iter))
808 GtkTreePath *path = gtk_tree_model_get_path (model, &iter);
809 gint *index = gtk_tree_path_get_indices (path);
811 gtk_tree_path_free (path);
812 g_object_get (model, "maximum-lines", &max_lines, NULL);
813 gtk_widget_set_sensitive (ia->variable_names_cb,
814 (n > 0 && n < max_lines));
815 ia->delimiters_model =
816 psppire_delimited_text_new (GTK_TREE_MODEL (ia->text_file));
817 g_object_set (ia->delimiters_model, "first-line", n, NULL);
822 render_text_preview_line (GtkTreeViewColumn *tree_column,
823 GtkCellRenderer *cell,
824 GtkTreeModel *tree_model,
829 Set the text to a "insensitive" state if the row
830 is greater than what the user declared to be the maximum.
832 GtkTreePath *path = gtk_tree_model_get_path (tree_model, iter);
833 gint *ii = gtk_tree_path_get_indices (path);
835 g_object_get (tree_model, "maximum-lines", &max_lines, NULL);
836 g_object_set (cell, "sensitive", (*ii < max_lines), NULL);
837 gtk_tree_path_free (path);
840 /* Initializes IA's first_line substructure. */
842 first_line_page_create (PsppireImportAssistant *ia)
844 GtkWidget *w = get_widget_assert (ia->builder, "FirstLine");
846 g_object_set_data (G_OBJECT (w), "on-entering", on_treeview_selection_change);
848 add_page_to_assistant (ia, w,
849 GTK_ASSISTANT_PAGE_CONTENT, _("Select the First Line"));
851 GtkWidget *scrolled_window = get_widget_assert (ia->builder, "first-line-scroller");
853 if (ia->first_line_tree_view == NULL)
855 ia->first_line_tree_view = gtk_tree_view_new ();
856 g_object_set (ia->first_line_tree_view, "enable-search", FALSE, NULL);
858 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (ia->first_line_tree_view), TRUE);
860 GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
861 GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes (_("Line"), renderer,
865 gtk_tree_view_column_set_cell_data_func (column, renderer, render_text_preview_line, ia, 0);
866 gtk_tree_view_append_column (GTK_TREE_VIEW (ia->first_line_tree_view), column);
868 renderer = gtk_cell_renderer_text_new ();
869 column = gtk_tree_view_column_new_with_attributes (_("Text"), renderer, "text", 1, NULL);
870 gtk_tree_view_column_set_cell_data_func (column, renderer, render_text_preview_line, ia, 0);
872 gtk_tree_view_append_column (GTK_TREE_VIEW (ia->first_line_tree_view), column);
874 g_signal_connect_swapped (ia->first_line_tree_view, "cursor-changed",
875 G_CALLBACK (on_treeview_selection_change), ia);
876 gtk_container_add (GTK_CONTAINER (scrolled_window), ia->first_line_tree_view);
879 gtk_widget_show_all (scrolled_window);
881 ia->variable_names_cb = get_widget_assert (ia->builder, "variable-names");
885 intro_on_leave (PsppireImportAssistant *ia)
888 g_object_get (ia->text_file, "line-count", &lc, NULL);
889 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->n_cases_button)))
891 gint max_lines = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (ia->n_cases_spin));
892 g_object_set (ia->text_file, "maximum-lines", max_lines, NULL);
894 else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->percent_button)))
896 gdouble percent = gtk_spin_button_get_value (GTK_SPIN_BUTTON (ia->percent_spin));
897 g_object_set (ia->text_file, "maximum-lines", (gint) (lc * percent / 100.0), NULL);
901 g_object_set (ia->text_file, "maximum-lines", lc, NULL);
907 intro_on_enter (PsppireImportAssistant *ia)
909 GtkBuilder *builder = ia->builder;
910 GtkWidget *table = get_widget_assert (builder, "button-table");
915 ds_put_cstr (&s, _("This assistant will guide you through the process of "
916 "importing data into PSPP from a text file with one line "
917 "per case, in which fields are separated by tabs, "
918 "commas, or other delimiters.\n\n"));
922 if (ia->text_file->total_is_exact)
925 &s, ngettext ("The selected file contains %'lu line of text. ",
926 "The selected file contains %'lu lines of text. ",
927 ia->text_file->total_lines),
928 ia->text_file->total_lines);
930 else if (ia->text_file->total_lines > 0)
934 "The selected file contains approximately %'lu line of text. ",
935 "The selected file contains approximately %'lu lines of text. ",
936 ia->text_file->total_lines),
937 ia->text_file->total_lines);
940 "Only the first %zu line of the file will be shown for "
941 "preview purposes in the following screens. ",
942 "Only the first %zu lines of the file will be shown for "
943 "preview purposes in the following screens. ",
944 ia->text_file->line_cnt),
945 ia->text_file->line_cnt);
949 ds_put_cstr (&s, _("You may choose below how much of the file should "
950 "actually be imported."));
952 gtk_label_set_text (GTK_LABEL (get_widget_assert (builder, "intro-label")),
956 if (gtk_grid_get_child_at (GTK_GRID (table), 1, 1) == NULL)
958 GtkWidget *hbox_n_cases = psppire_scanf_new (_("Only the first %4d cases"), &ia->n_cases_spin);
959 gtk_grid_attach (GTK_GRID (table), hbox_n_cases,
964 GtkAdjustment *adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (ia->n_cases_spin));
965 gtk_adjustment_set_lower (adj, 1.0);
967 if (gtk_grid_get_child_at (GTK_GRID (table), 1, 2) == NULL)
969 GtkWidget *hbox_percent = psppire_scanf_new (_("Only the first %3d %% of file (approximately)"),
972 gtk_grid_attach (GTK_GRID (table), hbox_percent,
977 gtk_widget_show_all (table);
979 on_intro_amount_changed (ia);
982 /* Initializes IA's intro substructure. */
984 intro_page_create (PsppireImportAssistant *ia)
986 GtkBuilder *builder = ia->builder;
988 GtkWidget *w = get_widget_assert (builder, "Intro");
990 ia->percent_spin = gtk_spin_button_new_with_range (0, 100, 10);
993 add_page_to_assistant (ia, w, GTK_ASSISTANT_PAGE_CONTENT, _("Select the Lines to Import"));
995 ia->all_cases_button = get_widget_assert (builder, "import-all-cases");
997 ia->n_cases_button = get_widget_assert (builder, "import-n-cases");
999 ia->percent_button = get_widget_assert (builder, "import-percent");
1001 g_signal_connect_swapped (ia->all_cases_button, "toggled",
1002 G_CALLBACK (on_intro_amount_changed), ia);
1003 g_signal_connect_swapped (ia->n_cases_button, "toggled",
1004 G_CALLBACK (on_intro_amount_changed), ia);
1005 g_signal_connect_swapped (ia->percent_button, "toggled",
1006 G_CALLBACK (on_intro_amount_changed), ia);
1009 g_object_set_data (G_OBJECT (w), "on-forward", intro_on_leave);
1010 g_object_set_data (G_OBJECT (w), "on-entering", intro_on_enter);
1011 g_object_set_data (G_OBJECT (w), "on-reset", reset_intro_page);
1016 psppire_import_assistant_new (GtkWindow *toplevel)
1018 return GTK_WIDGET (g_object_new (PSPPIRE_TYPE_IMPORT_ASSISTANT,
1019 /* Some window managers (notably ratpoison)
1020 ignore the maximise command when a window is
1021 transient. This causes problems for this
1023 /* "transient-for", toplevel, */
1031 /* Chooses a name for each column on the separators page */
1033 choose_column_names (PsppireImportAssistant *ia)
1036 unsigned long int generated_name_count = 0;
1037 dict_clear (ia->dict);
1040 i < gtk_tree_model_get_n_columns (GTK_TREE_MODEL (ia->delimiters_model)) - 1;
1043 const gchar *candidate_name = NULL;
1045 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->variable_names_cb)))
1047 candidate_name = psppire_delimited_text_get_header_title (PSPPIRE_DELIMITED_TEXT (ia->delimiters_model), i);
1050 char *name = dict_make_unique_var_name (ia->dict,
1052 &generated_name_count);
1054 dict_create_var_assert (ia->dict, name, 0);
1059 /* Called when the user toggles one of the separators
1062 on_separator_toggle (GtkToggleButton *toggle UNUSED,
1063 PsppireImportAssistant *ia)
1066 GSList *delimiters = NULL;
1067 for (i = 0; i < SEPARATOR_CNT; i++)
1069 const struct separator *s = &separators[i];
1070 GtkWidget *button = get_widget_assert (ia->builder, s->name);
1071 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
1073 delimiters = g_slist_prepend (delimiters, GINT_TO_POINTER (s->c));
1077 g_object_set (ia->delimiters_model, "delimiters", delimiters, NULL);
1079 revise_fields_preview (ia);
1083 /* Called when the user changes the entry field for custom
1086 on_separators_custom_entry_notify (GObject *gobject UNUSED,
1087 GParamSpec *arg1 UNUSED,
1088 PsppireImportAssistant *ia)
1090 revise_fields_preview (ia);
1093 /* Called when the user toggles the checkbox that enables custom
1096 on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
1097 PsppireImportAssistant *ia)
1099 bool is_active = gtk_toggle_button_get_active (custom_cb);
1100 gtk_widget_set_sensitive (ia->custom_entry, is_active);
1101 revise_fields_preview (ia);
1104 /* Called when the user changes the selection in the combo box
1105 that selects a quote character. */
1107 on_quote_combo_change (GtkComboBox *combo, PsppireImportAssistant *ia)
1109 // revise_fields_preview (ia);
1112 /* Called when the user toggles the checkbox that enables
1115 on_quote_cb_toggle (GtkToggleButton *quote_cb, PsppireImportAssistant *ia)
1117 bool is_active = gtk_toggle_button_get_active (quote_cb);
1118 gtk_widget_set_sensitive (ia->quote_combo, is_active);
1119 revise_fields_preview (ia);
1122 /* Initializes IA's separators substructure. */
1124 separators_page_create (PsppireImportAssistant *ia)
1126 GtkBuilder *builder = ia->builder;
1130 GtkWidget *w = get_widget_assert (builder, "Separators");
1132 g_object_set_data (G_OBJECT (w), "on-entering", prepare_separators_page);
1133 g_object_set_data (G_OBJECT (w), "on-reset", prepare_separators_page);
1135 add_page_to_assistant (ia, w, GTK_ASSISTANT_PAGE_CONTENT, _("Choose Separators"));
1137 ia->custom_cb = get_widget_assert (builder, "custom-cb");
1138 ia->custom_entry = get_widget_assert (builder, "custom-entry");
1139 ia->quote_combo = get_widget_assert (builder, "quote-combo");
1140 ia->quote_cb = get_widget_assert (builder, "quote-cb");
1142 gtk_combo_box_set_active (GTK_COMBO_BOX (ia->quote_combo), 0);
1144 if (ia->fields_tree_view == NULL)
1146 GtkWidget *scroller = get_widget_assert (ia->builder, "fields-scroller");
1147 ia->fields_tree_view = gtk_tree_view_new ();
1148 g_object_set (ia->fields_tree_view, "enable-search", FALSE, NULL);
1149 gtk_container_add (GTK_CONTAINER (scroller), GTK_WIDGET (ia->fields_tree_view));
1150 gtk_widget_show_all (scroller);
1153 g_signal_connect (ia->quote_combo, "changed",
1154 G_CALLBACK (on_quote_combo_change), ia);
1155 g_signal_connect (ia->quote_cb, "toggled",
1156 G_CALLBACK (on_quote_cb_toggle), ia);
1157 g_signal_connect (ia->custom_entry, "notify::text",
1158 G_CALLBACK (on_separators_custom_entry_notify), ia);
1159 g_signal_connect (ia->custom_cb, "toggled",
1160 G_CALLBACK (on_separators_custom_cb_toggle), ia);
1161 for (i = 0; i < SEPARATOR_CNT; i++)
1162 g_signal_connect (get_widget_assert (builder, separators[i].name),
1163 "toggled", G_CALLBACK (on_separator_toggle), ia);
1172 static struct casereader_random_class my_casereader_class;
1174 static struct ccase *
1175 my_read (struct casereader *reader, void *aux, casenumber idx)
1177 PsppireImportAssistant *ia = PSPPIRE_IMPORT_ASSISTANT (aux);
1178 GtkTreeModel *tm = GTK_TREE_MODEL (ia->delimiters_model);
1180 GtkTreePath *tp = gtk_tree_path_new_from_indices (idx, -1);
1182 const struct caseproto *proto = casereader_get_proto (reader);
1185 struct ccase *c = NULL;
1186 if (gtk_tree_model_get_iter (tm, &iter, tp))
1188 c = case_create (proto);
1190 for (i = 0 ; i < caseproto_get_n_widths (proto); ++i)
1193 gtk_tree_model_get_value (tm, &iter, i + 1, &value);
1195 const struct variable *var = dict_get_var (ia->casereader_dict, i);
1197 const gchar *ss = g_value_get_string (&value);
1200 union value *v = case_data_rw (c, var);
1201 /* In this reader we derive the union value from the
1202 string in the tree_model. We retrieve the width and format
1203 from a dictionary which is stored directly after
1204 the reader creation. Changes in ia->dict in the
1205 variable window are not reflected here and therefore
1206 this is always compatible with the width in the
1207 caseproto. See bug #58298 */
1208 char *xx = data_in (ss_cstr (ss),
1210 var_get_write_format (var)->type,
1211 v, var_get_width (var), "UTF-8");
1214 /* g_print ("%s:%d Err %s\n", __FILE__, __LINE__, xx); */
1217 g_value_unset (&value);
1221 gtk_tree_path_free (tp);
1227 my_destroy (struct casereader *reader, void *aux)
1229 g_print ("%s:%d %p\n", __FILE__, __LINE__, reader);
1233 my_advance (struct casereader *reader, void *aux, casenumber cnt)
1235 g_print ("%s:%d\n", __FILE__, __LINE__);
1238 static struct casereader *
1239 textfile_create_reader (PsppireImportAssistant *ia)
1241 int n_vars = dict_get_var_cnt (ia->dict);
1245 struct fmt_guesser **fg = XCALLOC (n_vars, struct fmt_guesser *);
1246 for (i = 0 ; i < n_vars; ++i)
1248 fg[i] = fmt_guesser_create ();
1251 gint n_rows = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (ia->delimiters_model), NULL);
1255 for (ok = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (ia->delimiters_model), &iter);
1257 ok = gtk_tree_model_iter_next (GTK_TREE_MODEL (ia->delimiters_model), &iter))
1259 for (i = 0 ; i < n_vars; ++i)
1262 gtk_tree_model_get (GTK_TREE_MODEL (ia->delimiters_model), &iter, i+1, &s, -1);
1264 fmt_guesser_add (fg[i], ss_cstr (s));
1269 struct caseproto *proto = caseproto_create ();
1270 for (i = 0 ; i < n_vars; ++i)
1273 fmt_guesser_guess (fg[i], &fs);
1275 fmt_fix (&fs, FMT_FOR_INPUT);
1277 struct variable *var = dict_get_var (ia->dict, i);
1279 int width = fmt_var_width (&fs);
1281 var_set_width_and_formats (var, width,
1284 proto = caseproto_add_width (proto, width);
1285 fmt_guesser_destroy (fg[i]);
1290 struct casereader *cr = casereader_create_random (proto, n_rows, &my_casereader_class, ia);
1291 /* Store the dictionary at this point when the casereader is created.
1292 my_read depends on the dictionary to interpret the strings in the treeview.
1293 This guarantees that the union value is produced according to the
1294 caseproto in the reader. */
1295 ia->casereader_dict = dict_clone (ia->dict);
1296 caseproto_unref (proto);
1300 /* When during import the variable type is changed, the reader is reinitialized
1301 based on the new dictionary with a fresh caseprototype. The default behaviour
1302 when a variable type is changed and the column is resized is that the union
1303 value is interpreted with new variable type and an overlay for that column
1304 is generated. Here we reinit to the original reader based on strings.
1305 As a result you can switch from string to numeric to string without loosing
1306 the string information. */
1308 ia_variable_changed_cb (GObject *obj, gint var_num, guint what,
1309 const struct variable *oldvar, gpointer data)
1311 PsppireImportAssistant *ia = PSPPIRE_IMPORT_ASSISTANT (data);
1313 struct caseproto *proto = caseproto_create();
1314 for (int i = 0; i < dict_get_var_cnt (ia->dict); i++)
1316 const struct variable *var = dict_get_var (ia->dict, i);
1317 int width = var_get_width (var);
1318 proto = caseproto_add_width (proto, width);
1321 gint n_rows = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (ia->delimiters_model), NULL);
1323 PsppireDataStore *store = NULL;
1324 g_object_get (ia->data_sheet, "data-model", &store, NULL);
1326 struct casereader *cr = casereader_create_random (proto, n_rows,
1327 &my_casereader_class, ia);
1328 psppire_data_store_set_reader (store, cr);
1329 dict_unref (ia->casereader_dict);
1330 ia->casereader_dict = dict_clone (ia->dict);
1333 /* Called just before the formats page of the assistant is
1336 prepare_formats_page (PsppireImportAssistant *ia)
1338 my_casereader_class.read = my_read;
1339 my_casereader_class.destroy = my_destroy;
1340 my_casereader_class.advance = my_advance;
1342 if (ia->spreadsheet)
1344 GtkBuilder *builder = ia->builder;
1345 GtkWidget *range_entry = get_widget_assert (builder, "cell-range-entry");
1346 GtkWidget *rnc = get_widget_assert (builder, "readnames-checkbox");
1347 GtkWidget *combo_box = get_widget_assert (builder, "sheet-entry");
1349 struct spreadsheet_read_options opts;
1350 opts.sheet_name = NULL;
1351 opts.sheet_index = gtk_combo_box_get_active (GTK_COMBO_BOX (combo_box)) + 1;
1352 opts.read_names = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rnc));
1353 opts.cell_range = g_strdup (gtk_entry_get_text (GTK_ENTRY (range_entry)));
1356 struct casereader *reader = spreadsheet_make_reader (ia->spreadsheet, &opts);
1358 PsppireDict *dict = psppire_dict_new_from_dict (ia->spreadsheet->dict);
1359 PsppireDataStore *store = psppire_data_store_new (dict);
1360 psppire_data_store_set_reader (store, reader);
1361 g_object_set (ia->data_sheet, "data-model", store, NULL);
1362 g_object_set (ia->var_sheet, "data-model", dict, NULL);
1366 struct casereader *reader = textfile_create_reader (ia);
1368 PsppireDict *dict = psppire_dict_new_from_dict (ia->dict);
1369 PsppireDataStore *store = psppire_data_store_new (dict);
1370 psppire_data_store_set_reader (store, reader);
1371 g_signal_connect (dict, "variable-changed",
1372 G_CALLBACK (ia_variable_changed_cb),
1375 g_object_set (ia->data_sheet, "data-model", store, NULL);
1376 g_object_set (ia->var_sheet, "data-model", dict, NULL);
1380 g_object_get (get_widget_assert (ia->builder, "vpaned1"),
1381 "max-position", &pmax, NULL);
1384 g_object_set (get_widget_assert (ia->builder, "vpaned1"),
1385 "position", pmax / 2, NULL);
1387 gtk_widget_show (ia->paste_button);
1391 formats_page_create (PsppireImportAssistant *ia)
1393 GtkBuilder *builder = ia->builder;
1395 GtkWidget *w = get_widget_assert (builder, "Formats");
1396 g_object_set_data (G_OBJECT (w), "on-entering", prepare_formats_page);
1397 g_object_set_data (G_OBJECT (w), "on-reset", reset_formats_page);
1399 GtkWidget *vars_scroller = get_widget_assert (builder, "vars-scroller");
1400 if (ia->var_sheet == NULL)
1402 ia->var_sheet = psppire_variable_sheet_new ();
1404 gtk_container_add (GTK_CONTAINER (vars_scroller), ia->var_sheet);
1406 ia->dict = dict_create (get_default_encoding ());
1408 gtk_widget_show_all (vars_scroller);
1410 GtkWidget *data_scroller = get_widget_assert (builder, "data-scroller");
1411 if (ia->data_sheet == NULL)
1413 ia->data_sheet = psppire_data_sheet_new ();
1414 g_object_set (ia->data_sheet, "editable", FALSE, NULL);
1416 gtk_container_add (GTK_CONTAINER (data_scroller), ia->data_sheet);
1418 gtk_widget_show_all (data_scroller);
1421 add_page_to_assistant (ia, w,
1422 GTK_ASSISTANT_PAGE_CONFIRM, _("Adjust Variable Formats"));
1429 separators_append_syntax (const PsppireImportAssistant *ia, struct string *s)
1433 ds_put_cstr (s, " /DELIMITERS=\"");
1435 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (get_widget_assert (ia->builder, "tab"))))
1436 ds_put_cstr (s, "\\t");
1437 for (i = 0; i < SEPARATOR_CNT; i++)
1439 const struct separator *seps = &separators[i];
1440 GtkWidget *button = get_widget_assert (ia->builder, seps->name);
1441 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
1443 if (seps->c == '\t')
1446 ds_put_byte (s, seps->c);
1449 ds_put_cstr (s, "\"\n");
1451 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->quote_cb)))
1453 GtkComboBoxText *cbt = GTK_COMBO_BOX_TEXT (ia->quote_combo);
1454 gchar *quotes = gtk_combo_box_text_get_active_text (cbt);
1455 if (quotes && *quotes)
1456 syntax_gen_pspp (s, " /QUALIFIER=%sq\n", quotes);
1462 formats_append_syntax (const PsppireImportAssistant *ia, struct string *s)
1467 g_return_if_fail (ia->dict);
1469 ds_put_cstr (s, " /VARIABLES=\n");
1471 var_cnt = dict_get_var_cnt (ia->dict);
1472 for (i = 0; i < var_cnt; i++)
1474 struct variable *var = dict_get_var (ia->dict, i);
1475 char format_string[FMT_STRING_LEN_MAX + 1];
1476 fmt_to_string (var_get_print_format (var), format_string);
1477 ds_put_format (s, " %s %s%s\n",
1478 var_get_name (var), format_string,
1479 i == var_cnt - 1 ? "." : "");
1484 first_line_append_syntax (const PsppireImportAssistant *ia, struct string *s)
1486 gint first_case = 0;
1487 g_object_get (ia->delimiters_model, "first-line", &first_case, NULL);
1490 ds_put_format (s, " /FIRSTCASE=%d\n", first_case + 1);
1494 intro_append_syntax (const PsppireImportAssistant *ia, struct string *s)
1496 gint first_line = 0;
1497 g_object_get (ia->delimiters_model, "first-line", &first_line, NULL);
1499 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->n_cases_button)))
1500 ds_put_format (s, "SELECT IF ($CASENUM <= %d).\n",
1501 gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (ia->n_cases_spin)) - first_line);
1502 else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->percent_button)))
1503 ds_put_format (s, "SAMPLE %.4g.\n",
1504 gtk_spin_button_get_value (GTK_SPIN_BUTTON (ia->percent_spin)) / 100.0);
1508 /* Emits PSPP syntax to S that applies the dictionary attributes
1509 (such as missing values and value labels) of the variables in
1512 apply_dict (const struct dictionary *dict, struct string *s)
1514 size_t var_cnt = dict_get_var_cnt (dict);
1517 for (i = 0; i < var_cnt; i++)
1519 struct variable *var = dict_get_var (dict, i);
1520 const char *name = var_get_name (var);
1521 enum val_type type = var_get_type (var);
1522 int width = var_get_width (var);
1523 enum measure measure = var_get_measure (var);
1524 enum var_role role = var_get_role (var);
1525 enum alignment alignment = var_get_alignment (var);
1526 const struct fmt_spec *format = var_get_print_format (var);
1528 if (var_has_missing_values (var))
1530 const struct missing_values *mv = var_get_missing_values (var);
1533 syntax_gen_pspp (s, "MISSING VALUES %ss (", name);
1534 for (j = 0; j < mv_n_values (mv); j++)
1537 ds_put_cstr (s, ", ");
1538 syntax_gen_value (s, mv_get_value (mv, j), width, format);
1541 if (mv_has_range (mv))
1544 if (mv_has_value (mv))
1545 ds_put_cstr (s, ", ");
1546 mv_get_range (mv, &low, &high);
1547 syntax_gen_num_range (s, low, high, format);
1549 ds_put_cstr (s, ").\n");
1551 if (var_has_value_labels (var))
1553 const struct val_labs *vls = var_get_value_labels (var);
1554 const struct val_lab **labels = val_labs_sorted (vls);
1555 size_t n_labels = val_labs_count (vls);
1558 syntax_gen_pspp (s, "VALUE LABELS %ss", name);
1559 for (i = 0; i < n_labels; i++)
1561 const struct val_lab *vl = labels[i];
1562 ds_put_cstr (s, "\n ");
1563 syntax_gen_value (s, &vl->value, width, format);
1564 ds_put_byte (s, ' ');
1565 syntax_gen_string (s, ss_cstr (val_lab_get_escaped_label (vl)));
1568 ds_put_cstr (s, ".\n");
1570 if (var_has_label (var))
1571 syntax_gen_pspp (s, "VARIABLE LABELS %ss %sq.\n",
1572 name, var_get_label (var));
1573 if (measure != var_default_measure (type))
1574 syntax_gen_pspp (s, "VARIABLE LEVEL %ss (%ss).\n",
1575 name, measure_to_syntax (measure));
1576 if (role != ROLE_INPUT)
1577 syntax_gen_pspp (s, "VARIABLE ROLE /%ss %ss.\n",
1578 var_role_to_syntax (role), name);
1579 if (alignment != var_default_alignment (type))
1580 syntax_gen_pspp (s, "VARIABLE ALIGNMENT %ss (%ss).\n",
1581 name, alignment_to_syntax (alignment));
1582 if (var_get_display_width (var) != var_default_display_width (width))
1583 syntax_gen_pspp (s, "VARIABLE WIDTH %ss (%d).\n",
1584 name, var_get_display_width (var));
1591 sheet_spec_gen_syntax (PsppireImportAssistant *ia, struct string *s)
1593 GtkBuilder *builder = ia->builder;
1594 GtkWidget *range_entry = get_widget_assert (builder, "cell-range-entry");
1595 GtkWidget *sheet_entry = get_widget_assert (builder, "sheet-entry");
1596 GtkWidget *rnc = get_widget_assert (builder, "readnames-checkbox");
1597 const gchar *range = gtk_entry_get_text (GTK_ENTRY (range_entry));
1598 int sheet_index = 1 + gtk_combo_box_get_active (GTK_COMBO_BOX (sheet_entry));
1599 gboolean read_names = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rnc));
1603 if (ia->spreadsheet)
1604 filename = ia->spreadsheet->file_name;
1606 g_object_get (ia->text_file, "file-name", &filename, NULL);
1611 "\n /SHEET=index %d"
1612 "\n /READNAMES=%ss",
1613 (ia->spreadsheet->type == SPREADSHEET_GNUMERIC) ? "GNM" : "ODS",
1616 read_names ? "ON" : "OFF");
1618 if (range && 0 != strcmp ("", range))
1621 "\n /CELLRANGE=RANGE %sq", range);
1626 "\n /CELLRANGE=FULL");
1630 syntax_gen_pspp (s, ".\n");
1635 psppire_import_assistant_generate_syntax (PsppireImportAssistant *ia)
1637 struct string s = DS_EMPTY_INITIALIZER;
1639 if (!ia->spreadsheet)
1641 gchar *file_name = NULL;
1642 gchar *encoding = NULL;
1643 g_object_get (ia->text_file,
1644 "file-name", &file_name,
1645 "encoding", &encoding,
1648 if (file_name == NULL)
1651 syntax_gen_pspp (&s,
1656 if (encoding && strcmp (encoding, "Auto"))
1657 syntax_gen_pspp (&s, " /ENCODING=%sq\n", encoding);
1660 " /ARRANGEMENT=DELIMITED\n"
1661 " /DELCASE=LINE\n");
1663 first_line_append_syntax (ia, &s);
1664 separators_append_syntax (ia, &s);
1666 formats_append_syntax (ia, &s);
1667 apply_dict (ia->dict, &s);
1668 intro_append_syntax (ia, &s);
1672 sheet_spec_gen_syntax (ia, &s);
1675 return ds_cstr (&s);
1680 psppire_import_assistant_run (PsppireImportAssistant *asst)
1682 g_main_loop_run (asst->main_loop);
1683 return asst->response;