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 GtkTreeModel *model = psppire_spreadsheet_model_new (ia->spreadsheet);
531 gtk_combo_box_set_model (GTK_COMBO_BOX (sheet_entry), model);
533 gint items = gtk_tree_model_iter_n_children (model, NULL);
534 gtk_widget_set_sensitive (sheet_entry, items > 1);
536 gtk_combo_box_set_active (GTK_COMBO_BOX (sheet_entry), 0);
538 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (readnames_checkbox), FALSE);
542 /* Initializes IA's sheet_spec substructure. */
544 sheet_spec_page_create (PsppireImportAssistant *ia)
546 GtkBuilder *builder = ia->builder;
547 GtkWidget *page = get_widget_assert (builder, "Spreadsheet-Importer");
549 GtkWidget *combo_box = get_widget_assert (builder, "sheet-entry");
550 GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
551 gtk_cell_layout_clear (GTK_CELL_LAYOUT (combo_box));
552 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), renderer, TRUE);
553 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), renderer,
557 g_signal_connect (combo_box, "changed", G_CALLBACK (on_sheet_combo_changed), ia);
559 add_page_to_assistant (ia, page,
560 GTK_ASSISTANT_PAGE_CONTENT, _("Importing Spreadsheet Data"));
562 g_object_set_data (G_OBJECT (page), "on-entering", prepare_sheet_spec_page);
566 on_chosen (PsppireImportAssistant *ia, GtkWidget *page)
568 GtkFileChooser *fc = GTK_FILE_CHOOSER (page);
569 gchar *f = gtk_file_chooser_get_filename (fc);
572 for(i = gtk_assistant_get_n_pages (GTK_ASSISTANT (ia)); i > 0; --i)
573 gtk_assistant_remove_page (GTK_ASSISTANT (ia), i);
575 gtk_assistant_set_page_complete (GTK_ASSISTANT(ia), GTK_WIDGET (fc), FALSE);
577 if (f && g_file_test (f, G_FILE_TEST_IS_REGULAR))
579 gtk_assistant_set_page_complete (GTK_ASSISTANT(ia), GTK_WIDGET (fc), TRUE);
582 spreadsheet_unref (ia->spreadsheet);
584 ia->spreadsheet = gnumeric_probe (f, FALSE);
586 if (!ia->spreadsheet)
587 ia->spreadsheet = ods_probe (f, FALSE);
591 sheet_spec_page_create (ia);
595 intro_page_create (ia);
596 first_line_page_create (ia);
597 separators_page_create (ia);
600 formats_page_create (ia);
606 /* This has to be done on a map signal callback,
607 because GtkFileChooserWidget resets everything when it is mapped. */
609 on_map (PsppireImportAssistant *ia, GtkWidget *page)
612 GtkFileChooser *fc = GTK_FILE_CHOOSER (page);
615 gtk_file_chooser_set_filename (fc, ia->file_name);
618 on_chosen (ia, page);
624 chooser_page_enter (PsppireImportAssistant *ia, GtkWidget *page)
629 chooser_page_leave (PsppireImportAssistant *ia, GtkWidget *page)
631 g_free (ia->file_name);
632 ia->file_name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (page));
633 gchar *encoding = psppire_encoding_selector_get_encoding (ia->encoding_selector);
635 if (!ia->spreadsheet)
637 ia->text_file = psppire_text_file_new (ia->file_name, encoding);
638 gtk_tree_view_set_model (GTK_TREE_VIEW (ia->first_line_tree_view),
639 GTK_TREE_MODEL (ia->text_file));
647 chooser_page_reset (PsppireImportAssistant *ia, GtkWidget *page)
649 GtkFileChooser *fc = GTK_FILE_CHOOSER (page);
651 gtk_file_chooser_set_filter (fc, ia->default_filter);
652 gtk_file_chooser_unselect_all (fc);
654 on_chosen (ia, page);
659 on_file_activated (GtkFileChooser *chooser, PsppireImportAssistant *ia)
661 gtk_assistant_next_page (GTK_ASSISTANT (ia));
665 chooser_page_create (PsppireImportAssistant *ia)
667 GtkFileFilter *filter = NULL;
669 GtkWidget *chooser = gtk_file_chooser_widget_new (GTK_FILE_CHOOSER_ACTION_OPEN);
671 g_signal_connect (chooser, "file-activated", G_CALLBACK (on_file_activated), ia);
673 g_object_set_data (G_OBJECT (chooser), "on-forward", chooser_page_leave);
674 g_object_set_data (G_OBJECT (chooser), "on-reset", chooser_page_reset);
675 g_object_set_data (G_OBJECT (chooser), "on-entering",chooser_page_enter);
677 g_object_set (chooser, "local-only", FALSE, NULL);
680 ia->default_filter = gtk_file_filter_new ();
681 gtk_file_filter_set_name (ia->default_filter, _("All Files"));
682 gtk_file_filter_add_pattern (ia->default_filter, "*");
683 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), ia->default_filter);
685 filter = gtk_file_filter_new ();
686 gtk_file_filter_set_name (filter, _("Text Files"));
687 gtk_file_filter_add_mime_type (filter, "text/*");
688 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
690 filter = gtk_file_filter_new ();
691 gtk_file_filter_set_name (filter, _("Text (*.txt) Files"));
692 gtk_file_filter_add_pattern (filter, "*.txt");
693 gtk_file_filter_add_pattern (filter, "*.TXT");
694 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
696 filter = gtk_file_filter_new ();
697 gtk_file_filter_set_name (filter, _("Plain Text (ASCII) Files"));
698 gtk_file_filter_add_mime_type (filter, "text/plain");
699 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
701 filter = gtk_file_filter_new ();
702 gtk_file_filter_set_name (filter, _("Comma Separated Value Files"));
703 gtk_file_filter_add_mime_type (filter, "text/csv");
704 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
706 /* I've never encountered one of these, but it's listed here:
707 http://www.iana.org/assignments/media-types/text/tab-separated-values */
708 filter = gtk_file_filter_new ();
709 gtk_file_filter_set_name (filter, _("Tab Separated Value Files"));
710 gtk_file_filter_add_mime_type (filter, "text/tab-separated-values");
711 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
713 filter = gtk_file_filter_new ();
714 gtk_file_filter_set_name (filter, _("Gnumeric Spreadsheet Files"));
715 gtk_file_filter_add_mime_type (filter, "application/x-gnumeric");
716 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
718 filter = gtk_file_filter_new ();
719 gtk_file_filter_set_name (filter, _("OpenDocument Spreadsheet Files"));
720 gtk_file_filter_add_mime_type (filter, "application/vnd.oasis.opendocument.spreadsheet");
721 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
723 filter = gtk_file_filter_new ();
724 gtk_file_filter_set_name (filter, _("All Spreadsheet Files"));
725 gtk_file_filter_add_mime_type (filter, "application/x-gnumeric");
726 gtk_file_filter_add_mime_type (filter, "application/vnd.oasis.opendocument.spreadsheet");
727 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
729 ia->encoding_selector = psppire_encoding_selector_new ("Auto", TRUE);
730 gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (chooser), ia->encoding_selector);
732 add_page_to_assistant (ia, chooser,
733 GTK_ASSISTANT_PAGE_INTRO, _("Select File to Import"));
735 g_signal_connect_swapped (chooser, "selection-changed", G_CALLBACK (on_chosen), ia);
736 g_signal_connect_swapped (chooser, "map", G_CALLBACK (on_map), ia);
742 psppire_import_assistant_init (PsppireImportAssistant *ia)
744 ia->builder = builder_new ("text-data-import.ui");
746 ia->current_page = -1 ;
747 ia->file_name = NULL;
749 ia->spreadsheet = NULL;
751 ia->casereader_dict = NULL;
753 ia->main_loop = g_main_loop_new (NULL, TRUE);
755 g_signal_connect (ia, "prepare", G_CALLBACK (on_prepare), ia);
756 g_signal_connect (ia, "cancel", G_CALLBACK (on_cancel), ia);
757 g_signal_connect (ia, "close", G_CALLBACK (on_close), ia);
759 ia->paste_button = gtk_button_new_with_label (_("Paste"));
760 ia->reset_button = gtk_button_new_with_label (_("Reset"));
762 gtk_assistant_add_action_widget (GTK_ASSISTANT(ia), ia->paste_button);
764 g_signal_connect (ia->paste_button, "clicked", G_CALLBACK (on_paste), ia);
765 g_signal_connect (ia->reset_button, "clicked", G_CALLBACK (on_reset), ia);
767 gtk_assistant_add_action_widget (GTK_ASSISTANT(ia), ia->reset_button);
769 gtk_window_set_title (GTK_WINDOW (ia),
770 _("Importing Delimited Text Data"));
772 gtk_window_set_icon_name (GTK_WINDOW (ia), "pspp");
774 chooser_page_create (ia);
776 gtk_assistant_set_forward_page_func (GTK_ASSISTANT (ia), next_page_func, NULL, NULL);
778 gtk_window_maximize (GTK_WINDOW (ia));
782 /* Appends a page of the given TYPE, with PAGE as its content, to
783 the GtkAssistant encapsulated by IA. Returns the GtkWidget
784 that represents the page. */
786 add_page_to_assistant (PsppireImportAssistant *ia,
787 GtkWidget *page, GtkAssistantPageType type, const gchar *title)
789 GtkWidget *content = page;
791 gtk_assistant_append_page (GTK_ASSISTANT (ia), content);
792 gtk_assistant_set_page_type (GTK_ASSISTANT(ia), content, type);
793 gtk_assistant_set_page_title (GTK_ASSISTANT(ia), content, title);
794 gtk_assistant_set_page_complete (GTK_ASSISTANT(ia), content, TRUE);
800 /* Called when one of the radio buttons is clicked. */
802 on_intro_amount_changed (PsppireImportAssistant *p)
804 gtk_widget_set_sensitive (p->n_cases_spin,
805 gtk_toggle_button_get_active
806 (GTK_TOGGLE_BUTTON (p->n_cases_button)));
808 gtk_widget_set_sensitive (p->percent_spin,
809 gtk_toggle_button_get_active
810 (GTK_TOGGLE_BUTTON (p->percent_button)));
814 on_treeview_selection_change (PsppireImportAssistant *ia)
816 GtkTreeSelection *selection =
817 gtk_tree_view_get_selection (GTK_TREE_VIEW (ia->first_line_tree_view));
818 GtkTreeModel *model = NULL;
820 if (gtk_tree_selection_get_selected (selection, &model, &iter))
824 GtkTreePath *path = gtk_tree_model_get_path (model, &iter);
825 gint *index = gtk_tree_path_get_indices (path);
827 gtk_tree_path_free (path);
828 g_object_get (model, "maximum-lines", &max_lines, NULL);
829 gtk_widget_set_sensitive (ia->variable_names_cb,
830 (n > 0 && n < max_lines));
831 ia->delimiters_model =
832 psppire_delimited_text_new (GTK_TREE_MODEL (ia->text_file));
833 g_object_set (ia->delimiters_model, "first-line", n, NULL);
838 render_text_preview_line (GtkTreeViewColumn *tree_column,
839 GtkCellRenderer *cell,
840 GtkTreeModel *tree_model,
845 Set the text to a "insensitive" state if the row
846 is greater than what the user declared to be the maximum.
848 GtkTreePath *path = gtk_tree_model_get_path (tree_model, iter);
849 gint *ii = gtk_tree_path_get_indices (path);
851 g_object_get (tree_model, "maximum-lines", &max_lines, NULL);
852 g_object_set (cell, "sensitive", (*ii < max_lines), NULL);
853 gtk_tree_path_free (path);
856 /* Initializes IA's first_line substructure. */
858 first_line_page_create (PsppireImportAssistant *ia)
860 GtkWidget *w = get_widget_assert (ia->builder, "FirstLine");
862 g_object_set_data (G_OBJECT (w), "on-entering", on_treeview_selection_change);
864 add_page_to_assistant (ia, w,
865 GTK_ASSISTANT_PAGE_CONTENT, _("Select the First Line"));
867 GtkWidget *scrolled_window = get_widget_assert (ia->builder, "first-line-scroller");
869 if (ia->first_line_tree_view == NULL)
871 ia->first_line_tree_view = gtk_tree_view_new ();
872 g_object_set (ia->first_line_tree_view, "enable-search", FALSE, NULL);
874 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (ia->first_line_tree_view), TRUE);
876 GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
877 GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes (_("Line"), renderer,
881 gtk_tree_view_column_set_cell_data_func (column, renderer, render_text_preview_line, ia, 0);
882 gtk_tree_view_append_column (GTK_TREE_VIEW (ia->first_line_tree_view), column);
884 renderer = gtk_cell_renderer_text_new ();
885 column = gtk_tree_view_column_new_with_attributes (_("Text"), renderer, "text", 1, NULL);
886 gtk_tree_view_column_set_cell_data_func (column, renderer, render_text_preview_line, ia, 0);
888 gtk_tree_view_append_column (GTK_TREE_VIEW (ia->first_line_tree_view), column);
890 g_signal_connect_swapped (ia->first_line_tree_view, "cursor-changed",
891 G_CALLBACK (on_treeview_selection_change), ia);
892 gtk_container_add (GTK_CONTAINER (scrolled_window), ia->first_line_tree_view);
895 gtk_widget_show_all (scrolled_window);
897 ia->variable_names_cb = get_widget_assert (ia->builder, "variable-names");
901 intro_on_leave (PsppireImportAssistant *ia)
904 g_object_get (ia->text_file, "line-count", &lc, NULL);
905 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->n_cases_button)))
907 gint max_lines = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (ia->n_cases_spin));
908 g_object_set (ia->text_file, "maximum-lines", max_lines, NULL);
910 else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->percent_button)))
912 gdouble percent = gtk_spin_button_get_value (GTK_SPIN_BUTTON (ia->percent_spin));
913 g_object_set (ia->text_file, "maximum-lines", (gint) (lc * percent / 100.0), NULL);
917 g_object_set (ia->text_file, "maximum-lines", lc, NULL);
923 intro_on_enter (PsppireImportAssistant *ia)
925 GtkBuilder *builder = ia->builder;
926 GtkWidget *table = get_widget_assert (builder, "button-table");
931 ds_put_cstr (&s, _("This assistant will guide you through the process of "
932 "importing data into PSPP from a text file with one line "
933 "per case, in which fields are separated by tabs, "
934 "commas, or other delimiters.\n\n"));
938 if (ia->text_file->total_is_exact)
941 &s, ngettext ("The selected file contains %'lu line of text. ",
942 "The selected file contains %'lu lines of text. ",
943 ia->text_file->total_lines),
944 ia->text_file->total_lines);
946 else if (ia->text_file->total_lines > 0)
950 "The selected file contains approximately %'lu line of text. ",
951 "The selected file contains approximately %'lu lines of text. ",
952 ia->text_file->total_lines),
953 ia->text_file->total_lines);
956 "Only the first %zu line of the file will be shown for "
957 "preview purposes in the following screens. ",
958 "Only the first %zu lines of the file will be shown for "
959 "preview purposes in the following screens. ",
960 ia->text_file->line_cnt),
961 ia->text_file->line_cnt);
965 ds_put_cstr (&s, _("You may choose below how much of the file should "
966 "actually be imported."));
968 gtk_label_set_text (GTK_LABEL (get_widget_assert (builder, "intro-label")),
972 if (gtk_grid_get_child_at (GTK_GRID (table), 1, 1) == NULL)
974 GtkWidget *hbox_n_cases = psppire_scanf_new (_("Only the first %4d cases"), &ia->n_cases_spin);
975 gtk_grid_attach (GTK_GRID (table), hbox_n_cases,
980 GtkAdjustment *adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (ia->n_cases_spin));
981 gtk_adjustment_set_lower (adj, 1.0);
983 if (gtk_grid_get_child_at (GTK_GRID (table), 1, 2) == NULL)
985 GtkWidget *hbox_percent = psppire_scanf_new (_("Only the first %3d %% of file (approximately)"),
988 gtk_grid_attach (GTK_GRID (table), hbox_percent,
993 gtk_widget_show_all (table);
995 on_intro_amount_changed (ia);
998 /* Initializes IA's intro substructure. */
1000 intro_page_create (PsppireImportAssistant *ia)
1002 GtkBuilder *builder = ia->builder;
1004 GtkWidget *w = get_widget_assert (builder, "Intro");
1006 ia->percent_spin = gtk_spin_button_new_with_range (0, 100, 10);
1009 add_page_to_assistant (ia, w, GTK_ASSISTANT_PAGE_CONTENT, _("Select the Lines to Import"));
1011 ia->all_cases_button = get_widget_assert (builder, "import-all-cases");
1013 ia->n_cases_button = get_widget_assert (builder, "import-n-cases");
1015 ia->percent_button = get_widget_assert (builder, "import-percent");
1017 g_signal_connect_swapped (ia->all_cases_button, "toggled",
1018 G_CALLBACK (on_intro_amount_changed), ia);
1019 g_signal_connect_swapped (ia->n_cases_button, "toggled",
1020 G_CALLBACK (on_intro_amount_changed), ia);
1021 g_signal_connect_swapped (ia->percent_button, "toggled",
1022 G_CALLBACK (on_intro_amount_changed), ia);
1025 g_object_set_data (G_OBJECT (w), "on-forward", intro_on_leave);
1026 g_object_set_data (G_OBJECT (w), "on-entering", intro_on_enter);
1027 g_object_set_data (G_OBJECT (w), "on-reset", reset_intro_page);
1032 psppire_import_assistant_new (GtkWindow *toplevel)
1034 return GTK_WIDGET (g_object_new (PSPPIRE_TYPE_IMPORT_ASSISTANT,
1035 /* Some window managers (notably ratpoison)
1036 ignore the maximise command when a window is
1037 transient. This causes problems for this
1039 /* "transient-for", toplevel, */
1047 /* Chooses a name for each column on the separators page */
1049 choose_column_names (PsppireImportAssistant *ia)
1052 unsigned long int generated_name_count = 0;
1053 dict_clear (ia->dict);
1056 i < gtk_tree_model_get_n_columns (GTK_TREE_MODEL (ia->delimiters_model)) - 1;
1059 const gchar *candidate_name = NULL;
1061 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->variable_names_cb)))
1063 candidate_name = psppire_delimited_text_get_header_title (PSPPIRE_DELIMITED_TEXT (ia->delimiters_model), i);
1066 char *name = dict_make_unique_var_name (ia->dict,
1068 &generated_name_count);
1070 dict_create_var_assert (ia->dict, name, 0);
1075 /* Called when the user toggles one of the separators
1078 on_separator_toggle (GtkToggleButton *toggle UNUSED,
1079 PsppireImportAssistant *ia)
1082 GSList *delimiters = NULL;
1083 for (i = 0; i < SEPARATOR_CNT; i++)
1085 const struct separator *s = &separators[i];
1086 GtkWidget *button = get_widget_assert (ia->builder, s->name);
1087 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
1089 delimiters = g_slist_prepend (delimiters, GINT_TO_POINTER (s->c));
1093 g_object_set (ia->delimiters_model, "delimiters", delimiters, NULL);
1095 revise_fields_preview (ia);
1099 /* Called when the user changes the entry field for custom
1102 on_separators_custom_entry_notify (GObject *gobject UNUSED,
1103 GParamSpec *arg1 UNUSED,
1104 PsppireImportAssistant *ia)
1106 revise_fields_preview (ia);
1109 /* Called when the user toggles the checkbox that enables custom
1112 on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
1113 PsppireImportAssistant *ia)
1115 bool is_active = gtk_toggle_button_get_active (custom_cb);
1116 gtk_widget_set_sensitive (ia->custom_entry, is_active);
1117 revise_fields_preview (ia);
1120 /* Called when the user changes the selection in the combo box
1121 that selects a quote character. */
1123 on_quote_combo_change (GtkComboBox *combo, PsppireImportAssistant *ia)
1125 // revise_fields_preview (ia);
1128 /* Called when the user toggles the checkbox that enables
1131 on_quote_cb_toggle (GtkToggleButton *quote_cb, PsppireImportAssistant *ia)
1133 bool is_active = gtk_toggle_button_get_active (quote_cb);
1134 gtk_widget_set_sensitive (ia->quote_combo, is_active);
1135 revise_fields_preview (ia);
1138 /* Initializes IA's separators substructure. */
1140 separators_page_create (PsppireImportAssistant *ia)
1142 GtkBuilder *builder = ia->builder;
1146 GtkWidget *w = get_widget_assert (builder, "Separators");
1148 g_object_set_data (G_OBJECT (w), "on-entering", prepare_separators_page);
1149 g_object_set_data (G_OBJECT (w), "on-reset", prepare_separators_page);
1151 add_page_to_assistant (ia, w, GTK_ASSISTANT_PAGE_CONTENT, _("Choose Separators"));
1153 ia->custom_cb = get_widget_assert (builder, "custom-cb");
1154 ia->custom_entry = get_widget_assert (builder, "custom-entry");
1155 ia->quote_combo = get_widget_assert (builder, "quote-combo");
1156 ia->quote_cb = get_widget_assert (builder, "quote-cb");
1158 gtk_combo_box_set_active (GTK_COMBO_BOX (ia->quote_combo), 0);
1160 if (ia->fields_tree_view == NULL)
1162 GtkWidget *scroller = get_widget_assert (ia->builder, "fields-scroller");
1163 ia->fields_tree_view = gtk_tree_view_new ();
1164 g_object_set (ia->fields_tree_view, "enable-search", FALSE, NULL);
1165 gtk_container_add (GTK_CONTAINER (scroller), GTK_WIDGET (ia->fields_tree_view));
1166 gtk_widget_show_all (scroller);
1169 g_signal_connect (ia->quote_combo, "changed",
1170 G_CALLBACK (on_quote_combo_change), ia);
1171 g_signal_connect (ia->quote_cb, "toggled",
1172 G_CALLBACK (on_quote_cb_toggle), ia);
1173 g_signal_connect (ia->custom_entry, "notify::text",
1174 G_CALLBACK (on_separators_custom_entry_notify), ia);
1175 g_signal_connect (ia->custom_cb, "toggled",
1176 G_CALLBACK (on_separators_custom_cb_toggle), ia);
1177 for (i = 0; i < SEPARATOR_CNT; i++)
1178 g_signal_connect (get_widget_assert (builder, separators[i].name),
1179 "toggled", G_CALLBACK (on_separator_toggle), ia);
1188 static struct casereader_random_class my_casereader_class;
1190 static struct ccase *
1191 my_read (struct casereader *reader, void *aux, casenumber idx)
1193 PsppireImportAssistant *ia = PSPPIRE_IMPORT_ASSISTANT (aux);
1194 GtkTreeModel *tm = GTK_TREE_MODEL (ia->delimiters_model);
1196 GtkTreePath *tp = gtk_tree_path_new_from_indices (idx, -1);
1198 const struct caseproto *proto = casereader_get_proto (reader);
1201 struct ccase *c = NULL;
1202 if (gtk_tree_model_get_iter (tm, &iter, tp))
1204 c = case_create (proto);
1206 for (i = 0 ; i < caseproto_get_n_widths (proto); ++i)
1209 gtk_tree_model_get_value (tm, &iter, i + 1, &value);
1211 const struct variable *var = dict_get_var (ia->casereader_dict, i);
1213 const gchar *ss = g_value_get_string (&value);
1216 union value *v = case_data_rw (c, var);
1217 /* In this reader we derive the union value from the
1218 string in the tree_model. We retrieve the width and format
1219 from a dictionary which is stored directly after
1220 the reader creation. Changes in ia->dict in the
1221 variable window are not reflected here and therefore
1222 this is always compatible with the width in the
1223 caseproto. See bug #58298 */
1224 char *xx = data_in (ss_cstr (ss),
1226 var_get_write_format (var)->type,
1227 v, var_get_width (var), "UTF-8");
1230 /* g_print ("%s:%d Err %s\n", __FILE__, __LINE__, xx); */
1233 g_value_unset (&value);
1237 gtk_tree_path_free (tp);
1243 my_destroy (struct casereader *reader, void *aux)
1245 g_print ("%s:%d %p\n", __FILE__, __LINE__, reader);
1249 my_advance (struct casereader *reader, void *aux, casenumber cnt)
1251 g_print ("%s:%d\n", __FILE__, __LINE__);
1254 static struct casereader *
1255 textfile_create_reader (PsppireImportAssistant *ia)
1257 int n_vars = dict_get_var_cnt (ia->dict);
1261 struct fmt_guesser **fg = XCALLOC (n_vars, struct fmt_guesser *);
1262 for (i = 0 ; i < n_vars; ++i)
1264 fg[i] = fmt_guesser_create ();
1267 gint n_rows = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (ia->delimiters_model), NULL);
1271 for (ok = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (ia->delimiters_model), &iter);
1273 ok = gtk_tree_model_iter_next (GTK_TREE_MODEL (ia->delimiters_model), &iter))
1275 for (i = 0 ; i < n_vars; ++i)
1278 gtk_tree_model_get (GTK_TREE_MODEL (ia->delimiters_model), &iter, i+1, &s, -1);
1280 fmt_guesser_add (fg[i], ss_cstr (s));
1285 struct caseproto *proto = caseproto_create ();
1286 for (i = 0 ; i < n_vars; ++i)
1289 fmt_guesser_guess (fg[i], &fs);
1291 fmt_fix (&fs, FMT_FOR_INPUT);
1293 struct variable *var = dict_get_var (ia->dict, i);
1295 int width = fmt_var_width (&fs);
1297 var_set_width_and_formats (var, width,
1300 proto = caseproto_add_width (proto, width);
1301 fmt_guesser_destroy (fg[i]);
1306 struct casereader *cr = casereader_create_random (proto, n_rows, &my_casereader_class, ia);
1307 /* Store the dictionary at this point when the casereader is created.
1308 my_read depends on the dictionary to interpret the strings in the treeview.
1309 This guarantees that the union value is produced according to the
1310 caseproto in the reader. */
1311 ia->casereader_dict = dict_clone (ia->dict);
1312 caseproto_unref (proto);
1316 /* When during import the variable type is changed, the reader is reinitialized
1317 based on the new dictionary with a fresh caseprototype. The default behaviour
1318 when a variable type is changed and the column is resized is that the union
1319 value is interpreted with new variable type and an overlay for that column
1320 is generated. Here we reinit to the original reader based on strings.
1321 As a result you can switch from string to numeric to string without loosing
1322 the string information. */
1324 ia_variable_changed_cb (GObject *obj, gint var_num, guint what,
1325 const struct variable *oldvar, gpointer data)
1327 PsppireImportAssistant *ia = PSPPIRE_IMPORT_ASSISTANT (data);
1329 struct caseproto *proto = caseproto_create();
1330 for (int i = 0; i < dict_get_var_cnt (ia->dict); i++)
1332 const struct variable *var = dict_get_var (ia->dict, i);
1333 int width = var_get_width (var);
1334 proto = caseproto_add_width (proto, width);
1337 gint n_rows = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (ia->delimiters_model), NULL);
1339 PsppireDataStore *store = NULL;
1340 g_object_get (ia->data_sheet, "data-model", &store, NULL);
1342 struct casereader *cr = casereader_create_random (proto, n_rows,
1343 &my_casereader_class, ia);
1344 psppire_data_store_set_reader (store, cr);
1345 dict_unref (ia->casereader_dict);
1346 ia->casereader_dict = dict_clone (ia->dict);
1349 /* Called just before the formats page of the assistant is
1352 prepare_formats_page (PsppireImportAssistant *ia)
1354 my_casereader_class.read = my_read;
1355 my_casereader_class.destroy = my_destroy;
1356 my_casereader_class.advance = my_advance;
1358 if (ia->spreadsheet)
1360 GtkBuilder *builder = ia->builder;
1361 GtkWidget *range_entry = get_widget_assert (builder, "cell-range-entry");
1362 GtkWidget *rnc = get_widget_assert (builder, "readnames-checkbox");
1363 GtkWidget *combo_box = get_widget_assert (builder, "sheet-entry");
1365 struct spreadsheet_read_options opts;
1366 opts.sheet_name = NULL;
1367 opts.sheet_index = gtk_combo_box_get_active (GTK_COMBO_BOX (combo_box)) + 1;
1368 opts.read_names = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rnc));
1369 opts.cell_range = g_strdup (gtk_entry_get_text (GTK_ENTRY (range_entry)));
1372 struct casereader *reader = spreadsheet_make_reader (ia->spreadsheet, &opts);
1374 PsppireDict *dict = psppire_dict_new_from_dict (ia->spreadsheet->dict);
1375 PsppireDataStore *store = psppire_data_store_new (dict);
1376 psppire_data_store_set_reader (store, reader);
1377 g_object_set (ia->data_sheet, "data-model", store, NULL);
1378 g_object_set (ia->var_sheet, "data-model", dict, NULL);
1382 struct casereader *reader = textfile_create_reader (ia);
1384 PsppireDict *dict = psppire_dict_new_from_dict (ia->dict);
1385 PsppireDataStore *store = psppire_data_store_new (dict);
1386 psppire_data_store_set_reader (store, reader);
1387 g_signal_connect (dict, "variable-changed",
1388 G_CALLBACK (ia_variable_changed_cb),
1391 g_object_set (ia->data_sheet, "data-model", store, NULL);
1392 g_object_set (ia->var_sheet, "data-model", dict, NULL);
1396 g_object_get (get_widget_assert (ia->builder, "vpaned1"),
1397 "max-position", &pmax, NULL);
1400 g_object_set (get_widget_assert (ia->builder, "vpaned1"),
1401 "position", pmax / 2, NULL);
1403 gtk_widget_show (ia->paste_button);
1407 formats_page_create (PsppireImportAssistant *ia)
1409 GtkBuilder *builder = ia->builder;
1411 GtkWidget *w = get_widget_assert (builder, "Formats");
1412 g_object_set_data (G_OBJECT (w), "on-entering", prepare_formats_page);
1413 g_object_set_data (G_OBJECT (w), "on-reset", reset_formats_page);
1415 GtkWidget *vars_scroller = get_widget_assert (builder, "vars-scroller");
1416 if (ia->var_sheet == NULL)
1418 ia->var_sheet = psppire_variable_sheet_new ();
1420 gtk_container_add (GTK_CONTAINER (vars_scroller), ia->var_sheet);
1422 ia->dict = dict_create (get_default_encoding ());
1424 gtk_widget_show_all (vars_scroller);
1426 GtkWidget *data_scroller = get_widget_assert (builder, "data-scroller");
1427 if (ia->data_sheet == NULL)
1429 ia->data_sheet = psppire_data_sheet_new ();
1430 g_object_set (ia->data_sheet, "editable", FALSE, NULL);
1432 gtk_container_add (GTK_CONTAINER (data_scroller), ia->data_sheet);
1434 gtk_widget_show_all (data_scroller);
1437 add_page_to_assistant (ia, w,
1438 GTK_ASSISTANT_PAGE_CONFIRM, _("Adjust Variable Formats"));
1445 separators_append_syntax (const PsppireImportAssistant *ia, struct string *s)
1449 ds_put_cstr (s, " /DELIMITERS=\"");
1451 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (get_widget_assert (ia->builder, "tab"))))
1452 ds_put_cstr (s, "\\t");
1453 for (i = 0; i < SEPARATOR_CNT; i++)
1455 const struct separator *seps = &separators[i];
1456 GtkWidget *button = get_widget_assert (ia->builder, seps->name);
1457 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
1459 if (seps->c == '\t')
1462 ds_put_byte (s, seps->c);
1465 ds_put_cstr (s, "\"\n");
1467 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->quote_cb)))
1469 GtkComboBoxText *cbt = GTK_COMBO_BOX_TEXT (ia->quote_combo);
1470 gchar *quotes = gtk_combo_box_text_get_active_text (cbt);
1471 if (quotes && *quotes)
1472 syntax_gen_pspp (s, " /QUALIFIER=%sq\n", quotes);
1478 formats_append_syntax (const PsppireImportAssistant *ia, struct string *s)
1483 g_return_if_fail (ia->dict);
1485 ds_put_cstr (s, " /VARIABLES=\n");
1487 var_cnt = dict_get_var_cnt (ia->dict);
1488 for (i = 0; i < var_cnt; i++)
1490 struct variable *var = dict_get_var (ia->dict, i);
1491 char format_string[FMT_STRING_LEN_MAX + 1];
1492 fmt_to_string (var_get_print_format (var), format_string);
1493 ds_put_format (s, " %s %s%s\n",
1494 var_get_name (var), format_string,
1495 i == var_cnt - 1 ? "." : "");
1500 first_line_append_syntax (const PsppireImportAssistant *ia, struct string *s)
1502 gint first_case = 0;
1503 g_object_get (ia->delimiters_model, "first-line", &first_case, NULL);
1506 ds_put_format (s, " /FIRSTCASE=%d\n", first_case + 1);
1510 intro_append_syntax (const PsppireImportAssistant *ia, struct string *s)
1512 gint first_line = 0;
1513 g_object_get (ia->delimiters_model, "first-line", &first_line, NULL);
1515 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->n_cases_button)))
1516 ds_put_format (s, "SELECT IF ($CASENUM <= %d).\n",
1517 gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (ia->n_cases_spin)) - first_line);
1518 else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->percent_button)))
1519 ds_put_format (s, "SAMPLE %.4g.\n",
1520 gtk_spin_button_get_value (GTK_SPIN_BUTTON (ia->percent_spin)) / 100.0);
1524 /* Emits PSPP syntax to S that applies the dictionary attributes
1525 (such as missing values and value labels) of the variables in
1528 apply_dict (const struct dictionary *dict, struct string *s)
1530 size_t var_cnt = dict_get_var_cnt (dict);
1533 for (i = 0; i < var_cnt; i++)
1535 struct variable *var = dict_get_var (dict, i);
1536 const char *name = var_get_name (var);
1537 enum val_type type = var_get_type (var);
1538 int width = var_get_width (var);
1539 enum measure measure = var_get_measure (var);
1540 enum var_role role = var_get_role (var);
1541 enum alignment alignment = var_get_alignment (var);
1542 const struct fmt_spec *format = var_get_print_format (var);
1544 if (var_has_missing_values (var))
1546 const struct missing_values *mv = var_get_missing_values (var);
1549 syntax_gen_pspp (s, "MISSING VALUES %ss (", name);
1550 for (j = 0; j < mv_n_values (mv); j++)
1553 ds_put_cstr (s, ", ");
1554 syntax_gen_value (s, mv_get_value (mv, j), width, format);
1557 if (mv_has_range (mv))
1560 if (mv_has_value (mv))
1561 ds_put_cstr (s, ", ");
1562 mv_get_range (mv, &low, &high);
1563 syntax_gen_num_range (s, low, high, format);
1565 ds_put_cstr (s, ").\n");
1567 if (var_has_value_labels (var))
1569 const struct val_labs *vls = var_get_value_labels (var);
1570 const struct val_lab **labels = val_labs_sorted (vls);
1571 size_t n_labels = val_labs_count (vls);
1574 syntax_gen_pspp (s, "VALUE LABELS %ss", name);
1575 for (i = 0; i < n_labels; i++)
1577 const struct val_lab *vl = labels[i];
1578 ds_put_cstr (s, "\n ");
1579 syntax_gen_value (s, &vl->value, width, format);
1580 ds_put_byte (s, ' ');
1581 syntax_gen_string (s, ss_cstr (val_lab_get_escaped_label (vl)));
1584 ds_put_cstr (s, ".\n");
1586 if (var_has_label (var))
1587 syntax_gen_pspp (s, "VARIABLE LABELS %ss %sq.\n",
1588 name, var_get_label (var));
1589 if (measure != var_default_measure (type))
1590 syntax_gen_pspp (s, "VARIABLE LEVEL %ss (%ss).\n",
1591 name, measure_to_syntax (measure));
1592 if (role != ROLE_INPUT)
1593 syntax_gen_pspp (s, "VARIABLE ROLE /%ss %ss.\n",
1594 var_role_to_syntax (role), name);
1595 if (alignment != var_default_alignment (type))
1596 syntax_gen_pspp (s, "VARIABLE ALIGNMENT %ss (%ss).\n",
1597 name, alignment_to_syntax (alignment));
1598 if (var_get_display_width (var) != var_default_display_width (width))
1599 syntax_gen_pspp (s, "VARIABLE WIDTH %ss (%d).\n",
1600 name, var_get_display_width (var));
1607 sheet_spec_gen_syntax (PsppireImportAssistant *ia, struct string *s)
1609 GtkBuilder *builder = ia->builder;
1610 GtkWidget *range_entry = get_widget_assert (builder, "cell-range-entry");
1611 GtkWidget *sheet_entry = get_widget_assert (builder, "sheet-entry");
1612 GtkWidget *rnc = get_widget_assert (builder, "readnames-checkbox");
1613 const gchar *range = gtk_entry_get_text (GTK_ENTRY (range_entry));
1614 int sheet_index = 1 + gtk_combo_box_get_active (GTK_COMBO_BOX (sheet_entry));
1615 gboolean read_names = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rnc));
1619 if (ia->spreadsheet)
1620 filename = ia->spreadsheet->file_name;
1622 g_object_get (ia->text_file, "file-name", &filename, NULL);
1627 "\n /SHEET=index %d"
1628 "\n /READNAMES=%ss",
1629 (ia->spreadsheet->type == SPREADSHEET_GNUMERIC) ? "GNM" : "ODS",
1632 read_names ? "ON" : "OFF");
1634 if (range && 0 != strcmp ("", range))
1637 "\n /CELLRANGE=RANGE %sq", range);
1642 "\n /CELLRANGE=FULL");
1646 syntax_gen_pspp (s, ".\n");
1651 psppire_import_assistant_generate_syntax (PsppireImportAssistant *ia)
1653 struct string s = DS_EMPTY_INITIALIZER;
1655 if (!ia->spreadsheet)
1657 gchar *file_name = NULL;
1658 gchar *encoding = NULL;
1659 g_object_get (ia->text_file,
1660 "file-name", &file_name,
1661 "encoding", &encoding,
1664 if (file_name == NULL)
1667 syntax_gen_pspp (&s,
1672 if (encoding && strcmp (encoding, "Auto"))
1673 syntax_gen_pspp (&s, " /ENCODING=%sq\n", encoding);
1676 " /ARRANGEMENT=DELIMITED\n"
1677 " /DELCASE=LINE\n");
1679 first_line_append_syntax (ia, &s);
1680 separators_append_syntax (ia, &s);
1682 formats_append_syntax (ia, &s);
1683 apply_dict (ia->dict, &s);
1684 intro_append_syntax (ia, &s);
1688 sheet_spec_gen_syntax (ia, &s);
1691 return ds_cstr (&s);
1696 psppire_import_assistant_run (PsppireImportAssistant *asst)
1698 g_main_loop_run (asst->main_loop);
1699 return asst->response;