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 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (get_widget_assert (ia->builder, separators[most_frequent].name)), TRUE);
308 repopulate_delimiter_columns (PsppireImportAssistant *ia)
310 /* Remove all the columns */
311 while (gtk_tree_view_get_n_columns (GTK_TREE_VIEW (ia->fields_tree_view)) > 0)
313 GtkTreeViewColumn *tvc = gtk_tree_view_get_column (GTK_TREE_VIEW (ia->fields_tree_view), 0);
314 gtk_tree_view_remove_column (GTK_TREE_VIEW (ia->fields_tree_view), tvc);
318 gtk_tree_model_get_n_columns (GTK_TREE_MODEL (ia->delimiters_model));
320 /* ... and put them back again. */
322 for (f = gtk_tree_view_get_n_columns (GTK_TREE_VIEW (ia->fields_tree_view));
325 GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
327 const gchar *title = NULL;
333 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->variable_names_cb)))
336 psppire_delimited_text_get_header_title
337 (PSPPIRE_DELIMITED_TEXT (ia->delimiters_model), f - 1);
343 GtkTreeViewColumn *column =
344 gtk_tree_view_column_new_with_attributes (title,
348 g_object_set (column,
350 "sizing", GTK_TREE_VIEW_COLUMN_AUTOSIZE,
353 gtk_tree_view_append_column (GTK_TREE_VIEW (ia->fields_tree_view), column);
358 reset_tree_view_model (PsppireImportAssistant *ia)
360 GtkTreeModel *tm = gtk_tree_view_get_model (GTK_TREE_VIEW (ia->fields_tree_view));
362 gtk_tree_view_set_model (GTK_TREE_VIEW (ia->fields_tree_view), NULL);
365 repopulate_delimiter_columns (ia);
367 gtk_tree_view_set_model (GTK_TREE_VIEW (ia->fields_tree_view), tm);
368 // gtk_tree_view_columns_autosize (GTK_TREE_VIEW (ia->fields_tree_view));
373 /* Called just before the separators page becomes visible in the
374 assistant, and when the Reset button is clicked. */
376 prepare_separators_page (PsppireImportAssistant *ia, GtkWidget *page)
378 gtk_tree_view_set_model (GTK_TREE_VIEW (ia->fields_tree_view),
379 GTK_TREE_MODEL (ia->delimiters_model));
381 g_signal_connect_swapped (GTK_TREE_MODEL (ia->delimiters_model), "notify::delimiters",
382 G_CALLBACK (reset_tree_view_model), ia);
385 repopulate_delimiter_columns (ia);
387 revise_fields_preview (ia);
388 choose_likely_separators (ia);
391 /* Resets IA's intro page to its initial state. */
393 reset_intro_page (PsppireImportAssistant *ia)
395 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->all_cases_button),
401 /* Clears the set of user-modified variables from IA's formats
402 substructure. This discards user modifications to variable
403 formats, thereby causing formats to revert to their
406 reset_formats_page (PsppireImportAssistant *ia, GtkWidget *page)
410 static void prepare_formats_page (PsppireImportAssistant *ia);
412 /* Called when the Reset button is clicked. */
414 on_reset (GtkButton *button, PsppireImportAssistant *ia)
416 gint pn = gtk_assistant_get_current_page (GTK_ASSISTANT (ia));
418 GtkWidget *page = gtk_assistant_get_nth_page (GTK_ASSISTANT (ia), pn);
420 page_func *on_reset = g_object_get_data (G_OBJECT (page), "on-reset");
429 next_page_func (gint old_page, gpointer data)
435 /* Called just before PAGE is displayed as the current page of
436 IMPORT_ASSISTANT, this updates IA content according to the new
439 on_prepare (GtkAssistant *assistant, GtkWidget *page, PsppireImportAssistant *ia)
441 gtk_widget_show (ia->reset_button);
442 gtk_widget_hide (ia->paste_button);
444 gint pn = gtk_assistant_get_current_page (assistant);
445 gint previous_page_index = ia->current_page;
447 if (previous_page_index >= 0)
449 GtkWidget *closing_page = gtk_assistant_get_nth_page (GTK_ASSISTANT (ia), previous_page_index);
451 if (pn > previous_page_index)
453 page_func *on_forward = g_object_get_data (G_OBJECT (closing_page), "on-forward");
456 on_forward (ia, closing_page);
460 page_func *on_back = g_object_get_data (G_OBJECT (closing_page), "on-back");
463 on_back (ia, closing_page);
468 GtkWidget *new_page = gtk_assistant_get_nth_page (GTK_ASSISTANT (ia), pn);
470 page_func *on_entering = g_object_get_data (G_OBJECT (new_page), "on-entering");
473 on_entering (ia, new_page);
476 ia->current_page = pn;
479 /* Called when the Cancel button in the assistant is clicked. */
481 on_cancel (GtkAssistant *assistant, PsppireImportAssistant *ia)
483 close_assistant (ia, GTK_RESPONSE_CANCEL);
486 /* Called when the Apply button on the last page of the assistant
489 on_close (GtkAssistant *assistant, PsppireImportAssistant *ia)
491 close_assistant (ia, GTK_RESPONSE_APPLY);
496 add_page_to_assistant (PsppireImportAssistant *ia,
497 GtkWidget *page, GtkAssistantPageType type, const gchar *);
501 on_sheet_combo_changed (GtkComboBox *cb, PsppireImportAssistant *ia)
505 GtkTreeModel *model = gtk_combo_box_get_model (cb);
506 GtkBuilder *builder = ia->builder;
507 GtkWidget *range_entry = get_widget_assert (builder, "cell-range-entry");
509 gtk_combo_box_get_active_iter (cb, &iter);
510 gtk_tree_model_get (model, &iter, PSPPIRE_SPREADSHEET_MODEL_COL_RANGE, &range, -1);
511 gtk_entry_set_text (GTK_ENTRY (range_entry), range ? range : "");
515 /* Prepares IA's sheet_spec page. */
517 prepare_sheet_spec_page (PsppireImportAssistant *ia)
519 GtkBuilder *builder = ia->builder;
520 GtkWidget *sheet_entry = get_widget_assert (builder, "sheet-entry");
521 GtkWidget *readnames_checkbox = get_widget_assert (builder, "readnames-checkbox");
523 gtk_combo_box_set_model (GTK_COMBO_BOX (sheet_entry),
524 psppire_spreadsheet_model_new (ia->spreadsheet));
526 gtk_combo_box_set_active (GTK_COMBO_BOX (sheet_entry), 0);
528 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (readnames_checkbox), FALSE);
532 /* Initializes IA's sheet_spec substructure. */
534 sheet_spec_page_create (PsppireImportAssistant *ia)
536 GtkBuilder *builder = ia->builder;
537 GtkWidget *page = get_widget_assert (builder, "Spreadsheet-Importer");
539 GtkWidget *combo_box = get_widget_assert (builder, "sheet-entry");
540 GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
541 gtk_cell_layout_clear (GTK_CELL_LAYOUT (combo_box));
542 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), renderer, TRUE);
543 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), renderer,
547 g_signal_connect (combo_box, "changed", G_CALLBACK (on_sheet_combo_changed), ia);
549 add_page_to_assistant (ia, page,
550 GTK_ASSISTANT_PAGE_CONTENT, _("Importing Spreadsheet Data"));
552 g_object_set_data (G_OBJECT (page), "on-entering", prepare_sheet_spec_page);
556 on_chosen (PsppireImportAssistant *ia, GtkWidget *page)
558 GtkFileChooser *fc = GTK_FILE_CHOOSER (page);
559 gchar *f = gtk_file_chooser_get_filename (fc);
562 for(i = gtk_assistant_get_n_pages (GTK_ASSISTANT (ia)); i > 0; --i)
563 gtk_assistant_remove_page (GTK_ASSISTANT (ia), i);
565 gtk_assistant_set_page_complete (GTK_ASSISTANT(ia), GTK_WIDGET (fc), FALSE);
567 if (f && g_file_test (f, G_FILE_TEST_IS_REGULAR))
569 gtk_assistant_set_page_complete (GTK_ASSISTANT(ia), GTK_WIDGET (fc), TRUE);
572 spreadsheet_unref (ia->spreadsheet);
574 ia->spreadsheet = gnumeric_probe (f, FALSE);
576 if (!ia->spreadsheet)
577 ia->spreadsheet = ods_probe (f, FALSE);
581 sheet_spec_page_create (ia);
585 intro_page_create (ia);
586 first_line_page_create (ia);
587 separators_page_create (ia);
590 formats_page_create (ia);
596 /* This has to be done on a map signal callback,
597 because GtkFileChooserWidget resets everything when it is mapped. */
599 on_map (PsppireImportAssistant *ia, GtkWidget *page)
602 GtkFileChooser *fc = GTK_FILE_CHOOSER (page);
605 gtk_file_chooser_set_filename (fc, ia->file_name);
608 on_chosen (ia, page);
614 chooser_page_enter (PsppireImportAssistant *ia, GtkWidget *page)
619 chooser_page_leave (PsppireImportAssistant *ia, GtkWidget *page)
621 g_free (ia->file_name);
622 ia->file_name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (page));
623 gchar *encoding = psppire_encoding_selector_get_encoding (ia->encoding_selector);
625 if (!ia->spreadsheet)
627 ia->text_file = psppire_text_file_new (ia->file_name, encoding);
628 gtk_tree_view_set_model (GTK_TREE_VIEW (ia->first_line_tree_view),
629 GTK_TREE_MODEL (ia->text_file));
637 chooser_page_reset (PsppireImportAssistant *ia, GtkWidget *page)
639 GtkFileChooser *fc = GTK_FILE_CHOOSER (page);
641 gtk_file_chooser_set_filter (fc, ia->default_filter);
642 gtk_file_chooser_unselect_all (fc);
644 on_chosen (ia, page);
650 chooser_page_create (PsppireImportAssistant *ia)
652 GtkFileFilter *filter = NULL;
654 GtkWidget *chooser = gtk_file_chooser_widget_new (GTK_FILE_CHOOSER_ACTION_OPEN);
656 g_object_set_data (G_OBJECT (chooser), "on-forward", chooser_page_leave);
657 g_object_set_data (G_OBJECT (chooser), "on-reset", chooser_page_reset);
658 g_object_set_data (G_OBJECT (chooser), "on-entering",chooser_page_enter);
660 g_object_set (chooser, "local-only", FALSE, NULL);
663 ia->default_filter = gtk_file_filter_new ();
664 gtk_file_filter_set_name (ia->default_filter, _("All Files"));
665 gtk_file_filter_add_pattern (ia->default_filter, "*");
666 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), ia->default_filter);
668 filter = gtk_file_filter_new ();
669 gtk_file_filter_set_name (filter, _("Text Files"));
670 gtk_file_filter_add_mime_type (filter, "text/*");
671 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
673 filter = gtk_file_filter_new ();
674 gtk_file_filter_set_name (filter, _("Text (*.txt) Files"));
675 gtk_file_filter_add_pattern (filter, "*.txt");
676 gtk_file_filter_add_pattern (filter, "*.TXT");
677 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
679 filter = gtk_file_filter_new ();
680 gtk_file_filter_set_name (filter, _("Plain Text (ASCII) Files"));
681 gtk_file_filter_add_mime_type (filter, "text/plain");
682 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
684 filter = gtk_file_filter_new ();
685 gtk_file_filter_set_name (filter, _("Comma Separated Value Files"));
686 gtk_file_filter_add_mime_type (filter, "text/csv");
687 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
689 /* I've never encountered one of these, but it's listed here:
690 http://www.iana.org/assignments/media-types/text/tab-separated-values */
691 filter = gtk_file_filter_new ();
692 gtk_file_filter_set_name (filter, _("Tab Separated Value Files"));
693 gtk_file_filter_add_mime_type (filter, "text/tab-separated-values");
694 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
696 filter = gtk_file_filter_new ();
697 gtk_file_filter_set_name (filter, _("Gnumeric Spreadsheet Files"));
698 gtk_file_filter_add_mime_type (filter, "application/x-gnumeric");
699 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
701 filter = gtk_file_filter_new ();
702 gtk_file_filter_set_name (filter, _("OpenDocument Spreadsheet Files"));
703 gtk_file_filter_add_mime_type (filter, "application/vnd.oasis.opendocument.spreadsheet");
704 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
706 filter = gtk_file_filter_new ();
707 gtk_file_filter_set_name (filter, _("All Spreadsheet Files"));
708 gtk_file_filter_add_mime_type (filter, "application/x-gnumeric");
709 gtk_file_filter_add_mime_type (filter, "application/vnd.oasis.opendocument.spreadsheet");
710 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
712 ia->encoding_selector = psppire_encoding_selector_new ("Auto", TRUE);
713 gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (chooser), ia->encoding_selector);
715 add_page_to_assistant (ia, chooser,
716 GTK_ASSISTANT_PAGE_INTRO, _("Select File to Import"));
718 g_signal_connect_swapped (chooser, "selection-changed", G_CALLBACK (on_chosen), ia);
719 g_signal_connect_swapped (chooser, "map", G_CALLBACK (on_map), ia);
725 psppire_import_assistant_init (PsppireImportAssistant *ia)
727 ia->builder = builder_new ("text-data-import.ui");
729 ia->current_page = -1 ;
730 ia->file_name = NULL;
732 ia->spreadsheet = NULL;
734 ia->casereader_dict = NULL;
736 ia->main_loop = g_main_loop_new (NULL, TRUE);
738 g_signal_connect (ia, "prepare", G_CALLBACK (on_prepare), ia);
739 g_signal_connect (ia, "cancel", G_CALLBACK (on_cancel), ia);
740 g_signal_connect (ia, "close", G_CALLBACK (on_close), ia);
742 ia->paste_button = gtk_button_new_with_label (_("Paste"));
743 ia->reset_button = gtk_button_new_with_label (_("Reset"));
745 gtk_assistant_add_action_widget (GTK_ASSISTANT(ia), ia->paste_button);
747 g_signal_connect (ia->paste_button, "clicked", G_CALLBACK (on_paste), ia);
748 g_signal_connect (ia->reset_button, "clicked", G_CALLBACK (on_reset), ia);
750 gtk_assistant_add_action_widget (GTK_ASSISTANT(ia), ia->reset_button);
752 gtk_window_set_title (GTK_WINDOW (ia),
753 _("Importing Delimited Text Data"));
755 gtk_window_set_icon_name (GTK_WINDOW (ia), "pspp");
757 chooser_page_create (ia);
759 gtk_assistant_set_forward_page_func (GTK_ASSISTANT (ia), next_page_func, NULL, NULL);
761 gtk_window_maximize (GTK_WINDOW (ia));
765 /* Appends a page of the given TYPE, with PAGE as its content, to
766 the GtkAssistant encapsulated by IA. Returns the GtkWidget
767 that represents the page. */
769 add_page_to_assistant (PsppireImportAssistant *ia,
770 GtkWidget *page, GtkAssistantPageType type, const gchar *title)
772 GtkWidget *content = page;
774 gtk_assistant_append_page (GTK_ASSISTANT (ia), content);
775 gtk_assistant_set_page_type (GTK_ASSISTANT(ia), content, type);
776 gtk_assistant_set_page_title (GTK_ASSISTANT(ia), content, title);
777 gtk_assistant_set_page_complete (GTK_ASSISTANT(ia), content, TRUE);
783 /* Called when one of the radio buttons is clicked. */
785 on_intro_amount_changed (PsppireImportAssistant *p)
787 gtk_widget_set_sensitive (p->n_cases_spin,
788 gtk_toggle_button_get_active
789 (GTK_TOGGLE_BUTTON (p->n_cases_button)));
791 gtk_widget_set_sensitive (p->percent_spin,
792 gtk_toggle_button_get_active
793 (GTK_TOGGLE_BUTTON (p->percent_button)));
797 on_treeview_selection_change (PsppireImportAssistant *ia)
799 GtkTreeSelection *selection =
800 gtk_tree_view_get_selection (GTK_TREE_VIEW (ia->first_line_tree_view));
801 GtkTreeModel *model = NULL;
803 if (gtk_tree_selection_get_selected (selection, &model, &iter))
807 GtkTreePath *path = gtk_tree_model_get_path (model, &iter);
808 gint *index = gtk_tree_path_get_indices (path);
810 gtk_tree_path_free (path);
811 g_object_get (model, "maximum-lines", &max_lines, NULL);
812 gtk_widget_set_sensitive (ia->variable_names_cb,
813 (n > 0 && n < max_lines));
814 ia->delimiters_model =
815 psppire_delimited_text_new (GTK_TREE_MODEL (ia->text_file));
816 g_object_set (ia->delimiters_model, "first-line", n, NULL);
821 render_text_preview_line (GtkTreeViewColumn *tree_column,
822 GtkCellRenderer *cell,
823 GtkTreeModel *tree_model,
828 Set the text to a "insensitive" state if the row
829 is greater than what the user declared to be the maximum.
831 GtkTreePath *path = gtk_tree_model_get_path (tree_model, iter);
832 gint *ii = gtk_tree_path_get_indices (path);
834 g_object_get (tree_model, "maximum-lines", &max_lines, NULL);
835 g_object_set (cell, "sensitive", (*ii < max_lines), NULL);
836 gtk_tree_path_free (path);
839 /* Initializes IA's first_line substructure. */
841 first_line_page_create (PsppireImportAssistant *ia)
843 GtkWidget *w = get_widget_assert (ia->builder, "FirstLine");
845 g_object_set_data (G_OBJECT (w), "on-entering", on_treeview_selection_change);
847 add_page_to_assistant (ia, w,
848 GTK_ASSISTANT_PAGE_CONTENT, _("Select the First Line"));
850 GtkWidget *scrolled_window = get_widget_assert (ia->builder, "first-line-scroller");
852 if (ia->first_line_tree_view == NULL)
854 ia->first_line_tree_view = gtk_tree_view_new ();
855 g_object_set (ia->first_line_tree_view, "enable-search", FALSE, NULL);
857 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (ia->first_line_tree_view), TRUE);
859 GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
860 GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes (_("Line"), renderer,
864 gtk_tree_view_column_set_cell_data_func (column, renderer, render_text_preview_line, ia, 0);
865 gtk_tree_view_append_column (GTK_TREE_VIEW (ia->first_line_tree_view), column);
867 renderer = gtk_cell_renderer_text_new ();
868 column = gtk_tree_view_column_new_with_attributes (_("Text"), renderer, "text", 1, NULL);
869 gtk_tree_view_column_set_cell_data_func (column, renderer, render_text_preview_line, ia, 0);
871 gtk_tree_view_append_column (GTK_TREE_VIEW (ia->first_line_tree_view), column);
873 g_signal_connect_swapped (ia->first_line_tree_view, "cursor-changed",
874 G_CALLBACK (on_treeview_selection_change), ia);
875 gtk_container_add (GTK_CONTAINER (scrolled_window), ia->first_line_tree_view);
878 gtk_widget_show_all (scrolled_window);
880 ia->variable_names_cb = get_widget_assert (ia->builder, "variable-names");
884 intro_on_leave (PsppireImportAssistant *ia)
887 g_object_get (ia->text_file, "line-count", &lc, NULL);
888 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->n_cases_button)))
890 gint max_lines = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (ia->n_cases_spin));
891 g_object_set (ia->text_file, "maximum-lines", max_lines, NULL);
893 else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->percent_button)))
895 gdouble percent = gtk_spin_button_get_value (GTK_SPIN_BUTTON (ia->percent_spin));
896 g_object_set (ia->text_file, "maximum-lines", (gint) (lc * percent / 100.0), NULL);
900 g_object_set (ia->text_file, "maximum-lines", lc, NULL);
906 intro_on_enter (PsppireImportAssistant *ia)
908 GtkBuilder *builder = ia->builder;
909 GtkWidget *table = get_widget_assert (builder, "button-table");
914 ds_put_cstr (&s, _("This assistant will guide you through the process of "
915 "importing data into PSPP from a text file with one line "
916 "per case, in which fields are separated by tabs, "
917 "commas, or other delimiters.\n\n"));
921 if (ia->text_file->total_is_exact)
924 &s, ngettext ("The selected file contains %'lu line of text. ",
925 "The selected file contains %'lu lines of text. ",
926 ia->text_file->total_lines),
927 ia->text_file->total_lines);
929 else if (ia->text_file->total_lines > 0)
933 "The selected file contains approximately %'lu line of text. ",
934 "The selected file contains approximately %'lu lines of text. ",
935 ia->text_file->total_lines),
936 ia->text_file->total_lines);
939 "Only the first %zu line of the file will be shown for "
940 "preview purposes in the following screens. ",
941 "Only the first %zu lines of the file will be shown for "
942 "preview purposes in the following screens. ",
943 ia->text_file->line_cnt),
944 ia->text_file->line_cnt);
948 ds_put_cstr (&s, _("You may choose below how much of the file should "
949 "actually be imported."));
951 gtk_label_set_text (GTK_LABEL (get_widget_assert (builder, "intro-label")),
955 if (gtk_grid_get_child_at (GTK_GRID (table), 1, 1) == NULL)
957 GtkWidget *hbox_n_cases = psppire_scanf_new (_("Only the first %4d cases"), &ia->n_cases_spin);
958 gtk_grid_attach (GTK_GRID (table), hbox_n_cases,
963 GtkAdjustment *adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (ia->n_cases_spin));
964 gtk_adjustment_set_lower (adj, 1.0);
966 if (gtk_grid_get_child_at (GTK_GRID (table), 1, 2) == NULL)
968 GtkWidget *hbox_percent = psppire_scanf_new (_("Only the first %3d %% of file (approximately)"),
971 gtk_grid_attach (GTK_GRID (table), hbox_percent,
976 gtk_widget_show_all (table);
978 on_intro_amount_changed (ia);
981 /* Initializes IA's intro substructure. */
983 intro_page_create (PsppireImportAssistant *ia)
985 GtkBuilder *builder = ia->builder;
987 GtkWidget *w = get_widget_assert (builder, "Intro");
989 ia->percent_spin = gtk_spin_button_new_with_range (0, 100, 10);
992 add_page_to_assistant (ia, w, GTK_ASSISTANT_PAGE_CONTENT, _("Select the Lines to Import"));
994 ia->all_cases_button = get_widget_assert (builder, "import-all-cases");
996 ia->n_cases_button = get_widget_assert (builder, "import-n-cases");
998 ia->percent_button = get_widget_assert (builder, "import-percent");
1000 g_signal_connect_swapped (ia->all_cases_button, "toggled",
1001 G_CALLBACK (on_intro_amount_changed), ia);
1002 g_signal_connect_swapped (ia->n_cases_button, "toggled",
1003 G_CALLBACK (on_intro_amount_changed), ia);
1004 g_signal_connect_swapped (ia->percent_button, "toggled",
1005 G_CALLBACK (on_intro_amount_changed), ia);
1008 g_object_set_data (G_OBJECT (w), "on-forward", intro_on_leave);
1009 g_object_set_data (G_OBJECT (w), "on-entering", intro_on_enter);
1010 g_object_set_data (G_OBJECT (w), "on-reset", reset_intro_page);
1015 psppire_import_assistant_new (GtkWindow *toplevel)
1017 return GTK_WIDGET (g_object_new (PSPPIRE_TYPE_IMPORT_ASSISTANT,
1018 /* Some window managers (notably ratpoison)
1019 ignore the maximise command when a window is
1020 transient. This causes problems for this
1022 /* "transient-for", toplevel, */
1030 /* Chooses a name for each column on the separators page */
1032 choose_column_names (PsppireImportAssistant *ia)
1035 unsigned long int generated_name_count = 0;
1036 dict_clear (ia->dict);
1039 i < gtk_tree_model_get_n_columns (GTK_TREE_MODEL (ia->delimiters_model)) - 1;
1042 const gchar *candidate_name = NULL;
1044 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->variable_names_cb)))
1046 candidate_name = psppire_delimited_text_get_header_title (PSPPIRE_DELIMITED_TEXT (ia->delimiters_model), i);
1049 char *name = dict_make_unique_var_name (ia->dict,
1051 &generated_name_count);
1053 dict_create_var_assert (ia->dict, name, 0);
1058 /* Called when the user toggles one of the separators
1061 on_separator_toggle (GtkToggleButton *toggle UNUSED,
1062 PsppireImportAssistant *ia)
1065 GSList *delimiters = NULL;
1066 for (i = 0; i < SEPARATOR_CNT; i++)
1068 const struct separator *s = &separators[i];
1069 GtkWidget *button = get_widget_assert (ia->builder, s->name);
1070 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
1072 delimiters = g_slist_prepend (delimiters, GINT_TO_POINTER (s->c));
1076 g_object_set (ia->delimiters_model, "delimiters", delimiters, NULL);
1078 revise_fields_preview (ia);
1082 /* Called when the user changes the entry field for custom
1085 on_separators_custom_entry_notify (GObject *gobject UNUSED,
1086 GParamSpec *arg1 UNUSED,
1087 PsppireImportAssistant *ia)
1089 revise_fields_preview (ia);
1092 /* Called when the user toggles the checkbox that enables custom
1095 on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
1096 PsppireImportAssistant *ia)
1098 bool is_active = gtk_toggle_button_get_active (custom_cb);
1099 gtk_widget_set_sensitive (ia->custom_entry, is_active);
1100 revise_fields_preview (ia);
1103 /* Called when the user changes the selection in the combo box
1104 that selects a quote character. */
1106 on_quote_combo_change (GtkComboBox *combo, PsppireImportAssistant *ia)
1108 // revise_fields_preview (ia);
1111 /* Called when the user toggles the checkbox that enables
1114 on_quote_cb_toggle (GtkToggleButton *quote_cb, PsppireImportAssistant *ia)
1116 bool is_active = gtk_toggle_button_get_active (quote_cb);
1117 gtk_widget_set_sensitive (ia->quote_combo, is_active);
1118 revise_fields_preview (ia);
1121 /* Initializes IA's separators substructure. */
1123 separators_page_create (PsppireImportAssistant *ia)
1125 GtkBuilder *builder = ia->builder;
1129 GtkWidget *w = get_widget_assert (builder, "Separators");
1131 g_object_set_data (G_OBJECT (w), "on-entering", prepare_separators_page);
1132 g_object_set_data (G_OBJECT (w), "on-reset", prepare_separators_page);
1134 add_page_to_assistant (ia, w, GTK_ASSISTANT_PAGE_CONTENT, _("Choose Separators"));
1136 ia->custom_cb = get_widget_assert (builder, "custom-cb");
1137 ia->custom_entry = get_widget_assert (builder, "custom-entry");
1138 ia->quote_combo = get_widget_assert (builder, "quote-combo");
1139 ia->quote_cb = get_widget_assert (builder, "quote-cb");
1141 gtk_combo_box_set_active (GTK_COMBO_BOX (ia->quote_combo), 0);
1143 if (ia->fields_tree_view == NULL)
1145 GtkWidget *scroller = get_widget_assert (ia->builder, "fields-scroller");
1146 ia->fields_tree_view = gtk_tree_view_new ();
1147 g_object_set (ia->fields_tree_view, "enable-search", FALSE, NULL);
1148 gtk_container_add (GTK_CONTAINER (scroller), GTK_WIDGET (ia->fields_tree_view));
1149 gtk_widget_show_all (scroller);
1152 g_signal_connect (ia->quote_combo, "changed",
1153 G_CALLBACK (on_quote_combo_change), ia);
1154 g_signal_connect (ia->quote_cb, "toggled",
1155 G_CALLBACK (on_quote_cb_toggle), ia);
1156 g_signal_connect (ia->custom_entry, "notify::text",
1157 G_CALLBACK (on_separators_custom_entry_notify), ia);
1158 g_signal_connect (ia->custom_cb, "toggled",
1159 G_CALLBACK (on_separators_custom_cb_toggle), ia);
1160 for (i = 0; i < SEPARATOR_CNT; i++)
1161 g_signal_connect (get_widget_assert (builder, separators[i].name),
1162 "toggled", G_CALLBACK (on_separator_toggle), ia);
1171 static struct casereader_random_class my_casereader_class;
1173 static struct ccase *
1174 my_read (struct casereader *reader, void *aux, casenumber idx)
1176 PsppireImportAssistant *ia = PSPPIRE_IMPORT_ASSISTANT (aux);
1177 GtkTreeModel *tm = GTK_TREE_MODEL (ia->delimiters_model);
1179 GtkTreePath *tp = gtk_tree_path_new_from_indices (idx, -1);
1181 const struct caseproto *proto = casereader_get_proto (reader);
1184 struct ccase *c = NULL;
1185 if (gtk_tree_model_get_iter (tm, &iter, tp))
1187 c = case_create (proto);
1189 for (i = 0 ; i < caseproto_get_n_widths (proto); ++i)
1192 gtk_tree_model_get_value (tm, &iter, i + 1, &value);
1194 const struct variable *var = dict_get_var (ia->casereader_dict, i);
1196 const gchar *ss = g_value_get_string (&value);
1199 union value *v = case_data_rw (c, var);
1200 /* In this reader we derive the union value from the
1201 string in the tree_model. We retrieve the width and format
1202 from a dictionary which is stored directly after
1203 the reader creation. Changes in ia->dict in the
1204 variable window are not reflected here and therefore
1205 this is always compatible with the width in the
1206 caseproto. See bug #58298 */
1207 char *xx = data_in (ss_cstr (ss),
1209 var_get_write_format (var)->type,
1210 v, var_get_width (var), "UTF-8");
1213 /* g_print ("%s:%d Err %s\n", __FILE__, __LINE__, xx); */
1216 g_value_unset (&value);
1220 gtk_tree_path_free (tp);
1226 my_destroy (struct casereader *reader, void *aux)
1228 g_print ("%s:%d %p\n", __FILE__, __LINE__, reader);
1232 my_advance (struct casereader *reader, void *aux, casenumber cnt)
1234 g_print ("%s:%d\n", __FILE__, __LINE__);
1237 static struct casereader *
1238 textfile_create_reader (PsppireImportAssistant *ia)
1240 int n_vars = dict_get_var_cnt (ia->dict);
1244 struct fmt_guesser **fg = xcalloc (sizeof *fg, n_vars);
1245 for (i = 0 ; i < n_vars; ++i)
1247 fg[i] = fmt_guesser_create ();
1250 gint n_rows = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (ia->delimiters_model), NULL);
1254 for (ok = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (ia->delimiters_model), &iter);
1256 ok = gtk_tree_model_iter_next (GTK_TREE_MODEL (ia->delimiters_model), &iter))
1258 for (i = 0 ; i < n_vars; ++i)
1261 gtk_tree_model_get (GTK_TREE_MODEL (ia->delimiters_model), &iter, i+1, &s, -1);
1263 fmt_guesser_add (fg[i], ss_cstr (s));
1268 struct caseproto *proto = caseproto_create ();
1269 for (i = 0 ; i < n_vars; ++i)
1272 fmt_guesser_guess (fg[i], &fs);
1274 fmt_fix (&fs, FMT_FOR_INPUT);
1276 struct variable *var = dict_get_var (ia->dict, i);
1278 int width = fmt_var_width (&fs);
1280 var_set_width_and_formats (var, width,
1283 proto = caseproto_add_width (proto, width);
1284 fmt_guesser_destroy (fg[i]);
1289 struct casereader *cr = casereader_create_random (proto, n_rows, &my_casereader_class, ia);
1290 /* Store the dictionary at this point when the casereader is created.
1291 my_read depends on the dictionary to interpret the strings in the treeview.
1292 This guarantees that the union value is produced according to the
1293 caseproto in the reader. */
1294 ia->casereader_dict = dict_clone (ia->dict);
1295 caseproto_unref (proto);
1299 /* When during import the variable type is changed, the reader is reinitialized
1300 based on the new dictionary with a fresh caseprototype. The default behaviour
1301 when a variable type is changed and the column is resized is that the union
1302 value is interpreted with new variable type and an overlay for that column
1303 is generated. Here we reinit to the original reader based on strings.
1304 As a result you can switch from string to numeric to string without loosing
1305 the string information. */
1307 ia_variable_changed_cb (GObject *obj, gint var_num, guint what,
1308 const struct variable *oldvar, gpointer data)
1310 PsppireImportAssistant *ia = PSPPIRE_IMPORT_ASSISTANT (data);
1312 struct caseproto *proto = caseproto_create();
1313 for (int i = 0; i < dict_get_var_cnt (ia->dict); i++)
1315 const struct variable *var = dict_get_var (ia->dict, i);
1316 int width = var_get_width (var);
1317 proto = caseproto_add_width (proto, width);
1320 gint n_rows = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (ia->delimiters_model), NULL);
1322 PsppireDataStore *store = NULL;
1323 g_object_get (ia->data_sheet, "data-model", &store, NULL);
1325 struct casereader *cr = casereader_create_random (proto, n_rows,
1326 &my_casereader_class, ia);
1327 psppire_data_store_set_reader (store, cr);
1328 dict_unref (ia->casereader_dict);
1329 ia->casereader_dict = dict_clone (ia->dict);
1332 /* Called just before the formats page of the assistant is
1335 prepare_formats_page (PsppireImportAssistant *ia)
1337 my_casereader_class.read = my_read;
1338 my_casereader_class.destroy = my_destroy;
1339 my_casereader_class.advance = my_advance;
1341 if (ia->spreadsheet)
1343 GtkBuilder *builder = ia->builder;
1344 GtkWidget *range_entry = get_widget_assert (builder, "cell-range-entry");
1345 GtkWidget *rnc = get_widget_assert (builder, "readnames-checkbox");
1346 GtkWidget *combo_box = get_widget_assert (builder, "sheet-entry");
1348 struct spreadsheet_read_options opts;
1349 opts.sheet_name = NULL;
1350 opts.sheet_index = gtk_combo_box_get_active (GTK_COMBO_BOX (combo_box)) + 1;
1351 opts.read_names = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rnc));
1352 opts.cell_range = g_strdup (gtk_entry_get_text (GTK_ENTRY (range_entry)));
1355 struct casereader *reader = spreadsheet_make_reader (ia->spreadsheet, &opts);
1357 PsppireDict *dict = psppire_dict_new_from_dict (ia->spreadsheet->dict);
1358 PsppireDataStore *store = psppire_data_store_new (dict);
1359 psppire_data_store_set_reader (store, reader);
1360 g_object_set (ia->data_sheet, "data-model", store, NULL);
1361 g_object_set (ia->var_sheet, "data-model", dict, NULL);
1365 struct casereader *reader = textfile_create_reader (ia);
1367 PsppireDict *dict = psppire_dict_new_from_dict (ia->dict);
1368 PsppireDataStore *store = psppire_data_store_new (dict);
1369 psppire_data_store_set_reader (store, reader);
1370 g_signal_connect (dict, "variable-changed",
1371 G_CALLBACK (ia_variable_changed_cb),
1374 g_object_set (ia->data_sheet, "data-model", store, NULL);
1375 g_object_set (ia->var_sheet, "data-model", dict, NULL);
1379 g_object_get (get_widget_assert (ia->builder, "vpaned1"),
1380 "max-position", &pmax, NULL);
1383 g_object_set (get_widget_assert (ia->builder, "vpaned1"),
1384 "position", pmax / 2, NULL);
1386 gtk_widget_show (ia->paste_button);
1390 formats_page_create (PsppireImportAssistant *ia)
1392 GtkBuilder *builder = ia->builder;
1394 GtkWidget *w = get_widget_assert (builder, "Formats");
1395 g_object_set_data (G_OBJECT (w), "on-entering", prepare_formats_page);
1396 g_object_set_data (G_OBJECT (w), "on-reset", reset_formats_page);
1398 GtkWidget *vars_scroller = get_widget_assert (builder, "vars-scroller");
1399 if (ia->var_sheet == NULL)
1401 ia->var_sheet = psppire_variable_sheet_new ();
1403 gtk_container_add (GTK_CONTAINER (vars_scroller), ia->var_sheet);
1405 ia->dict = dict_create (get_default_encoding ());
1407 gtk_widget_show_all (vars_scroller);
1409 GtkWidget *data_scroller = get_widget_assert (builder, "data-scroller");
1410 if (ia->data_sheet == NULL)
1412 ia->data_sheet = psppire_data_sheet_new ();
1413 g_object_set (ia->data_sheet, "editable", FALSE, NULL);
1415 gtk_container_add (GTK_CONTAINER (data_scroller), ia->data_sheet);
1417 gtk_widget_show_all (data_scroller);
1420 add_page_to_assistant (ia, w,
1421 GTK_ASSISTANT_PAGE_CONFIRM, _("Adjust Variable Formats"));
1428 separators_append_syntax (const PsppireImportAssistant *ia, struct string *s)
1432 ds_put_cstr (s, " /DELIMITERS=\"");
1434 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (get_widget_assert (ia->builder, "tab"))))
1435 ds_put_cstr (s, "\\t");
1436 for (i = 0; i < SEPARATOR_CNT; i++)
1438 const struct separator *seps = &separators[i];
1439 GtkWidget *button = get_widget_assert (ia->builder, seps->name);
1440 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
1442 if (seps->c == '\t')
1445 ds_put_byte (s, seps->c);
1448 ds_put_cstr (s, "\"\n");
1450 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->quote_cb)))
1452 GtkComboBoxText *cbt = GTK_COMBO_BOX_TEXT (ia->quote_combo);
1453 gchar *quotes = gtk_combo_box_text_get_active_text (cbt);
1454 if (quotes && *quotes)
1455 syntax_gen_pspp (s, " /QUALIFIER=%sq\n", quotes);
1461 formats_append_syntax (const PsppireImportAssistant *ia, struct string *s)
1466 g_return_if_fail (ia->dict);
1468 ds_put_cstr (s, " /VARIABLES=\n");
1470 var_cnt = dict_get_var_cnt (ia->dict);
1471 for (i = 0; i < var_cnt; i++)
1473 struct variable *var = dict_get_var (ia->dict, i);
1474 char format_string[FMT_STRING_LEN_MAX + 1];
1475 fmt_to_string (var_get_print_format (var), format_string);
1476 ds_put_format (s, " %s %s%s\n",
1477 var_get_name (var), format_string,
1478 i == var_cnt - 1 ? "." : "");
1483 first_line_append_syntax (const PsppireImportAssistant *ia, struct string *s)
1485 gint first_case = 0;
1486 g_object_get (ia->delimiters_model, "first-line", &first_case, NULL);
1489 ds_put_format (s, " /FIRSTCASE=%d\n", first_case + 1);
1493 intro_append_syntax (const PsppireImportAssistant *ia, struct string *s)
1495 gint first_line = 0;
1496 g_object_get (ia->delimiters_model, "first-line", &first_line, NULL);
1498 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->n_cases_button)))
1499 ds_put_format (s, "SELECT IF ($CASENUM <= %d).\n",
1500 gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (ia->n_cases_spin)) - first_line);
1501 else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->percent_button)))
1502 ds_put_format (s, "SAMPLE %.4g.\n",
1503 gtk_spin_button_get_value (GTK_SPIN_BUTTON (ia->percent_spin)) / 100.0);
1507 /* Emits PSPP syntax to S that applies the dictionary attributes
1508 (such as missing values and value labels) of the variables in
1511 apply_dict (const struct dictionary *dict, struct string *s)
1513 size_t var_cnt = dict_get_var_cnt (dict);
1516 for (i = 0; i < var_cnt; i++)
1518 struct variable *var = dict_get_var (dict, i);
1519 const char *name = var_get_name (var);
1520 enum val_type type = var_get_type (var);
1521 int width = var_get_width (var);
1522 enum measure measure = var_get_measure (var);
1523 enum var_role role = var_get_role (var);
1524 enum alignment alignment = var_get_alignment (var);
1525 const struct fmt_spec *format = var_get_print_format (var);
1527 if (var_has_missing_values (var))
1529 const struct missing_values *mv = var_get_missing_values (var);
1532 syntax_gen_pspp (s, "MISSING VALUES %ss (", name);
1533 for (j = 0; j < mv_n_values (mv); j++)
1536 ds_put_cstr (s, ", ");
1537 syntax_gen_value (s, mv_get_value (mv, j), width, format);
1540 if (mv_has_range (mv))
1543 if (mv_has_value (mv))
1544 ds_put_cstr (s, ", ");
1545 mv_get_range (mv, &low, &high);
1546 syntax_gen_num_range (s, low, high, format);
1548 ds_put_cstr (s, ").\n");
1550 if (var_has_value_labels (var))
1552 const struct val_labs *vls = var_get_value_labels (var);
1553 const struct val_lab **labels = val_labs_sorted (vls);
1554 size_t n_labels = val_labs_count (vls);
1557 syntax_gen_pspp (s, "VALUE LABELS %ss", name);
1558 for (i = 0; i < n_labels; i++)
1560 const struct val_lab *vl = labels[i];
1561 ds_put_cstr (s, "\n ");
1562 syntax_gen_value (s, &vl->value, width, format);
1563 ds_put_byte (s, ' ');
1564 syntax_gen_string (s, ss_cstr (val_lab_get_escaped_label (vl)));
1567 ds_put_cstr (s, ".\n");
1569 if (var_has_label (var))
1570 syntax_gen_pspp (s, "VARIABLE LABELS %ss %sq.\n",
1571 name, var_get_label (var));
1572 if (measure != var_default_measure (type))
1573 syntax_gen_pspp (s, "VARIABLE LEVEL %ss (%ss).\n",
1574 name, measure_to_syntax (measure));
1575 if (role != ROLE_INPUT)
1576 syntax_gen_pspp (s, "VARIABLE ROLE /%ss %ss.\n",
1577 var_role_to_syntax (role), name);
1578 if (alignment != var_default_alignment (type))
1579 syntax_gen_pspp (s, "VARIABLE ALIGNMENT %ss (%ss).\n",
1580 name, alignment_to_syntax (alignment));
1581 if (var_get_display_width (var) != var_default_display_width (width))
1582 syntax_gen_pspp (s, "VARIABLE WIDTH %ss (%d).\n",
1583 name, var_get_display_width (var));
1590 sheet_spec_gen_syntax (PsppireImportAssistant *ia, struct string *s)
1592 GtkBuilder *builder = ia->builder;
1593 GtkWidget *range_entry = get_widget_assert (builder, "cell-range-entry");
1594 GtkWidget *sheet_entry = get_widget_assert (builder, "sheet-entry");
1595 GtkWidget *rnc = get_widget_assert (builder, "readnames-checkbox");
1596 const gchar *range = gtk_entry_get_text (GTK_ENTRY (range_entry));
1597 int sheet_index = 1 + gtk_combo_box_get_active (GTK_COMBO_BOX (sheet_entry));
1598 gboolean read_names = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rnc));
1602 if (ia->spreadsheet)
1603 filename = ia->spreadsheet->file_name;
1605 g_object_get (ia->text_file, "file-name", &filename, NULL);
1610 "\n /SHEET=index %d"
1611 "\n /READNAMES=%ss",
1612 (ia->spreadsheet->type == SPREADSHEET_GNUMERIC) ? "GNM" : "ODS",
1615 read_names ? "ON" : "OFF");
1617 if (range && 0 != strcmp ("", range))
1620 "\n /CELLRANGE=RANGE %sq", range);
1625 "\n /CELLRANGE=FULL");
1629 syntax_gen_pspp (s, ".\n");
1634 psppire_import_assistant_generate_syntax (PsppireImportAssistant *ia)
1636 struct string s = DS_EMPTY_INITIALIZER;
1638 if (!ia->spreadsheet)
1640 gchar *file_name = NULL;
1641 gchar *encoding = NULL;
1642 g_object_get (ia->text_file,
1643 "file-name", &file_name,
1644 "encoding", &encoding,
1647 if (file_name == NULL)
1650 syntax_gen_pspp (&s,
1655 if (encoding && strcmp (encoding, "Auto"))
1656 syntax_gen_pspp (&s, " /ENCODING=%sq\n", encoding);
1659 " /ARRANGEMENT=DELIMITED\n"
1660 " /DELCASE=LINE\n");
1662 first_line_append_syntax (ia, &s);
1663 separators_append_syntax (ia, &s);
1665 formats_append_syntax (ia, &s);
1666 apply_dict (ia->dict, &s);
1667 intro_append_syntax (ia, &s);
1671 sheet_spec_gen_syntax (ia, &s);
1674 return ds_cstr (&s);
1679 psppire_import_assistant_run (PsppireImportAssistant *asst)
1681 g_main_loop_run (asst->main_loop);
1682 return asst->response;