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 if (hmap_count (count_map) > 0)
290 int most_frequent = -1;
292 for (j = 0; j < SEPARATOR_CNT; ++j)
294 struct separator_count_node *cn;
295 HMAP_FOR_EACH (cn, struct separator_count_node, node, &count_map[j])
297 if (largest < cn->quantity)
299 largest = cn->quantity;
303 hmap_destroy (&count_map[j]);
306 g_return_if_fail (most_frequent >= 0);
309 get_widget_assert (ia->builder, separators[most_frequent].name);
310 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), TRUE);
315 repopulate_delimiter_columns (PsppireImportAssistant *ia)
317 /* Remove all the columns */
318 while (gtk_tree_view_get_n_columns (GTK_TREE_VIEW (ia->fields_tree_view)) > 0)
320 GtkTreeViewColumn *tvc = gtk_tree_view_get_column (GTK_TREE_VIEW (ia->fields_tree_view), 0);
321 gtk_tree_view_remove_column (GTK_TREE_VIEW (ia->fields_tree_view), tvc);
325 gtk_tree_model_get_n_columns (GTK_TREE_MODEL (ia->delimiters_model));
327 /* ... and put them back again. */
329 for (f = gtk_tree_view_get_n_columns (GTK_TREE_VIEW (ia->fields_tree_view));
332 GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
334 const gchar *title = NULL;
340 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->variable_names_cb)))
343 psppire_delimited_text_get_header_title
344 (PSPPIRE_DELIMITED_TEXT (ia->delimiters_model), f - 1);
350 GtkTreeViewColumn *column =
351 gtk_tree_view_column_new_with_attributes (title,
355 g_object_set (column,
357 "sizing", GTK_TREE_VIEW_COLUMN_AUTOSIZE,
360 gtk_tree_view_append_column (GTK_TREE_VIEW (ia->fields_tree_view), column);
365 reset_tree_view_model (PsppireImportAssistant *ia)
367 GtkTreeModel *tm = gtk_tree_view_get_model (GTK_TREE_VIEW (ia->fields_tree_view));
369 gtk_tree_view_set_model (GTK_TREE_VIEW (ia->fields_tree_view), NULL);
372 repopulate_delimiter_columns (ia);
374 gtk_tree_view_set_model (GTK_TREE_VIEW (ia->fields_tree_view), tm);
375 // gtk_tree_view_columns_autosize (GTK_TREE_VIEW (ia->fields_tree_view));
380 /* Called just before the separators page becomes visible in the
381 assistant, and when the Reset button is clicked. */
383 prepare_separators_page (PsppireImportAssistant *ia, GtkWidget *page)
385 gtk_tree_view_set_model (GTK_TREE_VIEW (ia->fields_tree_view),
386 GTK_TREE_MODEL (ia->delimiters_model));
388 g_signal_connect_swapped (GTK_TREE_MODEL (ia->delimiters_model), "notify::delimiters",
389 G_CALLBACK (reset_tree_view_model), ia);
392 repopulate_delimiter_columns (ia);
394 revise_fields_preview (ia);
395 choose_likely_separators (ia);
398 /* Resets IA's intro page to its initial state. */
400 reset_intro_page (PsppireImportAssistant *ia)
402 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->all_cases_button),
408 /* Clears the set of user-modified variables from IA's formats
409 substructure. This discards user modifications to variable
410 formats, thereby causing formats to revert to their
413 reset_formats_page (PsppireImportAssistant *ia, GtkWidget *page)
417 static void prepare_formats_page (PsppireImportAssistant *ia);
419 /* Called when the Reset button is clicked. */
421 on_reset (GtkButton *button, PsppireImportAssistant *ia)
423 gint pn = gtk_assistant_get_current_page (GTK_ASSISTANT (ia));
425 GtkWidget *page = gtk_assistant_get_nth_page (GTK_ASSISTANT (ia), pn);
427 page_func *on_reset = g_object_get_data (G_OBJECT (page), "on-reset");
436 next_page_func (gint old_page, gpointer data)
442 /* Called just before PAGE is displayed as the current page of
443 IMPORT_ASSISTANT, this updates IA content according to the new
446 on_prepare (GtkAssistant *assistant, GtkWidget *page, PsppireImportAssistant *ia)
448 gtk_widget_show (ia->reset_button);
449 gtk_widget_hide (ia->paste_button);
451 gint pn = gtk_assistant_get_current_page (assistant);
452 gint previous_page_index = ia->current_page;
454 if (previous_page_index >= 0)
456 GtkWidget *closing_page = gtk_assistant_get_nth_page (GTK_ASSISTANT (ia), previous_page_index);
458 if (pn > previous_page_index)
460 page_func *on_forward = g_object_get_data (G_OBJECT (closing_page), "on-forward");
463 on_forward (ia, closing_page);
467 page_func *on_back = g_object_get_data (G_OBJECT (closing_page), "on-back");
470 on_back (ia, closing_page);
475 GtkWidget *new_page = gtk_assistant_get_nth_page (GTK_ASSISTANT (ia), pn);
477 page_func *on_entering = g_object_get_data (G_OBJECT (new_page), "on-entering");
480 on_entering (ia, new_page);
483 ia->current_page = pn;
486 /* Called when the Cancel button in the assistant is clicked. */
488 on_cancel (GtkAssistant *assistant, PsppireImportAssistant *ia)
490 close_assistant (ia, GTK_RESPONSE_CANCEL);
493 /* Called when the Apply button on the last page of the assistant
496 on_close (GtkAssistant *assistant, PsppireImportAssistant *ia)
498 close_assistant (ia, GTK_RESPONSE_APPLY);
503 add_page_to_assistant (PsppireImportAssistant *ia,
504 GtkWidget *page, GtkAssistantPageType type, const gchar *);
508 on_sheet_combo_changed (GtkComboBox *cb, PsppireImportAssistant *ia)
512 GtkTreeModel *model = gtk_combo_box_get_model (cb);
513 GtkBuilder *builder = ia->builder;
514 GtkWidget *range_entry = get_widget_assert (builder, "cell-range-entry");
516 gtk_combo_box_get_active_iter (cb, &iter);
517 gtk_tree_model_get (model, &iter, PSPPIRE_SPREADSHEET_MODEL_COL_RANGE, &range, -1);
518 gtk_entry_set_text (GTK_ENTRY (range_entry), range ? range : "");
522 /* Prepares IA's sheet_spec page. */
524 prepare_sheet_spec_page (PsppireImportAssistant *ia)
526 GtkBuilder *builder = ia->builder;
527 GtkWidget *sheet_entry = get_widget_assert (builder, "sheet-entry");
528 GtkWidget *readnames_checkbox = get_widget_assert (builder, "readnames-checkbox");
530 gtk_combo_box_set_model (GTK_COMBO_BOX (sheet_entry),
531 psppire_spreadsheet_model_new (ia->spreadsheet));
533 gtk_combo_box_set_active (GTK_COMBO_BOX (sheet_entry), 0);
535 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (readnames_checkbox), FALSE);
539 /* Initializes IA's sheet_spec substructure. */
541 sheet_spec_page_create (PsppireImportAssistant *ia)
543 GtkBuilder *builder = ia->builder;
544 GtkWidget *page = get_widget_assert (builder, "Spreadsheet-Importer");
546 GtkWidget *combo_box = get_widget_assert (builder, "sheet-entry");
547 GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
548 gtk_cell_layout_clear (GTK_CELL_LAYOUT (combo_box));
549 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), renderer, TRUE);
550 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), renderer,
554 g_signal_connect (combo_box, "changed", G_CALLBACK (on_sheet_combo_changed), ia);
556 add_page_to_assistant (ia, page,
557 GTK_ASSISTANT_PAGE_CONTENT, _("Importing Spreadsheet Data"));
559 g_object_set_data (G_OBJECT (page), "on-entering", prepare_sheet_spec_page);
563 on_chosen (PsppireImportAssistant *ia, GtkWidget *page)
565 GtkFileChooser *fc = GTK_FILE_CHOOSER (page);
566 gchar *f = gtk_file_chooser_get_filename (fc);
569 for(i = gtk_assistant_get_n_pages (GTK_ASSISTANT (ia)); i > 0; --i)
570 gtk_assistant_remove_page (GTK_ASSISTANT (ia), i);
572 gtk_assistant_set_page_complete (GTK_ASSISTANT(ia), GTK_WIDGET (fc), FALSE);
574 if (f && g_file_test (f, G_FILE_TEST_IS_REGULAR))
576 gtk_assistant_set_page_complete (GTK_ASSISTANT(ia), GTK_WIDGET (fc), TRUE);
579 spreadsheet_unref (ia->spreadsheet);
581 ia->spreadsheet = gnumeric_probe (f, FALSE);
583 if (!ia->spreadsheet)
584 ia->spreadsheet = ods_probe (f, FALSE);
588 sheet_spec_page_create (ia);
592 intro_page_create (ia);
593 first_line_page_create (ia);
594 separators_page_create (ia);
597 formats_page_create (ia);
603 /* This has to be done on a map signal callback,
604 because GtkFileChooserWidget resets everything when it is mapped. */
606 on_map (PsppireImportAssistant *ia, GtkWidget *page)
609 GtkFileChooser *fc = GTK_FILE_CHOOSER (page);
612 gtk_file_chooser_set_filename (fc, ia->file_name);
615 on_chosen (ia, page);
621 chooser_page_enter (PsppireImportAssistant *ia, GtkWidget *page)
626 chooser_page_leave (PsppireImportAssistant *ia, GtkWidget *page)
628 g_free (ia->file_name);
629 ia->file_name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (page));
630 gchar *encoding = psppire_encoding_selector_get_encoding (ia->encoding_selector);
632 if (!ia->spreadsheet)
634 ia->text_file = psppire_text_file_new (ia->file_name, encoding);
635 gtk_tree_view_set_model (GTK_TREE_VIEW (ia->first_line_tree_view),
636 GTK_TREE_MODEL (ia->text_file));
644 chooser_page_reset (PsppireImportAssistant *ia, GtkWidget *page)
646 GtkFileChooser *fc = GTK_FILE_CHOOSER (page);
648 gtk_file_chooser_set_filter (fc, ia->default_filter);
649 gtk_file_chooser_unselect_all (fc);
651 on_chosen (ia, page);
657 chooser_page_create (PsppireImportAssistant *ia)
659 GtkFileFilter *filter = NULL;
661 GtkWidget *chooser = gtk_file_chooser_widget_new (GTK_FILE_CHOOSER_ACTION_OPEN);
663 g_object_set_data (G_OBJECT (chooser), "on-forward", chooser_page_leave);
664 g_object_set_data (G_OBJECT (chooser), "on-reset", chooser_page_reset);
665 g_object_set_data (G_OBJECT (chooser), "on-entering",chooser_page_enter);
667 g_object_set (chooser, "local-only", FALSE, NULL);
670 ia->default_filter = gtk_file_filter_new ();
671 gtk_file_filter_set_name (ia->default_filter, _("All Files"));
672 gtk_file_filter_add_pattern (ia->default_filter, "*");
673 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), ia->default_filter);
675 filter = gtk_file_filter_new ();
676 gtk_file_filter_set_name (filter, _("Text Files"));
677 gtk_file_filter_add_mime_type (filter, "text/*");
678 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
680 filter = gtk_file_filter_new ();
681 gtk_file_filter_set_name (filter, _("Text (*.txt) Files"));
682 gtk_file_filter_add_pattern (filter, "*.txt");
683 gtk_file_filter_add_pattern (filter, "*.TXT");
684 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
686 filter = gtk_file_filter_new ();
687 gtk_file_filter_set_name (filter, _("Plain Text (ASCII) Files"));
688 gtk_file_filter_add_mime_type (filter, "text/plain");
689 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
691 filter = gtk_file_filter_new ();
692 gtk_file_filter_set_name (filter, _("Comma Separated Value Files"));
693 gtk_file_filter_add_mime_type (filter, "text/csv");
694 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
696 /* I've never encountered one of these, but it's listed here:
697 http://www.iana.org/assignments/media-types/text/tab-separated-values */
698 filter = gtk_file_filter_new ();
699 gtk_file_filter_set_name (filter, _("Tab Separated Value Files"));
700 gtk_file_filter_add_mime_type (filter, "text/tab-separated-values");
701 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
703 filter = gtk_file_filter_new ();
704 gtk_file_filter_set_name (filter, _("Gnumeric Spreadsheet Files"));
705 gtk_file_filter_add_mime_type (filter, "application/x-gnumeric");
706 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
708 filter = gtk_file_filter_new ();
709 gtk_file_filter_set_name (filter, _("OpenDocument Spreadsheet Files"));
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 filter = gtk_file_filter_new ();
714 gtk_file_filter_set_name (filter, _("All Spreadsheet Files"));
715 gtk_file_filter_add_mime_type (filter, "application/x-gnumeric");
716 gtk_file_filter_add_mime_type (filter, "application/vnd.oasis.opendocument.spreadsheet");
717 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
719 ia->encoding_selector = psppire_encoding_selector_new ("Auto", TRUE);
720 gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (chooser), ia->encoding_selector);
722 add_page_to_assistant (ia, chooser,
723 GTK_ASSISTANT_PAGE_INTRO, _("Select File to Import"));
725 g_signal_connect_swapped (chooser, "selection-changed", G_CALLBACK (on_chosen), ia);
726 g_signal_connect_swapped (chooser, "map", G_CALLBACK (on_map), ia);
732 psppire_import_assistant_init (PsppireImportAssistant *ia)
734 ia->builder = builder_new ("text-data-import.ui");
736 ia->current_page = -1 ;
737 ia->file_name = NULL;
739 ia->spreadsheet = NULL;
741 ia->casereader_dict = NULL;
743 ia->main_loop = g_main_loop_new (NULL, TRUE);
745 g_signal_connect (ia, "prepare", G_CALLBACK (on_prepare), ia);
746 g_signal_connect (ia, "cancel", G_CALLBACK (on_cancel), ia);
747 g_signal_connect (ia, "close", G_CALLBACK (on_close), ia);
749 ia->paste_button = gtk_button_new_with_label (_("Paste"));
750 ia->reset_button = gtk_button_new_with_label (_("Reset"));
752 gtk_assistant_add_action_widget (GTK_ASSISTANT(ia), ia->paste_button);
754 g_signal_connect (ia->paste_button, "clicked", G_CALLBACK (on_paste), ia);
755 g_signal_connect (ia->reset_button, "clicked", G_CALLBACK (on_reset), ia);
757 gtk_assistant_add_action_widget (GTK_ASSISTANT(ia), ia->reset_button);
759 gtk_window_set_title (GTK_WINDOW (ia),
760 _("Importing Delimited Text Data"));
762 gtk_window_set_icon_name (GTK_WINDOW (ia), "pspp");
764 chooser_page_create (ia);
766 gtk_assistant_set_forward_page_func (GTK_ASSISTANT (ia), next_page_func, NULL, NULL);
768 gtk_window_maximize (GTK_WINDOW (ia));
772 /* Appends a page of the given TYPE, with PAGE as its content, to
773 the GtkAssistant encapsulated by IA. Returns the GtkWidget
774 that represents the page. */
776 add_page_to_assistant (PsppireImportAssistant *ia,
777 GtkWidget *page, GtkAssistantPageType type, const gchar *title)
779 GtkWidget *content = page;
781 gtk_assistant_append_page (GTK_ASSISTANT (ia), content);
782 gtk_assistant_set_page_type (GTK_ASSISTANT(ia), content, type);
783 gtk_assistant_set_page_title (GTK_ASSISTANT(ia), content, title);
784 gtk_assistant_set_page_complete (GTK_ASSISTANT(ia), content, TRUE);
790 /* Called when one of the radio buttons is clicked. */
792 on_intro_amount_changed (PsppireImportAssistant *p)
794 gtk_widget_set_sensitive (p->n_cases_spin,
795 gtk_toggle_button_get_active
796 (GTK_TOGGLE_BUTTON (p->n_cases_button)));
798 gtk_widget_set_sensitive (p->percent_spin,
799 gtk_toggle_button_get_active
800 (GTK_TOGGLE_BUTTON (p->percent_button)));
804 on_treeview_selection_change (PsppireImportAssistant *ia)
806 GtkTreeSelection *selection =
807 gtk_tree_view_get_selection (GTK_TREE_VIEW (ia->first_line_tree_view));
808 GtkTreeModel *model = NULL;
810 if (gtk_tree_selection_get_selected (selection, &model, &iter))
814 GtkTreePath *path = gtk_tree_model_get_path (model, &iter);
815 gint *index = gtk_tree_path_get_indices (path);
817 gtk_tree_path_free (path);
818 g_object_get (model, "maximum-lines", &max_lines, NULL);
819 gtk_widget_set_sensitive (ia->variable_names_cb,
820 (n > 0 && n < max_lines));
821 ia->delimiters_model =
822 psppire_delimited_text_new (GTK_TREE_MODEL (ia->text_file));
823 g_object_set (ia->delimiters_model, "first-line", n, NULL);
828 render_text_preview_line (GtkTreeViewColumn *tree_column,
829 GtkCellRenderer *cell,
830 GtkTreeModel *tree_model,
835 Set the text to a "insensitive" state if the row
836 is greater than what the user declared to be the maximum.
838 GtkTreePath *path = gtk_tree_model_get_path (tree_model, iter);
839 gint *ii = gtk_tree_path_get_indices (path);
841 g_object_get (tree_model, "maximum-lines", &max_lines, NULL);
842 g_object_set (cell, "sensitive", (*ii < max_lines), NULL);
843 gtk_tree_path_free (path);
846 /* Initializes IA's first_line substructure. */
848 first_line_page_create (PsppireImportAssistant *ia)
850 GtkWidget *w = get_widget_assert (ia->builder, "FirstLine");
852 g_object_set_data (G_OBJECT (w), "on-entering", on_treeview_selection_change);
854 add_page_to_assistant (ia, w,
855 GTK_ASSISTANT_PAGE_CONTENT, _("Select the First Line"));
857 GtkWidget *scrolled_window = get_widget_assert (ia->builder, "first-line-scroller");
859 if (ia->first_line_tree_view == NULL)
861 ia->first_line_tree_view = gtk_tree_view_new ();
862 g_object_set (ia->first_line_tree_view, "enable-search", FALSE, NULL);
864 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (ia->first_line_tree_view), TRUE);
866 GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
867 GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes (_("Line"), renderer,
871 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 renderer = gtk_cell_renderer_text_new ();
875 column = gtk_tree_view_column_new_with_attributes (_("Text"), renderer, "text", 1, NULL);
876 gtk_tree_view_column_set_cell_data_func (column, renderer, render_text_preview_line, ia, 0);
878 gtk_tree_view_append_column (GTK_TREE_VIEW (ia->first_line_tree_view), column);
880 g_signal_connect_swapped (ia->first_line_tree_view, "cursor-changed",
881 G_CALLBACK (on_treeview_selection_change), ia);
882 gtk_container_add (GTK_CONTAINER (scrolled_window), ia->first_line_tree_view);
885 gtk_widget_show_all (scrolled_window);
887 ia->variable_names_cb = get_widget_assert (ia->builder, "variable-names");
891 intro_on_leave (PsppireImportAssistant *ia)
894 g_object_get (ia->text_file, "line-count", &lc, NULL);
895 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->n_cases_button)))
897 gint max_lines = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (ia->n_cases_spin));
898 g_object_set (ia->text_file, "maximum-lines", max_lines, NULL);
900 else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->percent_button)))
902 gdouble percent = gtk_spin_button_get_value (GTK_SPIN_BUTTON (ia->percent_spin));
903 g_object_set (ia->text_file, "maximum-lines", (gint) (lc * percent / 100.0), NULL);
907 g_object_set (ia->text_file, "maximum-lines", lc, NULL);
913 intro_on_enter (PsppireImportAssistant *ia)
915 GtkBuilder *builder = ia->builder;
916 GtkWidget *table = get_widget_assert (builder, "button-table");
921 ds_put_cstr (&s, _("This assistant will guide you through the process of "
922 "importing data into PSPP from a text file with one line "
923 "per case, in which fields are separated by tabs, "
924 "commas, or other delimiters.\n\n"));
928 if (ia->text_file->total_is_exact)
931 &s, ngettext ("The selected file contains %'lu line of text. ",
932 "The selected file contains %'lu lines of text. ",
933 ia->text_file->total_lines),
934 ia->text_file->total_lines);
936 else if (ia->text_file->total_lines > 0)
940 "The selected file contains approximately %'lu line of text. ",
941 "The selected file contains approximately %'lu lines of text. ",
942 ia->text_file->total_lines),
943 ia->text_file->total_lines);
946 "Only the first %zu line of the file will be shown for "
947 "preview purposes in the following screens. ",
948 "Only the first %zu lines of the file will be shown for "
949 "preview purposes in the following screens. ",
950 ia->text_file->line_cnt),
951 ia->text_file->line_cnt);
955 ds_put_cstr (&s, _("You may choose below how much of the file should "
956 "actually be imported."));
958 gtk_label_set_text (GTK_LABEL (get_widget_assert (builder, "intro-label")),
962 if (gtk_grid_get_child_at (GTK_GRID (table), 1, 1) == NULL)
964 GtkWidget *hbox_n_cases = psppire_scanf_new (_("Only the first %4d cases"), &ia->n_cases_spin);
965 gtk_grid_attach (GTK_GRID (table), hbox_n_cases,
970 GtkAdjustment *adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (ia->n_cases_spin));
971 gtk_adjustment_set_lower (adj, 1.0);
973 if (gtk_grid_get_child_at (GTK_GRID (table), 1, 2) == NULL)
975 GtkWidget *hbox_percent = psppire_scanf_new (_("Only the first %3d %% of file (approximately)"),
978 gtk_grid_attach (GTK_GRID (table), hbox_percent,
983 gtk_widget_show_all (table);
985 on_intro_amount_changed (ia);
988 /* Initializes IA's intro substructure. */
990 intro_page_create (PsppireImportAssistant *ia)
992 GtkBuilder *builder = ia->builder;
994 GtkWidget *w = get_widget_assert (builder, "Intro");
996 ia->percent_spin = gtk_spin_button_new_with_range (0, 100, 10);
999 add_page_to_assistant (ia, w, GTK_ASSISTANT_PAGE_CONTENT, _("Select the Lines to Import"));
1001 ia->all_cases_button = get_widget_assert (builder, "import-all-cases");
1003 ia->n_cases_button = get_widget_assert (builder, "import-n-cases");
1005 ia->percent_button = get_widget_assert (builder, "import-percent");
1007 g_signal_connect_swapped (ia->all_cases_button, "toggled",
1008 G_CALLBACK (on_intro_amount_changed), ia);
1009 g_signal_connect_swapped (ia->n_cases_button, "toggled",
1010 G_CALLBACK (on_intro_amount_changed), ia);
1011 g_signal_connect_swapped (ia->percent_button, "toggled",
1012 G_CALLBACK (on_intro_amount_changed), ia);
1015 g_object_set_data (G_OBJECT (w), "on-forward", intro_on_leave);
1016 g_object_set_data (G_OBJECT (w), "on-entering", intro_on_enter);
1017 g_object_set_data (G_OBJECT (w), "on-reset", reset_intro_page);
1022 psppire_import_assistant_new (GtkWindow *toplevel)
1024 return GTK_WIDGET (g_object_new (PSPPIRE_TYPE_IMPORT_ASSISTANT,
1025 /* Some window managers (notably ratpoison)
1026 ignore the maximise command when a window is
1027 transient. This causes problems for this
1029 /* "transient-for", toplevel, */
1037 /* Chooses a name for each column on the separators page */
1039 choose_column_names (PsppireImportAssistant *ia)
1042 unsigned long int generated_name_count = 0;
1043 dict_clear (ia->dict);
1046 i < gtk_tree_model_get_n_columns (GTK_TREE_MODEL (ia->delimiters_model)) - 1;
1049 const gchar *candidate_name = NULL;
1051 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->variable_names_cb)))
1053 candidate_name = psppire_delimited_text_get_header_title (PSPPIRE_DELIMITED_TEXT (ia->delimiters_model), i);
1056 char *name = dict_make_unique_var_name (ia->dict,
1058 &generated_name_count);
1060 dict_create_var_assert (ia->dict, name, 0);
1065 /* Called when the user toggles one of the separators
1068 on_separator_toggle (GtkToggleButton *toggle UNUSED,
1069 PsppireImportAssistant *ia)
1072 GSList *delimiters = NULL;
1073 for (i = 0; i < SEPARATOR_CNT; i++)
1075 const struct separator *s = &separators[i];
1076 GtkWidget *button = get_widget_assert (ia->builder, s->name);
1077 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
1079 delimiters = g_slist_prepend (delimiters, GINT_TO_POINTER (s->c));
1083 g_object_set (ia->delimiters_model, "delimiters", delimiters, NULL);
1085 revise_fields_preview (ia);
1089 /* Called when the user changes the entry field for custom
1092 on_separators_custom_entry_notify (GObject *gobject UNUSED,
1093 GParamSpec *arg1 UNUSED,
1094 PsppireImportAssistant *ia)
1096 revise_fields_preview (ia);
1099 /* Called when the user toggles the checkbox that enables custom
1102 on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
1103 PsppireImportAssistant *ia)
1105 bool is_active = gtk_toggle_button_get_active (custom_cb);
1106 gtk_widget_set_sensitive (ia->custom_entry, is_active);
1107 revise_fields_preview (ia);
1110 /* Called when the user changes the selection in the combo box
1111 that selects a quote character. */
1113 on_quote_combo_change (GtkComboBox *combo, PsppireImportAssistant *ia)
1115 // revise_fields_preview (ia);
1118 /* Called when the user toggles the checkbox that enables
1121 on_quote_cb_toggle (GtkToggleButton *quote_cb, PsppireImportAssistant *ia)
1123 bool is_active = gtk_toggle_button_get_active (quote_cb);
1124 gtk_widget_set_sensitive (ia->quote_combo, is_active);
1125 revise_fields_preview (ia);
1128 /* Initializes IA's separators substructure. */
1130 separators_page_create (PsppireImportAssistant *ia)
1132 GtkBuilder *builder = ia->builder;
1136 GtkWidget *w = get_widget_assert (builder, "Separators");
1138 g_object_set_data (G_OBJECT (w), "on-entering", prepare_separators_page);
1139 g_object_set_data (G_OBJECT (w), "on-reset", prepare_separators_page);
1141 add_page_to_assistant (ia, w, GTK_ASSISTANT_PAGE_CONTENT, _("Choose Separators"));
1143 ia->custom_cb = get_widget_assert (builder, "custom-cb");
1144 ia->custom_entry = get_widget_assert (builder, "custom-entry");
1145 ia->quote_combo = get_widget_assert (builder, "quote-combo");
1146 ia->quote_cb = get_widget_assert (builder, "quote-cb");
1148 gtk_combo_box_set_active (GTK_COMBO_BOX (ia->quote_combo), 0);
1150 if (ia->fields_tree_view == NULL)
1152 GtkWidget *scroller = get_widget_assert (ia->builder, "fields-scroller");
1153 ia->fields_tree_view = gtk_tree_view_new ();
1154 g_object_set (ia->fields_tree_view, "enable-search", FALSE, NULL);
1155 gtk_container_add (GTK_CONTAINER (scroller), GTK_WIDGET (ia->fields_tree_view));
1156 gtk_widget_show_all (scroller);
1159 g_signal_connect (ia->quote_combo, "changed",
1160 G_CALLBACK (on_quote_combo_change), ia);
1161 g_signal_connect (ia->quote_cb, "toggled",
1162 G_CALLBACK (on_quote_cb_toggle), ia);
1163 g_signal_connect (ia->custom_entry, "notify::text",
1164 G_CALLBACK (on_separators_custom_entry_notify), ia);
1165 g_signal_connect (ia->custom_cb, "toggled",
1166 G_CALLBACK (on_separators_custom_cb_toggle), ia);
1167 for (i = 0; i < SEPARATOR_CNT; i++)
1168 g_signal_connect (get_widget_assert (builder, separators[i].name),
1169 "toggled", G_CALLBACK (on_separator_toggle), ia);
1178 static struct casereader_random_class my_casereader_class;
1180 static struct ccase *
1181 my_read (struct casereader *reader, void *aux, casenumber idx)
1183 PsppireImportAssistant *ia = PSPPIRE_IMPORT_ASSISTANT (aux);
1184 GtkTreeModel *tm = GTK_TREE_MODEL (ia->delimiters_model);
1186 GtkTreePath *tp = gtk_tree_path_new_from_indices (idx, -1);
1188 const struct caseproto *proto = casereader_get_proto (reader);
1191 struct ccase *c = NULL;
1192 if (gtk_tree_model_get_iter (tm, &iter, tp))
1194 c = case_create (proto);
1196 for (i = 0 ; i < caseproto_get_n_widths (proto); ++i)
1199 gtk_tree_model_get_value (tm, &iter, i + 1, &value);
1201 const struct variable *var = dict_get_var (ia->casereader_dict, i);
1203 const gchar *ss = g_value_get_string (&value);
1206 union value *v = case_data_rw (c, var);
1207 /* In this reader we derive the union value from the
1208 string in the tree_model. We retrieve the width and format
1209 from a dictionary which is stored directly after
1210 the reader creation. Changes in ia->dict in the
1211 variable window are not reflected here and therefore
1212 this is always compatible with the width in the
1213 caseproto. See bug #58298 */
1214 char *xx = data_in (ss_cstr (ss),
1216 var_get_write_format (var)->type,
1217 v, var_get_width (var), "UTF-8");
1220 /* g_print ("%s:%d Err %s\n", __FILE__, __LINE__, xx); */
1223 g_value_unset (&value);
1227 gtk_tree_path_free (tp);
1233 my_destroy (struct casereader *reader, void *aux)
1235 g_print ("%s:%d %p\n", __FILE__, __LINE__, reader);
1239 my_advance (struct casereader *reader, void *aux, casenumber cnt)
1241 g_print ("%s:%d\n", __FILE__, __LINE__);
1244 static struct casereader *
1245 textfile_create_reader (PsppireImportAssistant *ia)
1247 int n_vars = dict_get_var_cnt (ia->dict);
1251 struct fmt_guesser **fg = XCALLOC (n_vars, struct fmt_guesser *);
1252 for (i = 0 ; i < n_vars; ++i)
1254 fg[i] = fmt_guesser_create ();
1257 gint n_rows = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (ia->delimiters_model), NULL);
1261 for (ok = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (ia->delimiters_model), &iter);
1263 ok = gtk_tree_model_iter_next (GTK_TREE_MODEL (ia->delimiters_model), &iter))
1265 for (i = 0 ; i < n_vars; ++i)
1268 gtk_tree_model_get (GTK_TREE_MODEL (ia->delimiters_model), &iter, i+1, &s, -1);
1270 fmt_guesser_add (fg[i], ss_cstr (s));
1275 struct caseproto *proto = caseproto_create ();
1276 for (i = 0 ; i < n_vars; ++i)
1279 fmt_guesser_guess (fg[i], &fs);
1281 fmt_fix (&fs, FMT_FOR_INPUT);
1283 struct variable *var = dict_get_var (ia->dict, i);
1285 int width = fmt_var_width (&fs);
1287 var_set_width_and_formats (var, width,
1290 proto = caseproto_add_width (proto, width);
1291 fmt_guesser_destroy (fg[i]);
1296 struct casereader *cr = casereader_create_random (proto, n_rows, &my_casereader_class, ia);
1297 /* Store the dictionary at this point when the casereader is created.
1298 my_read depends on the dictionary to interpret the strings in the treeview.
1299 This guarantees that the union value is produced according to the
1300 caseproto in the reader. */
1301 ia->casereader_dict = dict_clone (ia->dict);
1302 caseproto_unref (proto);
1306 /* When during import the variable type is changed, the reader is reinitialized
1307 based on the new dictionary with a fresh caseprototype. The default behaviour
1308 when a variable type is changed and the column is resized is that the union
1309 value is interpreted with new variable type and an overlay for that column
1310 is generated. Here we reinit to the original reader based on strings.
1311 As a result you can switch from string to numeric to string without loosing
1312 the string information. */
1314 ia_variable_changed_cb (GObject *obj, gint var_num, guint what,
1315 const struct variable *oldvar, gpointer data)
1317 PsppireImportAssistant *ia = PSPPIRE_IMPORT_ASSISTANT (data);
1319 struct caseproto *proto = caseproto_create();
1320 for (int i = 0; i < dict_get_var_cnt (ia->dict); i++)
1322 const struct variable *var = dict_get_var (ia->dict, i);
1323 int width = var_get_width (var);
1324 proto = caseproto_add_width (proto, width);
1327 gint n_rows = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (ia->delimiters_model), NULL);
1329 PsppireDataStore *store = NULL;
1330 g_object_get (ia->data_sheet, "data-model", &store, NULL);
1332 struct casereader *cr = casereader_create_random (proto, n_rows,
1333 &my_casereader_class, ia);
1334 psppire_data_store_set_reader (store, cr);
1335 dict_unref (ia->casereader_dict);
1336 ia->casereader_dict = dict_clone (ia->dict);
1339 /* Called just before the formats page of the assistant is
1342 prepare_formats_page (PsppireImportAssistant *ia)
1344 my_casereader_class.read = my_read;
1345 my_casereader_class.destroy = my_destroy;
1346 my_casereader_class.advance = my_advance;
1348 if (ia->spreadsheet)
1350 GtkBuilder *builder = ia->builder;
1351 GtkWidget *range_entry = get_widget_assert (builder, "cell-range-entry");
1352 GtkWidget *rnc = get_widget_assert (builder, "readnames-checkbox");
1353 GtkWidget *combo_box = get_widget_assert (builder, "sheet-entry");
1355 struct spreadsheet_read_options opts;
1356 opts.sheet_name = NULL;
1357 opts.sheet_index = gtk_combo_box_get_active (GTK_COMBO_BOX (combo_box)) + 1;
1358 opts.read_names = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rnc));
1359 opts.cell_range = g_strdup (gtk_entry_get_text (GTK_ENTRY (range_entry)));
1362 struct casereader *reader = spreadsheet_make_reader (ia->spreadsheet, &opts);
1364 PsppireDict *dict = psppire_dict_new_from_dict (ia->spreadsheet->dict);
1365 PsppireDataStore *store = psppire_data_store_new (dict);
1366 psppire_data_store_set_reader (store, reader);
1367 g_object_set (ia->data_sheet, "data-model", store, NULL);
1368 g_object_set (ia->var_sheet, "data-model", dict, NULL);
1372 struct casereader *reader = textfile_create_reader (ia);
1374 PsppireDict *dict = psppire_dict_new_from_dict (ia->dict);
1375 PsppireDataStore *store = psppire_data_store_new (dict);
1376 psppire_data_store_set_reader (store, reader);
1377 g_signal_connect (dict, "variable-changed",
1378 G_CALLBACK (ia_variable_changed_cb),
1381 g_object_set (ia->data_sheet, "data-model", store, NULL);
1382 g_object_set (ia->var_sheet, "data-model", dict, NULL);
1386 g_object_get (get_widget_assert (ia->builder, "vpaned1"),
1387 "max-position", &pmax, NULL);
1390 g_object_set (get_widget_assert (ia->builder, "vpaned1"),
1391 "position", pmax / 2, NULL);
1393 gtk_widget_show (ia->paste_button);
1397 formats_page_create (PsppireImportAssistant *ia)
1399 GtkBuilder *builder = ia->builder;
1401 GtkWidget *w = get_widget_assert (builder, "Formats");
1402 g_object_set_data (G_OBJECT (w), "on-entering", prepare_formats_page);
1403 g_object_set_data (G_OBJECT (w), "on-reset", reset_formats_page);
1405 GtkWidget *vars_scroller = get_widget_assert (builder, "vars-scroller");
1406 if (ia->var_sheet == NULL)
1408 ia->var_sheet = psppire_variable_sheet_new ();
1410 gtk_container_add (GTK_CONTAINER (vars_scroller), ia->var_sheet);
1412 ia->dict = dict_create (get_default_encoding ());
1414 gtk_widget_show_all (vars_scroller);
1416 GtkWidget *data_scroller = get_widget_assert (builder, "data-scroller");
1417 if (ia->data_sheet == NULL)
1419 ia->data_sheet = psppire_data_sheet_new ();
1420 g_object_set (ia->data_sheet, "editable", FALSE, NULL);
1422 gtk_container_add (GTK_CONTAINER (data_scroller), ia->data_sheet);
1424 gtk_widget_show_all (data_scroller);
1427 add_page_to_assistant (ia, w,
1428 GTK_ASSISTANT_PAGE_CONFIRM, _("Adjust Variable Formats"));
1435 separators_append_syntax (const PsppireImportAssistant *ia, struct string *s)
1439 ds_put_cstr (s, " /DELIMITERS=\"");
1441 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (get_widget_assert (ia->builder, "tab"))))
1442 ds_put_cstr (s, "\\t");
1443 for (i = 0; i < SEPARATOR_CNT; i++)
1445 const struct separator *seps = &separators[i];
1446 GtkWidget *button = get_widget_assert (ia->builder, seps->name);
1447 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
1449 if (seps->c == '\t')
1452 ds_put_byte (s, seps->c);
1455 ds_put_cstr (s, "\"\n");
1457 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->quote_cb)))
1459 GtkComboBoxText *cbt = GTK_COMBO_BOX_TEXT (ia->quote_combo);
1460 gchar *quotes = gtk_combo_box_text_get_active_text (cbt);
1461 if (quotes && *quotes)
1462 syntax_gen_pspp (s, " /QUALIFIER=%sq\n", quotes);
1468 formats_append_syntax (const PsppireImportAssistant *ia, struct string *s)
1473 g_return_if_fail (ia->dict);
1475 ds_put_cstr (s, " /VARIABLES=\n");
1477 var_cnt = dict_get_var_cnt (ia->dict);
1478 for (i = 0; i < var_cnt; i++)
1480 struct variable *var = dict_get_var (ia->dict, i);
1481 char format_string[FMT_STRING_LEN_MAX + 1];
1482 fmt_to_string (var_get_print_format (var), format_string);
1483 ds_put_format (s, " %s %s%s\n",
1484 var_get_name (var), format_string,
1485 i == var_cnt - 1 ? "." : "");
1490 first_line_append_syntax (const PsppireImportAssistant *ia, struct string *s)
1492 gint first_case = 0;
1493 g_object_get (ia->delimiters_model, "first-line", &first_case, NULL);
1496 ds_put_format (s, " /FIRSTCASE=%d\n", first_case + 1);
1500 intro_append_syntax (const PsppireImportAssistant *ia, struct string *s)
1502 gint first_line = 0;
1503 g_object_get (ia->delimiters_model, "first-line", &first_line, NULL);
1505 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->n_cases_button)))
1506 ds_put_format (s, "SELECT IF ($CASENUM <= %d).\n",
1507 gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (ia->n_cases_spin)) - first_line);
1508 else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->percent_button)))
1509 ds_put_format (s, "SAMPLE %.4g.\n",
1510 gtk_spin_button_get_value (GTK_SPIN_BUTTON (ia->percent_spin)) / 100.0);
1514 /* Emits PSPP syntax to S that applies the dictionary attributes
1515 (such as missing values and value labels) of the variables in
1518 apply_dict (const struct dictionary *dict, struct string *s)
1520 size_t var_cnt = dict_get_var_cnt (dict);
1523 for (i = 0; i < var_cnt; i++)
1525 struct variable *var = dict_get_var (dict, i);
1526 const char *name = var_get_name (var);
1527 enum val_type type = var_get_type (var);
1528 int width = var_get_width (var);
1529 enum measure measure = var_get_measure (var);
1530 enum var_role role = var_get_role (var);
1531 enum alignment alignment = var_get_alignment (var);
1532 const struct fmt_spec *format = var_get_print_format (var);
1534 if (var_has_missing_values (var))
1536 const struct missing_values *mv = var_get_missing_values (var);
1539 syntax_gen_pspp (s, "MISSING VALUES %ss (", name);
1540 for (j = 0; j < mv_n_values (mv); j++)
1543 ds_put_cstr (s, ", ");
1544 syntax_gen_value (s, mv_get_value (mv, j), width, format);
1547 if (mv_has_range (mv))
1550 if (mv_has_value (mv))
1551 ds_put_cstr (s, ", ");
1552 mv_get_range (mv, &low, &high);
1553 syntax_gen_num_range (s, low, high, format);
1555 ds_put_cstr (s, ").\n");
1557 if (var_has_value_labels (var))
1559 const struct val_labs *vls = var_get_value_labels (var);
1560 const struct val_lab **labels = val_labs_sorted (vls);
1561 size_t n_labels = val_labs_count (vls);
1564 syntax_gen_pspp (s, "VALUE LABELS %ss", name);
1565 for (i = 0; i < n_labels; i++)
1567 const struct val_lab *vl = labels[i];
1568 ds_put_cstr (s, "\n ");
1569 syntax_gen_value (s, &vl->value, width, format);
1570 ds_put_byte (s, ' ');
1571 syntax_gen_string (s, ss_cstr (val_lab_get_escaped_label (vl)));
1574 ds_put_cstr (s, ".\n");
1576 if (var_has_label (var))
1577 syntax_gen_pspp (s, "VARIABLE LABELS %ss %sq.\n",
1578 name, var_get_label (var));
1579 if (measure != var_default_measure (type))
1580 syntax_gen_pspp (s, "VARIABLE LEVEL %ss (%ss).\n",
1581 name, measure_to_syntax (measure));
1582 if (role != ROLE_INPUT)
1583 syntax_gen_pspp (s, "VARIABLE ROLE /%ss %ss.\n",
1584 var_role_to_syntax (role), name);
1585 if (alignment != var_default_alignment (type))
1586 syntax_gen_pspp (s, "VARIABLE ALIGNMENT %ss (%ss).\n",
1587 name, alignment_to_syntax (alignment));
1588 if (var_get_display_width (var) != var_default_display_width (width))
1589 syntax_gen_pspp (s, "VARIABLE WIDTH %ss (%d).\n",
1590 name, var_get_display_width (var));
1597 sheet_spec_gen_syntax (PsppireImportAssistant *ia, struct string *s)
1599 GtkBuilder *builder = ia->builder;
1600 GtkWidget *range_entry = get_widget_assert (builder, "cell-range-entry");
1601 GtkWidget *sheet_entry = get_widget_assert (builder, "sheet-entry");
1602 GtkWidget *rnc = get_widget_assert (builder, "readnames-checkbox");
1603 const gchar *range = gtk_entry_get_text (GTK_ENTRY (range_entry));
1604 int sheet_index = 1 + gtk_combo_box_get_active (GTK_COMBO_BOX (sheet_entry));
1605 gboolean read_names = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rnc));
1609 if (ia->spreadsheet)
1610 filename = ia->spreadsheet->file_name;
1612 g_object_get (ia->text_file, "file-name", &filename, NULL);
1617 "\n /SHEET=index %d"
1618 "\n /READNAMES=%ss",
1619 (ia->spreadsheet->type == SPREADSHEET_GNUMERIC) ? "GNM" : "ODS",
1622 read_names ? "ON" : "OFF");
1624 if (range && 0 != strcmp ("", range))
1627 "\n /CELLRANGE=RANGE %sq", range);
1632 "\n /CELLRANGE=FULL");
1636 syntax_gen_pspp (s, ".\n");
1641 psppire_import_assistant_generate_syntax (PsppireImportAssistant *ia)
1643 struct string s = DS_EMPTY_INITIALIZER;
1645 if (!ia->spreadsheet)
1647 gchar *file_name = NULL;
1648 gchar *encoding = NULL;
1649 g_object_get (ia->text_file,
1650 "file-name", &file_name,
1651 "encoding", &encoding,
1654 if (file_name == NULL)
1657 syntax_gen_pspp (&s,
1662 if (encoding && strcmp (encoding, "Auto"))
1663 syntax_gen_pspp (&s, " /ENCODING=%sq\n", encoding);
1666 " /ARRANGEMENT=DELIMITED\n"
1667 " /DELCASE=LINE\n");
1669 first_line_append_syntax (ia, &s);
1670 separators_append_syntax (ia, &s);
1672 formats_append_syntax (ia, &s);
1673 apply_dict (ia->dict, &s);
1674 intro_append_syntax (ia, &s);
1678 sheet_spec_gen_syntax (ia, &s);
1681 return ds_cstr (&s);
1686 psppire_import_assistant_run (PsppireImportAssistant *asst)
1688 g_main_loop_run (asst->main_loop);
1689 return asst->response;