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);
660 chooser_page_create (PsppireImportAssistant *ia)
662 GtkFileFilter *filter = NULL;
664 GtkWidget *chooser = gtk_file_chooser_widget_new (GTK_FILE_CHOOSER_ACTION_OPEN);
666 g_object_set_data (G_OBJECT (chooser), "on-forward", chooser_page_leave);
667 g_object_set_data (G_OBJECT (chooser), "on-reset", chooser_page_reset);
668 g_object_set_data (G_OBJECT (chooser), "on-entering",chooser_page_enter);
670 g_object_set (chooser, "local-only", FALSE, NULL);
673 ia->default_filter = gtk_file_filter_new ();
674 gtk_file_filter_set_name (ia->default_filter, _("All Files"));
675 gtk_file_filter_add_pattern (ia->default_filter, "*");
676 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), ia->default_filter);
678 filter = gtk_file_filter_new ();
679 gtk_file_filter_set_name (filter, _("Text Files"));
680 gtk_file_filter_add_mime_type (filter, "text/*");
681 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
683 filter = gtk_file_filter_new ();
684 gtk_file_filter_set_name (filter, _("Text (*.txt) Files"));
685 gtk_file_filter_add_pattern (filter, "*.txt");
686 gtk_file_filter_add_pattern (filter, "*.TXT");
687 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
689 filter = gtk_file_filter_new ();
690 gtk_file_filter_set_name (filter, _("Plain Text (ASCII) Files"));
691 gtk_file_filter_add_mime_type (filter, "text/plain");
692 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
694 filter = gtk_file_filter_new ();
695 gtk_file_filter_set_name (filter, _("Comma Separated Value Files"));
696 gtk_file_filter_add_mime_type (filter, "text/csv");
697 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
699 /* I've never encountered one of these, but it's listed here:
700 http://www.iana.org/assignments/media-types/text/tab-separated-values */
701 filter = gtk_file_filter_new ();
702 gtk_file_filter_set_name (filter, _("Tab Separated Value Files"));
703 gtk_file_filter_add_mime_type (filter, "text/tab-separated-values");
704 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
706 filter = gtk_file_filter_new ();
707 gtk_file_filter_set_name (filter, _("Gnumeric Spreadsheet Files"));
708 gtk_file_filter_add_mime_type (filter, "application/x-gnumeric");
709 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
711 filter = gtk_file_filter_new ();
712 gtk_file_filter_set_name (filter, _("OpenDocument Spreadsheet Files"));
713 gtk_file_filter_add_mime_type (filter, "application/vnd.oasis.opendocument.spreadsheet");
714 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
716 filter = gtk_file_filter_new ();
717 gtk_file_filter_set_name (filter, _("All Spreadsheet Files"));
718 gtk_file_filter_add_mime_type (filter, "application/x-gnumeric");
719 gtk_file_filter_add_mime_type (filter, "application/vnd.oasis.opendocument.spreadsheet");
720 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
722 ia->encoding_selector = psppire_encoding_selector_new ("Auto", TRUE);
723 gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (chooser), ia->encoding_selector);
725 add_page_to_assistant (ia, chooser,
726 GTK_ASSISTANT_PAGE_INTRO, _("Select File to Import"));
728 g_signal_connect_swapped (chooser, "selection-changed", G_CALLBACK (on_chosen), ia);
729 g_signal_connect_swapped (chooser, "map", G_CALLBACK (on_map), ia);
735 psppire_import_assistant_init (PsppireImportAssistant *ia)
737 ia->builder = builder_new ("text-data-import.ui");
739 ia->current_page = -1 ;
740 ia->file_name = NULL;
742 ia->spreadsheet = NULL;
744 ia->casereader_dict = NULL;
746 ia->main_loop = g_main_loop_new (NULL, TRUE);
748 g_signal_connect (ia, "prepare", G_CALLBACK (on_prepare), ia);
749 g_signal_connect (ia, "cancel", G_CALLBACK (on_cancel), ia);
750 g_signal_connect (ia, "close", G_CALLBACK (on_close), ia);
752 ia->paste_button = gtk_button_new_with_label (_("Paste"));
753 ia->reset_button = gtk_button_new_with_label (_("Reset"));
755 gtk_assistant_add_action_widget (GTK_ASSISTANT(ia), ia->paste_button);
757 g_signal_connect (ia->paste_button, "clicked", G_CALLBACK (on_paste), ia);
758 g_signal_connect (ia->reset_button, "clicked", G_CALLBACK (on_reset), ia);
760 gtk_assistant_add_action_widget (GTK_ASSISTANT(ia), ia->reset_button);
762 gtk_window_set_title (GTK_WINDOW (ia),
763 _("Importing Delimited Text Data"));
765 gtk_window_set_icon_name (GTK_WINDOW (ia), "pspp");
767 chooser_page_create (ia);
769 gtk_assistant_set_forward_page_func (GTK_ASSISTANT (ia), next_page_func, NULL, NULL);
771 gtk_window_maximize (GTK_WINDOW (ia));
775 /* Appends a page of the given TYPE, with PAGE as its content, to
776 the GtkAssistant encapsulated by IA. Returns the GtkWidget
777 that represents the page. */
779 add_page_to_assistant (PsppireImportAssistant *ia,
780 GtkWidget *page, GtkAssistantPageType type, const gchar *title)
782 GtkWidget *content = page;
784 gtk_assistant_append_page (GTK_ASSISTANT (ia), content);
785 gtk_assistant_set_page_type (GTK_ASSISTANT(ia), content, type);
786 gtk_assistant_set_page_title (GTK_ASSISTANT(ia), content, title);
787 gtk_assistant_set_page_complete (GTK_ASSISTANT(ia), content, TRUE);
793 /* Called when one of the radio buttons is clicked. */
795 on_intro_amount_changed (PsppireImportAssistant *p)
797 gtk_widget_set_sensitive (p->n_cases_spin,
798 gtk_toggle_button_get_active
799 (GTK_TOGGLE_BUTTON (p->n_cases_button)));
801 gtk_widget_set_sensitive (p->percent_spin,
802 gtk_toggle_button_get_active
803 (GTK_TOGGLE_BUTTON (p->percent_button)));
807 on_treeview_selection_change (PsppireImportAssistant *ia)
809 GtkTreeSelection *selection =
810 gtk_tree_view_get_selection (GTK_TREE_VIEW (ia->first_line_tree_view));
811 GtkTreeModel *model = NULL;
813 if (gtk_tree_selection_get_selected (selection, &model, &iter))
817 GtkTreePath *path = gtk_tree_model_get_path (model, &iter);
818 gint *index = gtk_tree_path_get_indices (path);
820 gtk_tree_path_free (path);
821 g_object_get (model, "maximum-lines", &max_lines, NULL);
822 gtk_widget_set_sensitive (ia->variable_names_cb,
823 (n > 0 && n < max_lines));
824 ia->delimiters_model =
825 psppire_delimited_text_new (GTK_TREE_MODEL (ia->text_file));
826 g_object_set (ia->delimiters_model, "first-line", n, NULL);
831 render_text_preview_line (GtkTreeViewColumn *tree_column,
832 GtkCellRenderer *cell,
833 GtkTreeModel *tree_model,
838 Set the text to a "insensitive" state if the row
839 is greater than what the user declared to be the maximum.
841 GtkTreePath *path = gtk_tree_model_get_path (tree_model, iter);
842 gint *ii = gtk_tree_path_get_indices (path);
844 g_object_get (tree_model, "maximum-lines", &max_lines, NULL);
845 g_object_set (cell, "sensitive", (*ii < max_lines), NULL);
846 gtk_tree_path_free (path);
849 /* Initializes IA's first_line substructure. */
851 first_line_page_create (PsppireImportAssistant *ia)
853 GtkWidget *w = get_widget_assert (ia->builder, "FirstLine");
855 g_object_set_data (G_OBJECT (w), "on-entering", on_treeview_selection_change);
857 add_page_to_assistant (ia, w,
858 GTK_ASSISTANT_PAGE_CONTENT, _("Select the First Line"));
860 GtkWidget *scrolled_window = get_widget_assert (ia->builder, "first-line-scroller");
862 if (ia->first_line_tree_view == NULL)
864 ia->first_line_tree_view = gtk_tree_view_new ();
865 g_object_set (ia->first_line_tree_view, "enable-search", FALSE, NULL);
867 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (ia->first_line_tree_view), TRUE);
869 GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
870 GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes (_("Line"), renderer,
874 gtk_tree_view_column_set_cell_data_func (column, renderer, render_text_preview_line, ia, 0);
875 gtk_tree_view_append_column (GTK_TREE_VIEW (ia->first_line_tree_view), column);
877 renderer = gtk_cell_renderer_text_new ();
878 column = gtk_tree_view_column_new_with_attributes (_("Text"), renderer, "text", 1, NULL);
879 gtk_tree_view_column_set_cell_data_func (column, renderer, render_text_preview_line, ia, 0);
881 gtk_tree_view_append_column (GTK_TREE_VIEW (ia->first_line_tree_view), column);
883 g_signal_connect_swapped (ia->first_line_tree_view, "cursor-changed",
884 G_CALLBACK (on_treeview_selection_change), ia);
885 gtk_container_add (GTK_CONTAINER (scrolled_window), ia->first_line_tree_view);
888 gtk_widget_show_all (scrolled_window);
890 ia->variable_names_cb = get_widget_assert (ia->builder, "variable-names");
894 intro_on_leave (PsppireImportAssistant *ia)
897 g_object_get (ia->text_file, "line-count", &lc, NULL);
898 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->n_cases_button)))
900 gint max_lines = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (ia->n_cases_spin));
901 g_object_set (ia->text_file, "maximum-lines", max_lines, NULL);
903 else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->percent_button)))
905 gdouble percent = gtk_spin_button_get_value (GTK_SPIN_BUTTON (ia->percent_spin));
906 g_object_set (ia->text_file, "maximum-lines", (gint) (lc * percent / 100.0), NULL);
910 g_object_set (ia->text_file, "maximum-lines", lc, NULL);
916 intro_on_enter (PsppireImportAssistant *ia)
918 GtkBuilder *builder = ia->builder;
919 GtkWidget *table = get_widget_assert (builder, "button-table");
924 ds_put_cstr (&s, _("This assistant will guide you through the process of "
925 "importing data into PSPP from a text file with one line "
926 "per case, in which fields are separated by tabs, "
927 "commas, or other delimiters.\n\n"));
931 if (ia->text_file->total_is_exact)
934 &s, ngettext ("The selected file contains %'lu line of text. ",
935 "The selected file contains %'lu lines of text. ",
936 ia->text_file->total_lines),
937 ia->text_file->total_lines);
939 else if (ia->text_file->total_lines > 0)
943 "The selected file contains approximately %'lu line of text. ",
944 "The selected file contains approximately %'lu lines of text. ",
945 ia->text_file->total_lines),
946 ia->text_file->total_lines);
949 "Only the first %zu line of the file will be shown for "
950 "preview purposes in the following screens. ",
951 "Only the first %zu lines of the file will be shown for "
952 "preview purposes in the following screens. ",
953 ia->text_file->line_cnt),
954 ia->text_file->line_cnt);
958 ds_put_cstr (&s, _("You may choose below how much of the file should "
959 "actually be imported."));
961 gtk_label_set_text (GTK_LABEL (get_widget_assert (builder, "intro-label")),
965 if (gtk_grid_get_child_at (GTK_GRID (table), 1, 1) == NULL)
967 GtkWidget *hbox_n_cases = psppire_scanf_new (_("Only the first %4d cases"), &ia->n_cases_spin);
968 gtk_grid_attach (GTK_GRID (table), hbox_n_cases,
973 GtkAdjustment *adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (ia->n_cases_spin));
974 gtk_adjustment_set_lower (adj, 1.0);
976 if (gtk_grid_get_child_at (GTK_GRID (table), 1, 2) == NULL)
978 GtkWidget *hbox_percent = psppire_scanf_new (_("Only the first %3d %% of file (approximately)"),
981 gtk_grid_attach (GTK_GRID (table), hbox_percent,
986 gtk_widget_show_all (table);
988 on_intro_amount_changed (ia);
991 /* Initializes IA's intro substructure. */
993 intro_page_create (PsppireImportAssistant *ia)
995 GtkBuilder *builder = ia->builder;
997 GtkWidget *w = get_widget_assert (builder, "Intro");
999 ia->percent_spin = gtk_spin_button_new_with_range (0, 100, 10);
1002 add_page_to_assistant (ia, w, GTK_ASSISTANT_PAGE_CONTENT, _("Select the Lines to Import"));
1004 ia->all_cases_button = get_widget_assert (builder, "import-all-cases");
1006 ia->n_cases_button = get_widget_assert (builder, "import-n-cases");
1008 ia->percent_button = get_widget_assert (builder, "import-percent");
1010 g_signal_connect_swapped (ia->all_cases_button, "toggled",
1011 G_CALLBACK (on_intro_amount_changed), ia);
1012 g_signal_connect_swapped (ia->n_cases_button, "toggled",
1013 G_CALLBACK (on_intro_amount_changed), ia);
1014 g_signal_connect_swapped (ia->percent_button, "toggled",
1015 G_CALLBACK (on_intro_amount_changed), ia);
1018 g_object_set_data (G_OBJECT (w), "on-forward", intro_on_leave);
1019 g_object_set_data (G_OBJECT (w), "on-entering", intro_on_enter);
1020 g_object_set_data (G_OBJECT (w), "on-reset", reset_intro_page);
1025 psppire_import_assistant_new (GtkWindow *toplevel)
1027 return GTK_WIDGET (g_object_new (PSPPIRE_TYPE_IMPORT_ASSISTANT,
1028 /* Some window managers (notably ratpoison)
1029 ignore the maximise command when a window is
1030 transient. This causes problems for this
1032 /* "transient-for", toplevel, */
1040 /* Chooses a name for each column on the separators page */
1042 choose_column_names (PsppireImportAssistant *ia)
1045 unsigned long int generated_name_count = 0;
1046 dict_clear (ia->dict);
1049 i < gtk_tree_model_get_n_columns (GTK_TREE_MODEL (ia->delimiters_model)) - 1;
1052 const gchar *candidate_name = NULL;
1054 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->variable_names_cb)))
1056 candidate_name = psppire_delimited_text_get_header_title (PSPPIRE_DELIMITED_TEXT (ia->delimiters_model), i);
1059 char *name = dict_make_unique_var_name (ia->dict,
1061 &generated_name_count);
1063 dict_create_var_assert (ia->dict, name, 0);
1068 /* Called when the user toggles one of the separators
1071 on_separator_toggle (GtkToggleButton *toggle UNUSED,
1072 PsppireImportAssistant *ia)
1075 GSList *delimiters = NULL;
1076 for (i = 0; i < SEPARATOR_CNT; i++)
1078 const struct separator *s = &separators[i];
1079 GtkWidget *button = get_widget_assert (ia->builder, s->name);
1080 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
1082 delimiters = g_slist_prepend (delimiters, GINT_TO_POINTER (s->c));
1086 g_object_set (ia->delimiters_model, "delimiters", delimiters, NULL);
1088 revise_fields_preview (ia);
1092 /* Called when the user changes the entry field for custom
1095 on_separators_custom_entry_notify (GObject *gobject UNUSED,
1096 GParamSpec *arg1 UNUSED,
1097 PsppireImportAssistant *ia)
1099 revise_fields_preview (ia);
1102 /* Called when the user toggles the checkbox that enables custom
1105 on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
1106 PsppireImportAssistant *ia)
1108 bool is_active = gtk_toggle_button_get_active (custom_cb);
1109 gtk_widget_set_sensitive (ia->custom_entry, is_active);
1110 revise_fields_preview (ia);
1113 /* Called when the user changes the selection in the combo box
1114 that selects a quote character. */
1116 on_quote_combo_change (GtkComboBox *combo, PsppireImportAssistant *ia)
1118 // revise_fields_preview (ia);
1121 /* Called when the user toggles the checkbox that enables
1124 on_quote_cb_toggle (GtkToggleButton *quote_cb, PsppireImportAssistant *ia)
1126 bool is_active = gtk_toggle_button_get_active (quote_cb);
1127 gtk_widget_set_sensitive (ia->quote_combo, is_active);
1128 revise_fields_preview (ia);
1131 /* Initializes IA's separators substructure. */
1133 separators_page_create (PsppireImportAssistant *ia)
1135 GtkBuilder *builder = ia->builder;
1139 GtkWidget *w = get_widget_assert (builder, "Separators");
1141 g_object_set_data (G_OBJECT (w), "on-entering", prepare_separators_page);
1142 g_object_set_data (G_OBJECT (w), "on-reset", prepare_separators_page);
1144 add_page_to_assistant (ia, w, GTK_ASSISTANT_PAGE_CONTENT, _("Choose Separators"));
1146 ia->custom_cb = get_widget_assert (builder, "custom-cb");
1147 ia->custom_entry = get_widget_assert (builder, "custom-entry");
1148 ia->quote_combo = get_widget_assert (builder, "quote-combo");
1149 ia->quote_cb = get_widget_assert (builder, "quote-cb");
1151 gtk_combo_box_set_active (GTK_COMBO_BOX (ia->quote_combo), 0);
1153 if (ia->fields_tree_view == NULL)
1155 GtkWidget *scroller = get_widget_assert (ia->builder, "fields-scroller");
1156 ia->fields_tree_view = gtk_tree_view_new ();
1157 g_object_set (ia->fields_tree_view, "enable-search", FALSE, NULL);
1158 gtk_container_add (GTK_CONTAINER (scroller), GTK_WIDGET (ia->fields_tree_view));
1159 gtk_widget_show_all (scroller);
1162 g_signal_connect (ia->quote_combo, "changed",
1163 G_CALLBACK (on_quote_combo_change), ia);
1164 g_signal_connect (ia->quote_cb, "toggled",
1165 G_CALLBACK (on_quote_cb_toggle), ia);
1166 g_signal_connect (ia->custom_entry, "notify::text",
1167 G_CALLBACK (on_separators_custom_entry_notify), ia);
1168 g_signal_connect (ia->custom_cb, "toggled",
1169 G_CALLBACK (on_separators_custom_cb_toggle), ia);
1170 for (i = 0; i < SEPARATOR_CNT; i++)
1171 g_signal_connect (get_widget_assert (builder, separators[i].name),
1172 "toggled", G_CALLBACK (on_separator_toggle), ia);
1181 static struct casereader_random_class my_casereader_class;
1183 static struct ccase *
1184 my_read (struct casereader *reader, void *aux, casenumber idx)
1186 PsppireImportAssistant *ia = PSPPIRE_IMPORT_ASSISTANT (aux);
1187 GtkTreeModel *tm = GTK_TREE_MODEL (ia->delimiters_model);
1189 GtkTreePath *tp = gtk_tree_path_new_from_indices (idx, -1);
1191 const struct caseproto *proto = casereader_get_proto (reader);
1194 struct ccase *c = NULL;
1195 if (gtk_tree_model_get_iter (tm, &iter, tp))
1197 c = case_create (proto);
1199 for (i = 0 ; i < caseproto_get_n_widths (proto); ++i)
1202 gtk_tree_model_get_value (tm, &iter, i + 1, &value);
1204 const struct variable *var = dict_get_var (ia->casereader_dict, i);
1206 const gchar *ss = g_value_get_string (&value);
1209 union value *v = case_data_rw (c, var);
1210 /* In this reader we derive the union value from the
1211 string in the tree_model. We retrieve the width and format
1212 from a dictionary which is stored directly after
1213 the reader creation. Changes in ia->dict in the
1214 variable window are not reflected here and therefore
1215 this is always compatible with the width in the
1216 caseproto. See bug #58298 */
1217 char *xx = data_in (ss_cstr (ss),
1219 var_get_write_format (var)->type,
1220 v, var_get_width (var), "UTF-8");
1223 /* g_print ("%s:%d Err %s\n", __FILE__, __LINE__, xx); */
1226 g_value_unset (&value);
1230 gtk_tree_path_free (tp);
1236 my_destroy (struct casereader *reader, void *aux)
1238 g_print ("%s:%d %p\n", __FILE__, __LINE__, reader);
1242 my_advance (struct casereader *reader, void *aux, casenumber cnt)
1244 g_print ("%s:%d\n", __FILE__, __LINE__);
1247 static struct casereader *
1248 textfile_create_reader (PsppireImportAssistant *ia)
1250 int n_vars = dict_get_var_cnt (ia->dict);
1254 struct fmt_guesser **fg = XCALLOC (n_vars, struct fmt_guesser *);
1255 for (i = 0 ; i < n_vars; ++i)
1257 fg[i] = fmt_guesser_create ();
1260 gint n_rows = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (ia->delimiters_model), NULL);
1264 for (ok = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (ia->delimiters_model), &iter);
1266 ok = gtk_tree_model_iter_next (GTK_TREE_MODEL (ia->delimiters_model), &iter))
1268 for (i = 0 ; i < n_vars; ++i)
1271 gtk_tree_model_get (GTK_TREE_MODEL (ia->delimiters_model), &iter, i+1, &s, -1);
1273 fmt_guesser_add (fg[i], ss_cstr (s));
1278 struct caseproto *proto = caseproto_create ();
1279 for (i = 0 ; i < n_vars; ++i)
1282 fmt_guesser_guess (fg[i], &fs);
1284 fmt_fix (&fs, FMT_FOR_INPUT);
1286 struct variable *var = dict_get_var (ia->dict, i);
1288 int width = fmt_var_width (&fs);
1290 var_set_width_and_formats (var, width,
1293 proto = caseproto_add_width (proto, width);
1294 fmt_guesser_destroy (fg[i]);
1299 struct casereader *cr = casereader_create_random (proto, n_rows, &my_casereader_class, ia);
1300 /* Store the dictionary at this point when the casereader is created.
1301 my_read depends on the dictionary to interpret the strings in the treeview.
1302 This guarantees that the union value is produced according to the
1303 caseproto in the reader. */
1304 ia->casereader_dict = dict_clone (ia->dict);
1305 caseproto_unref (proto);
1309 /* When during import the variable type is changed, the reader is reinitialized
1310 based on the new dictionary with a fresh caseprototype. The default behaviour
1311 when a variable type is changed and the column is resized is that the union
1312 value is interpreted with new variable type and an overlay for that column
1313 is generated. Here we reinit to the original reader based on strings.
1314 As a result you can switch from string to numeric to string without loosing
1315 the string information. */
1317 ia_variable_changed_cb (GObject *obj, gint var_num, guint what,
1318 const struct variable *oldvar, gpointer data)
1320 PsppireImportAssistant *ia = PSPPIRE_IMPORT_ASSISTANT (data);
1322 struct caseproto *proto = caseproto_create();
1323 for (int i = 0; i < dict_get_var_cnt (ia->dict); i++)
1325 const struct variable *var = dict_get_var (ia->dict, i);
1326 int width = var_get_width (var);
1327 proto = caseproto_add_width (proto, width);
1330 gint n_rows = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (ia->delimiters_model), NULL);
1332 PsppireDataStore *store = NULL;
1333 g_object_get (ia->data_sheet, "data-model", &store, NULL);
1335 struct casereader *cr = casereader_create_random (proto, n_rows,
1336 &my_casereader_class, ia);
1337 psppire_data_store_set_reader (store, cr);
1338 dict_unref (ia->casereader_dict);
1339 ia->casereader_dict = dict_clone (ia->dict);
1342 /* Called just before the formats page of the assistant is
1345 prepare_formats_page (PsppireImportAssistant *ia)
1347 my_casereader_class.read = my_read;
1348 my_casereader_class.destroy = my_destroy;
1349 my_casereader_class.advance = my_advance;
1351 if (ia->spreadsheet)
1353 GtkBuilder *builder = ia->builder;
1354 GtkWidget *range_entry = get_widget_assert (builder, "cell-range-entry");
1355 GtkWidget *rnc = get_widget_assert (builder, "readnames-checkbox");
1356 GtkWidget *combo_box = get_widget_assert (builder, "sheet-entry");
1358 struct spreadsheet_read_options opts;
1359 opts.sheet_name = NULL;
1360 opts.sheet_index = gtk_combo_box_get_active (GTK_COMBO_BOX (combo_box)) + 1;
1361 opts.read_names = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rnc));
1362 opts.cell_range = g_strdup (gtk_entry_get_text (GTK_ENTRY (range_entry)));
1365 struct casereader *reader = spreadsheet_make_reader (ia->spreadsheet, &opts);
1367 PsppireDict *dict = psppire_dict_new_from_dict (ia->spreadsheet->dict);
1368 PsppireDataStore *store = psppire_data_store_new (dict);
1369 psppire_data_store_set_reader (store, reader);
1370 g_object_set (ia->data_sheet, "data-model", store, NULL);
1371 g_object_set (ia->var_sheet, "data-model", dict, NULL);
1375 struct casereader *reader = textfile_create_reader (ia);
1377 PsppireDict *dict = psppire_dict_new_from_dict (ia->dict);
1378 PsppireDataStore *store = psppire_data_store_new (dict);
1379 psppire_data_store_set_reader (store, reader);
1380 g_signal_connect (dict, "variable-changed",
1381 G_CALLBACK (ia_variable_changed_cb),
1384 g_object_set (ia->data_sheet, "data-model", store, NULL);
1385 g_object_set (ia->var_sheet, "data-model", dict, NULL);
1389 g_object_get (get_widget_assert (ia->builder, "vpaned1"),
1390 "max-position", &pmax, NULL);
1393 g_object_set (get_widget_assert (ia->builder, "vpaned1"),
1394 "position", pmax / 2, NULL);
1396 gtk_widget_show (ia->paste_button);
1400 formats_page_create (PsppireImportAssistant *ia)
1402 GtkBuilder *builder = ia->builder;
1404 GtkWidget *w = get_widget_assert (builder, "Formats");
1405 g_object_set_data (G_OBJECT (w), "on-entering", prepare_formats_page);
1406 g_object_set_data (G_OBJECT (w), "on-reset", reset_formats_page);
1408 GtkWidget *vars_scroller = get_widget_assert (builder, "vars-scroller");
1409 if (ia->var_sheet == NULL)
1411 ia->var_sheet = psppire_variable_sheet_new ();
1413 gtk_container_add (GTK_CONTAINER (vars_scroller), ia->var_sheet);
1415 ia->dict = dict_create (get_default_encoding ());
1417 gtk_widget_show_all (vars_scroller);
1419 GtkWidget *data_scroller = get_widget_assert (builder, "data-scroller");
1420 if (ia->data_sheet == NULL)
1422 ia->data_sheet = psppire_data_sheet_new ();
1423 g_object_set (ia->data_sheet, "editable", FALSE, NULL);
1425 gtk_container_add (GTK_CONTAINER (data_scroller), ia->data_sheet);
1427 gtk_widget_show_all (data_scroller);
1430 add_page_to_assistant (ia, w,
1431 GTK_ASSISTANT_PAGE_CONFIRM, _("Adjust Variable Formats"));
1438 separators_append_syntax (const PsppireImportAssistant *ia, struct string *s)
1442 ds_put_cstr (s, " /DELIMITERS=\"");
1444 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (get_widget_assert (ia->builder, "tab"))))
1445 ds_put_cstr (s, "\\t");
1446 for (i = 0; i < SEPARATOR_CNT; i++)
1448 const struct separator *seps = &separators[i];
1449 GtkWidget *button = get_widget_assert (ia->builder, seps->name);
1450 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
1452 if (seps->c == '\t')
1455 ds_put_byte (s, seps->c);
1458 ds_put_cstr (s, "\"\n");
1460 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->quote_cb)))
1462 GtkComboBoxText *cbt = GTK_COMBO_BOX_TEXT (ia->quote_combo);
1463 gchar *quotes = gtk_combo_box_text_get_active_text (cbt);
1464 if (quotes && *quotes)
1465 syntax_gen_pspp (s, " /QUALIFIER=%sq\n", quotes);
1471 formats_append_syntax (const PsppireImportAssistant *ia, struct string *s)
1476 g_return_if_fail (ia->dict);
1478 ds_put_cstr (s, " /VARIABLES=\n");
1480 var_cnt = dict_get_var_cnt (ia->dict);
1481 for (i = 0; i < var_cnt; i++)
1483 struct variable *var = dict_get_var (ia->dict, i);
1484 char format_string[FMT_STRING_LEN_MAX + 1];
1485 fmt_to_string (var_get_print_format (var), format_string);
1486 ds_put_format (s, " %s %s%s\n",
1487 var_get_name (var), format_string,
1488 i == var_cnt - 1 ? "." : "");
1493 first_line_append_syntax (const PsppireImportAssistant *ia, struct string *s)
1495 gint first_case = 0;
1496 g_object_get (ia->delimiters_model, "first-line", &first_case, NULL);
1499 ds_put_format (s, " /FIRSTCASE=%d\n", first_case + 1);
1503 intro_append_syntax (const PsppireImportAssistant *ia, struct string *s)
1505 gint first_line = 0;
1506 g_object_get (ia->delimiters_model, "first-line", &first_line, NULL);
1508 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->n_cases_button)))
1509 ds_put_format (s, "SELECT IF ($CASENUM <= %d).\n",
1510 gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (ia->n_cases_spin)) - first_line);
1511 else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->percent_button)))
1512 ds_put_format (s, "SAMPLE %.4g.\n",
1513 gtk_spin_button_get_value (GTK_SPIN_BUTTON (ia->percent_spin)) / 100.0);
1517 /* Emits PSPP syntax to S that applies the dictionary attributes
1518 (such as missing values and value labels) of the variables in
1521 apply_dict (const struct dictionary *dict, struct string *s)
1523 size_t var_cnt = dict_get_var_cnt (dict);
1526 for (i = 0; i < var_cnt; i++)
1528 struct variable *var = dict_get_var (dict, i);
1529 const char *name = var_get_name (var);
1530 enum val_type type = var_get_type (var);
1531 int width = var_get_width (var);
1532 enum measure measure = var_get_measure (var);
1533 enum var_role role = var_get_role (var);
1534 enum alignment alignment = var_get_alignment (var);
1535 const struct fmt_spec *format = var_get_print_format (var);
1537 if (var_has_missing_values (var))
1539 const struct missing_values *mv = var_get_missing_values (var);
1542 syntax_gen_pspp (s, "MISSING VALUES %ss (", name);
1543 for (j = 0; j < mv_n_values (mv); j++)
1546 ds_put_cstr (s, ", ");
1547 syntax_gen_value (s, mv_get_value (mv, j), width, format);
1550 if (mv_has_range (mv))
1553 if (mv_has_value (mv))
1554 ds_put_cstr (s, ", ");
1555 mv_get_range (mv, &low, &high);
1556 syntax_gen_num_range (s, low, high, format);
1558 ds_put_cstr (s, ").\n");
1560 if (var_has_value_labels (var))
1562 const struct val_labs *vls = var_get_value_labels (var);
1563 const struct val_lab **labels = val_labs_sorted (vls);
1564 size_t n_labels = val_labs_count (vls);
1567 syntax_gen_pspp (s, "VALUE LABELS %ss", name);
1568 for (i = 0; i < n_labels; i++)
1570 const struct val_lab *vl = labels[i];
1571 ds_put_cstr (s, "\n ");
1572 syntax_gen_value (s, &vl->value, width, format);
1573 ds_put_byte (s, ' ');
1574 syntax_gen_string (s, ss_cstr (val_lab_get_escaped_label (vl)));
1577 ds_put_cstr (s, ".\n");
1579 if (var_has_label (var))
1580 syntax_gen_pspp (s, "VARIABLE LABELS %ss %sq.\n",
1581 name, var_get_label (var));
1582 if (measure != var_default_measure (type))
1583 syntax_gen_pspp (s, "VARIABLE LEVEL %ss (%ss).\n",
1584 name, measure_to_syntax (measure));
1585 if (role != ROLE_INPUT)
1586 syntax_gen_pspp (s, "VARIABLE ROLE /%ss %ss.\n",
1587 var_role_to_syntax (role), name);
1588 if (alignment != var_default_alignment (type))
1589 syntax_gen_pspp (s, "VARIABLE ALIGNMENT %ss (%ss).\n",
1590 name, alignment_to_syntax (alignment));
1591 if (var_get_display_width (var) != var_default_display_width (width))
1592 syntax_gen_pspp (s, "VARIABLE WIDTH %ss (%d).\n",
1593 name, var_get_display_width (var));
1600 sheet_spec_gen_syntax (PsppireImportAssistant *ia, struct string *s)
1602 GtkBuilder *builder = ia->builder;
1603 GtkWidget *range_entry = get_widget_assert (builder, "cell-range-entry");
1604 GtkWidget *sheet_entry = get_widget_assert (builder, "sheet-entry");
1605 GtkWidget *rnc = get_widget_assert (builder, "readnames-checkbox");
1606 const gchar *range = gtk_entry_get_text (GTK_ENTRY (range_entry));
1607 int sheet_index = 1 + gtk_combo_box_get_active (GTK_COMBO_BOX (sheet_entry));
1608 gboolean read_names = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rnc));
1612 if (ia->spreadsheet)
1613 filename = ia->spreadsheet->file_name;
1615 g_object_get (ia->text_file, "file-name", &filename, NULL);
1620 "\n /SHEET=index %d"
1621 "\n /READNAMES=%ss",
1622 (ia->spreadsheet->type == SPREADSHEET_GNUMERIC) ? "GNM" : "ODS",
1625 read_names ? "ON" : "OFF");
1627 if (range && 0 != strcmp ("", range))
1630 "\n /CELLRANGE=RANGE %sq", range);
1635 "\n /CELLRANGE=FULL");
1639 syntax_gen_pspp (s, ".\n");
1644 psppire_import_assistant_generate_syntax (PsppireImportAssistant *ia)
1646 struct string s = DS_EMPTY_INITIALIZER;
1648 if (!ia->spreadsheet)
1650 gchar *file_name = NULL;
1651 gchar *encoding = NULL;
1652 g_object_get (ia->text_file,
1653 "file-name", &file_name,
1654 "encoding", &encoding,
1657 if (file_name == NULL)
1660 syntax_gen_pspp (&s,
1665 if (encoding && strcmp (encoding, "Auto"))
1666 syntax_gen_pspp (&s, " /ENCODING=%sq\n", encoding);
1669 " /ARRANGEMENT=DELIMITED\n"
1670 " /DELCASE=LINE\n");
1672 first_line_append_syntax (ia, &s);
1673 separators_append_syntax (ia, &s);
1675 formats_append_syntax (ia, &s);
1676 apply_dict (ia->dict, &s);
1677 intro_append_syntax (ia, &s);
1681 sheet_spec_gen_syntax (ia, &s);
1684 return ds_cstr (&s);
1689 psppire_import_assistant_run (PsppireImportAssistant *asst)
1691 g_main_loop_run (asst->main_loop);
1692 return asst->response;