- size_t var_cnt;
- size_t i;
-
- syntax_gen_pspp (&s,
- "GET DATA\n"
- " /TYPE=TXT\n"
- " /FILE=%sq\n",
- ia->file.file_name);
- if (ia->file.encoding && strcmp (ia->file.encoding, "Auto"))
- syntax_gen_pspp (&s, " /ENCODING=%sq\n", ia->file.encoding);
- if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (
- ia->intro.n_cases_button)))
- ds_put_format (&s, " /IMPORTCASES=FIRST %d\n",
- gtk_spin_button_get_value_as_int (
- GTK_SPIN_BUTTON (ia->intro.n_cases_spin)));
- else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (
- ia->intro.percent_button)))
- ds_put_format (&s, " /IMPORTCASES=PERCENT %d\n",
- gtk_spin_button_get_value_as_int (
- GTK_SPIN_BUTTON (ia->intro.percent_spin)));
- else
- ds_put_cstr (&s, " /IMPORTCASES=ALL\n");
- ds_put_cstr (&s,
- " /ARRANGEMENT=DELIMITED\n"
- " /DELCASE=LINE\n");
- if (ia->first_line.skip_lines > 0)
- ds_put_format (&s, " /FIRSTCASE=%d\n", ia->first_line.skip_lines + 1);
- ds_put_cstr (&s, " /DELIMITERS=\"");
- if (ds_find_byte (&ia->separators.separators, '\t') != SIZE_MAX)
- ds_put_cstr (&s, "\\t");
- if (ds_find_byte (&ia->separators.separators, '\\') != SIZE_MAX)
- ds_put_cstr (&s, "\\\\");
- for (i = 0; i < ds_length (&ia->separators.separators); i++)
- {
- char c = ds_at (&ia->separators.separators, i);
- if (c == '"')
- ds_put_cstr (&s, "\"\"");
- else if (c != '\t' && c != '\\')
- ds_put_byte (&s, c);
- }
- ds_put_cstr (&s, "\"\n");
- if (!ds_is_empty (&ia->separators.quotes))
- syntax_gen_pspp (&s, " /QUALIFIER=%sq\n", ds_cstr (&ia->separators.quotes));
- if (!ds_is_empty (&ia->separators.quotes) && ia->separators.escape)
- ds_put_cstr (&s, " /ESCAPE\n");
- ds_put_cstr (&s, " /VARIABLES=\n");
-
- var_cnt = dict_get_var_cnt (ia->formats.dict);
- for (i = 0; i < var_cnt; i++)
- {
- struct variable *var = dict_get_var (ia->formats.dict, i);
- char format_string[FMT_STRING_LEN_MAX + 1];
- fmt_to_string (var_get_print_format (var), format_string);
- ds_put_format (&s, " %s %s%s\n",
- var_get_name (var), format_string,
- i == var_cnt - 1 ? "." : "");
- }
-
- apply_dict (ia->formats.dict, &s);
-
- return ds_cstr (&s);
-}
-\f
-/* Choosing a file and reading it. */
-
-static char *choose_file (GtkWindow *parent_window, gchar **encodingp);
-
-/* Obtains the file to import from the user and initializes IA's
- file substructure. PARENT_WINDOW must be the window to use
- as the file chooser window's parent.
-
- Returns true if successful, false if the file name could not
- be obtained or the file could not be read. */
-static bool
-init_file (struct import_assistant *ia, GtkWindow *parent_window)
-{
- struct file *file = &ia->file;
- enum { MAX_PREVIEW_LINES = 1000 }; /* Max number of lines to read. */
- enum { MAX_LINE_LEN = 16384 }; /* Max length of an acceptable line. */
- struct line_reader *reader;
- struct string input;
-
- file->file_name = choose_file (parent_window, &file->encoding);
- if (file->file_name == NULL)
- return false;
-
- reader = line_reader_for_file (file->encoding, file->file_name, O_RDONLY);
- if (reader == NULL)
- {
- msg (ME, _("Could not open `%s': %s"),
- file->file_name, strerror (errno));
- return false;
- }
-
- ds_init_empty (&input);
- file->lines = xnmalloc (MAX_PREVIEW_LINES, sizeof *file->lines);
- for (; file->line_cnt < MAX_PREVIEW_LINES; file->line_cnt++)
- {
- ds_clear (&input);
- if (!line_reader_read (reader, &input, MAX_LINE_LEN + 1)
- || ds_length (&input) > MAX_LINE_LEN)
- {
- if (line_reader_eof (reader))
- break;
- else if (line_reader_error (reader))
- msg (ME, _("Error reading `%s': %s"),
- file->file_name, strerror (line_reader_error (reader)));
- else
- msg (ME, _("Failed to read `%s', because it contains a line "
- "over %d bytes long and therefore appears not to be "
- "a text file."),
- file->file_name, MAX_LINE_LEN);
- line_reader_close (reader);
- destroy_file (ia);
- ds_destroy (&input);
- return false;
- }
-
- ds_init_cstr (&file->lines[file->line_cnt],
- recode_string ("UTF-8", line_reader_get_encoding (reader),
- ds_cstr (&input), ds_length (&input)));
- }
- ds_destroy (&input);
-
- if (file->line_cnt == 0)
- {
- msg (ME, _("`%s' is empty."), file->file_name);
- line_reader_close (reader);
- destroy_file (ia);
- return false;
- }
-
- /* Estimate the number of lines in the file. */
- if (file->line_cnt < MAX_PREVIEW_LINES)
- file->total_lines = file->line_cnt;
- else
- {
- struct stat s;
- off_t position = line_reader_tell (reader);
- if (fstat (line_reader_fileno (reader), &s) == 0 && position > 0)
- file->total_lines = (double) file->line_cnt / position * s.st_size;
- else
- file->total_lines = 0;
- }
-
- line_reader_close (reader);
-
- return true;
-}
-
-/* Frees IA's file substructure. */
-static void
-destroy_file (struct import_assistant *ia)
-{
- struct file *f = &ia->file;
- size_t i;
-
- for (i = 0; i < f->line_cnt; i++)
- ds_destroy (&f->lines[i]);
- free (f->lines);
- g_free (f->file_name);
- g_free (f->encoding);
-}
-
-/* Obtains the file to read from the user. If successful, returns the name of
- the file and stores the user's chosen encoding for the file into *ENCODINGP.
- The caller must free each of these strings with g_free().
-
- On failure, stores a null pointer and stores NULL in *ENCODINGP.
-
- PARENT_WINDOW must be the window to use as the file chooser window's
- parent. */
-static char *
-choose_file (GtkWindow *parent_window, gchar **encodingp)
-{
- char *file_name;
- GtkFileFilter *filter = NULL;
-
- GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Import Delimited Text Data"),
- parent_window,
- GTK_FILE_CHOOSER_ACTION_OPEN,
- GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
- GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
- NULL);
-
- g_object_set (dialog, "local-only", FALSE, NULL);
-
- filter = gtk_file_filter_new ();
- gtk_file_filter_set_name (filter, _("Text files"));
- gtk_file_filter_add_mime_type (filter, "text/*");
- gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
-
- filter = gtk_file_filter_new ();
- gtk_file_filter_set_name (filter, _("Text (*.txt) Files"));
- gtk_file_filter_add_pattern (filter, "*.txt");
- gtk_file_filter_add_pattern (filter, "*.TXT");
- gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
-
- filter = gtk_file_filter_new ();
- gtk_file_filter_set_name (filter, _("Plain Text (ASCII) Files"));
- gtk_file_filter_add_mime_type (filter, "text/plain");
- gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
-
- filter = gtk_file_filter_new ();
- gtk_file_filter_set_name (filter, _("Comma Separated Value Files"));
- gtk_file_filter_add_mime_type (filter, "text/csv");
- gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
-
- /* I've never encountered one of these, but it's listed here:
- http://www.iana.org/assignments/media-types/text/tab-separated-values */
- filter = gtk_file_filter_new ();
- gtk_file_filter_set_name (filter, _("Tab Separated Value Files"));
- gtk_file_filter_add_mime_type (filter, "text/tab-separated-values");
- gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
-
- gtk_file_chooser_set_extra_widget (
- GTK_FILE_CHOOSER (dialog), psppire_encoding_selector_new ("Auto", true));
-
- filter = gtk_file_filter_new ();
- gtk_file_filter_set_name (filter, _("All Files"));
- gtk_file_filter_add_pattern (filter, "*");
- gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
-
- switch (gtk_dialog_run (GTK_DIALOG (dialog)))
- {
- case GTK_RESPONSE_ACCEPT:
- file_name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
- *encodingp = psppire_encoding_selector_get_encoding (
- gtk_file_chooser_get_extra_widget (GTK_FILE_CHOOSER (dialog)));
- break;
- default:
- file_name = NULL;
- *encodingp = NULL;
- break;
- }
- gtk_widget_destroy (dialog);
-
- return file_name;
-}
-\f
-/* Assistant. */
-
-static void close_assistant (struct import_assistant *, int response);
-static void on_prepare (GtkAssistant *assistant, GtkWidget *page,
- struct import_assistant *);
-static void on_cancel (GtkAssistant *assistant, struct import_assistant *);
-static void on_close (GtkAssistant *assistant, struct import_assistant *);
-static void on_paste (GtkButton *button, struct import_assistant *);
-static void on_reset (GtkButton *button, struct import_assistant *);
-static void close_assistant (struct import_assistant *, int response);
-
-/* Initializes IA's asst substructure. PARENT_WINDOW must be the
- window to use as the assistant window's parent. */
-static void
-init_assistant (struct import_assistant *ia, GtkWindow *parent_window)
-{
- struct assistant *a = &ia->asst;
-
- a->builder = builder_new ("text-data-import.ui");
- a->assistant = GTK_ASSISTANT (gtk_assistant_new ());
- g_signal_connect (a->assistant, "prepare", G_CALLBACK (on_prepare), ia);
- g_signal_connect (a->assistant, "cancel", G_CALLBACK (on_cancel), ia);
- g_signal_connect (a->assistant, "close", G_CALLBACK (on_close), ia);
- a->paste_button = gtk_button_new_from_stock (GTK_STOCK_PASTE);
- gtk_assistant_add_action_widget (a->assistant, a->paste_button);
- g_signal_connect (a->paste_button, "clicked", G_CALLBACK (on_paste), ia);
- a->reset_button = gtk_button_new_from_stock ("pspp-stock-reset");
- gtk_assistant_add_action_widget (a->assistant, a->reset_button);
- g_signal_connect (a->reset_button, "clicked", G_CALLBACK (on_reset), ia);
- gtk_window_set_title (GTK_WINDOW (a->assistant),
- _("Importing Delimited Text Data"));
- gtk_window_set_transient_for (GTK_WINDOW (a->assistant), parent_window);
- gtk_window_set_icon_name (GTK_WINDOW (a->assistant), "pspp");
-
- a->prop_renderer = gtk_cell_renderer_text_new ();
- g_object_ref_sink (a->prop_renderer);
- a->fixed_renderer = gtk_cell_renderer_text_new ();
- g_object_ref_sink (a->fixed_renderer);
- g_object_set (G_OBJECT (a->fixed_renderer),
- "family", "Monospace",
- (void *) NULL);
-}
-
-/* Frees IA's asst substructure. */
-static void
-destroy_assistant (struct import_assistant *ia)
-{
- struct assistant *a = &ia->asst;
-
- g_object_unref (a->prop_renderer);
- g_object_unref (a->fixed_renderer);
- g_object_unref (a->builder);
-}
-
-/* Appends a page of the given TYPE, with PAGE as its content, to
- the GtkAssistant encapsulated by IA. Returns the GtkWidget
- that represents the page. */
-static GtkWidget *
-add_page_to_assistant (struct import_assistant *ia,
- GtkWidget *page, GtkAssistantPageType type)
-{
- const char *title;
- char *title_copy;
- GtkWidget *content;
-
- title = gtk_window_get_title (GTK_WINDOW (page));
- title_copy = xstrdup (title ? title : "");
-
- content = gtk_bin_get_child (GTK_BIN (page));
- assert (content);
- g_object_ref (content);
- gtk_container_remove (GTK_CONTAINER (page), content);
-
- gtk_widget_destroy (page);
-
- gtk_assistant_append_page (ia->asst.assistant, content);
- gtk_assistant_set_page_type (ia->asst.assistant, content, type);
- gtk_assistant_set_page_title (ia->asst.assistant, content, title_copy);
- gtk_assistant_set_page_complete (ia->asst.assistant, content, true);
-
- free (title_copy);
-
- return content;
-}
-
-/* Called just before PAGE is displayed as the current page of
- ASSISTANT, this updates IA content according to the new
- page. */
-static void
-on_prepare (GtkAssistant *assistant, GtkWidget *page,
- struct import_assistant *ia)
-{
-
- if (gtk_assistant_get_page_type (assistant, page)
- == GTK_ASSISTANT_PAGE_CONFIRM)
- gtk_widget_grab_focus (assistant->apply);
- else
- gtk_widget_grab_focus (assistant->forward);
-
- if (page == ia->separators.page)
- prepare_separators_page (ia);
- else if (page == ia->formats.page)
- prepare_formats_page (ia);
-
- gtk_widget_show (ia->asst.reset_button);
- if (page == ia->formats.page)
- gtk_widget_show (ia->asst.paste_button);
- else
- gtk_widget_hide (ia->asst.paste_button);
-}
-
-/* Called when the Cancel button in the assistant is clicked. */
-static void
-on_cancel (GtkAssistant *assistant, struct import_assistant *ia)
-{
- close_assistant (ia, GTK_RESPONSE_CANCEL);
-}
-
-/* Called when the Apply button on the last page of the assistant
- is clicked. */
-static void
-on_close (GtkAssistant *assistant, struct import_assistant *ia)
-{
- close_assistant (ia, GTK_RESPONSE_APPLY);
-}
-
-/* Called when the Paste button on the last page of the assistant
- is clicked. */
-static void
-on_paste (GtkButton *button, struct import_assistant *ia)
-{
- close_assistant (ia, PSPPIRE_RESPONSE_PASTE);
-}
-
-/* Called when the Reset button is clicked. */
-static void
-on_reset (GtkButton *button, struct import_assistant *ia)
-{
- gint page_num = gtk_assistant_get_current_page (ia->asst.assistant);
- GtkWidget *page = gtk_assistant_get_nth_page (ia->asst.assistant, page_num);
-
- if (page == ia->intro.page)
- reset_intro_page (ia);
- else if (page == ia->first_line.page)
- reset_first_line_page (ia);
- else if (page == ia->separators.page)
- reset_separators_page (ia);
- else if (page == ia->formats.page)
- reset_formats_page (ia);
-}
-
-/* Causes the assistant to close, returning RESPONSE for
- interpretation by text_data_import_assistant. */
-static void
-close_assistant (struct import_assistant *ia, int response)
-{
- ia->asst.response = response;
- g_main_loop_quit (ia->asst.main_loop);
- gtk_widget_hide (GTK_WIDGET (ia->asst.assistant));
-}
-\f
-/* The "intro" page of the assistant. */
-
-static void on_intro_amount_changed (struct import_assistant *);
-
-/* Initializes IA's intro substructure. */
-static void
-init_intro_page (struct import_assistant *ia)
-{
- GtkBuilder *builder = ia->asst.builder;
- struct intro_page *p = &ia->intro;
- struct string s;
- GtkWidget *hbox_n_cases ;
- GtkWidget *hbox_percent ;
- GtkWidget *table ;
-
-
- p->n_cases_spin = gtk_spin_button_new_with_range (0, INT_MAX, 100);
-
- hbox_n_cases = psppire_scanf_new (_("Only the first %4d cases"), &p->n_cases_spin);
-
- table = get_widget_assert (builder, "button-table");
-
- gtk_table_attach_defaults (GTK_TABLE (table), hbox_n_cases,
- 1, 2,
- 1, 2);
-
- p->percent_spin = gtk_spin_button_new_with_range (0, 100, 10);
-
- hbox_percent = psppire_scanf_new (_("Only the first %3d %% of file (approximately)"), &p->percent_spin);
-
- gtk_table_attach_defaults (GTK_TABLE (table), hbox_percent,
- 1, 2,
- 2, 3);
-
- p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Intro"),
- GTK_ASSISTANT_PAGE_INTRO);
-
- p->all_cases_button = get_widget_assert (builder, "import-all-cases");
-
- p->n_cases_button = get_widget_assert (builder, "import-n-cases");
-
- p->percent_button = get_widget_assert (builder, "import-percent");
-
- g_signal_connect_swapped (p->all_cases_button, "toggled",
- G_CALLBACK (on_intro_amount_changed), ia);
- g_signal_connect_swapped (p->n_cases_button, "toggled",
- G_CALLBACK (on_intro_amount_changed), ia);
- g_signal_connect_swapped (p->percent_button, "toggled",
- G_CALLBACK (on_intro_amount_changed), ia);