Merge 'psppsheet' into 'master'. 20130507010505/pspp
authorBen Pfaff <blp@cs.stanford.edu>
Tue, 7 May 2013 05:09:59 +0000 (22:09 -0700)
committerBen Pfaff <blp@cs.stanford.edu>
Tue, 7 May 2013 05:09:59 +0000 (22:09 -0700)
This merge required considerable manual resolution in
text-data-import-dialog.c because that file was split into multiple files
on master.

1  2 
src/ui/gui/automake.mk
src/ui/gui/data-editor.ui
src/ui/gui/page-assistant.c
src/ui/gui/page-file.c
src/ui/gui/page-first-line.c
src/ui/gui/page-formats.c
src/ui/gui/page-intro.c
src/ui/gui/page-separators.c
src/ui/gui/psppire-data-window.c
src/ui/gui/text-data-import-dialog.c
src/ui/gui/text-data-import-dialog.h

Simple merge
Simple merge
index 0000000000000000000000000000000000000000,88314734e2d82845a82d965b3c859d3657d8d3b0..608412b03cd8db782f5fdf8dcc09bf1a92a29307
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,247 +1,245 @@@
 -#include <gtk-contrib/psppire-sheet.h>
+ /* PSPPIRE - a graphical user interface for PSPP.
+    Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013  Free Software Foundation
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+ #include <config.h>
+ #include "ui/gui/text-data-import-dialog.h"
+ #include <errno.h>
+ #include <fcntl.h>
 -#include "ui/gui/psppire-var-store.h"
+ #include <gtk/gtk.h>
+ #include <limits.h>
+ #include <stdlib.h>
+ #include <sys/stat.h>
+ #include "data/data-in.h"
+ #include "data/data-out.h"
+ #include "data/format-guesser.h"
+ #include "data/value-labels.h"
+ #include "language/data-io/data-parser.h"
+ #include "language/lexer/lexer.h"
+ #include "libpspp/assertion.h"
+ #include "libpspp/i18n.h"
+ #include "libpspp/line-reader.h"
+ #include "libpspp/message.h"
+ #include "ui/gui/checkbox-treeview.h"
+ #include "ui/gui/dialog-common.h"
+ #include "ui/gui/executor.h"
+ #include "ui/gui/helper.h"
+ #include "ui/gui/builder-wrapper.h"
+ #include "ui/gui/psppire-data-window.h"
+ #include "ui/gui/psppire-dialog.h"
+ #include "ui/gui/psppire-encoding-selector.h"
+ #include "ui/gui/psppire-empty-list-store.h"
+ #include "ui/gui/psppire-var-sheet.h"
+ #include "gl/error.h"
+ #include "gl/intprops.h"
+ #include "gl/xalloc.h"
+ #include "gettext.h"
+ #define _(msgid) gettext (msgid)
+ #define N_(msgid) msgid
+ /* 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 *);
+ /* Initializes IA's asst substructure.  PARENT_WINDOW must be the
+    window to use as the assistant window's parent.  */
+ struct import_assistant *
+ init_assistant (GtkWindow *parent_window)
+ {
+   struct import_assistant *ia = xzalloc (sizeof *ia);
+   struct assistant *a = &ia->asst;
+   a->builder = builder_new ("text-data-import.ui");
+   a->assistant = GTK_ASSISTANT (gtk_assistant_new ());
+   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);
+   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");
+   return ia;
+ }
+ /* Frees IA's asst substructure. */
+ 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. */
+ 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)
+ {
+   int pn = gtk_assistant_get_current_page (assistant);
+   gtk_widget_show (ia->asst.reset_button);
+   gtk_widget_hide (ia->asst.paste_button);
+   if ( ia->spreadsheet) 
+     {
+       if (pn == 0)
+       {
+       }
+       else if (pn == 1)
+       {
+         prepare_formats_page (ia);
+       }
+     }
+   else
+     {
+       if (pn == 0)
+       prepare_separators_page (ia);
+       else if (pn == 3)
+       prepare_formats_page (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);
+ }
+ /* 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);
+ }
+ static GtkWidget *
+ assist_get_page (struct assist_page *ap)
+ {
+   if (ap == NULL)
+     return NULL;
+   return ap->page;
+ }
+ /* 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 == assist_get_page ((struct assist_page *) ia->intro))
+     reset_intro_page (ia);
+   else if (page == assist_get_page ((struct assist_page *) ia->first_line))
+     reset_first_line_page (ia);
+   else if (page == assist_get_page ((struct assist_page *) ia->separators))
+     reset_separators_page (ia);
+   else if (page == assist_get_page ((struct assist_page *) ia->formats))
+     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;
+   /*  Use our loop_done variable until we find out
+       why      g_main_loop_quit (ia->asst.main_loop); doesn't work.
+   */
+   ia->asst.loop_done = true;
+   gtk_widget_hide (GTK_WIDGET (ia->asst.assistant));
+ }
index 0000000000000000000000000000000000000000,93bc1edd9f3efa25713c9dfc668133fb56cd2dc0..850f20787d44019324bde4c0889085fe7e20573e
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,257 +1,255 @@@
 -#include <gtk-contrib/psppire-sheet.h>
+ /* PSPPIRE - a graphical user interface for PSPP.
+    Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013  Free Software Foundation
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+ #include <config.h>
+ #include "ui/gui/text-data-import-dialog.h"
+ #include <errno.h>
+ #include <fcntl.h>
 -#include "ui/gui/psppire-var-store.h"
+ #include <gtk/gtk.h>
+ #include <limits.h>
+ #include <stdlib.h>
+ #include <sys/stat.h>
+ #include "data/data-in.h"
+ #include "data/data-out.h"
+ #include "data/format-guesser.h"
+ #include "data/casereader.h"
+ #include "data/gnumeric-reader.h"
+ #include "data/ods-reader.h"
+ #include "data/spreadsheet-reader.h"
+ #include "data/value-labels.h"
+ #include "language/data-io/data-parser.h"
+ #include "language/lexer/lexer.h"
+ #include "libpspp/assertion.h"
+ #include "libpspp/i18n.h"
+ #include "libpspp/line-reader.h"
+ #include "libpspp/message.h"
+ #include "ui/gui/checkbox-treeview.h"
+ #include "ui/gui/dialog-common.h"
+ #include "ui/gui/executor.h"
+ #include "ui/gui/helper.h"
+ #include "ui/gui/builder-wrapper.h"
+ #include "ui/gui/psppire-data-window.h"
+ #include "ui/gui/psppire-dialog.h"
+ #include "ui/gui/psppire-encoding-selector.h"
+ #include "ui/gui/psppire-empty-list-store.h"
+ #include "ui/gui/psppire-var-sheet.h"
+ #include "ui/gui/psppire-scanf.h"
+ #include "ui/syntax-gen.h"
+ #include "gl/error.h"
+ #include "gl/intprops.h"
+ #include "gl/xalloc.h"
+ #include "gettext.h"
+ #define _(msgid) gettext (msgid)
+ #define N_(msgid) msgid
+ struct import_assistant;
+ /* Choose a file */
+ 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. */
+ bool
+ init_file (struct import_assistant *ia, GtkWindow *parent_window)
+ {
+   enum { MAX_LINE_LEN = 16384 }; /* Max length of an acceptable line. */
+   struct file *file = &ia->file;
+   file->lines = NULL;
+   file->file_name = choose_file (parent_window, &file->encoding);
+   if (file->file_name == NULL)
+     return false;
+   if (ia->spreadsheet == NULL)
+     ia->spreadsheet = gnumeric_probe (file->file_name, false);
+   if (ia->spreadsheet == NULL)
+     ia->spreadsheet = ods_probe (file->file_name, false);
+   if (ia->spreadsheet == NULL)
+     {
+     struct string input;
+     struct line_reader *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. */
+ void
+ destroy_file (struct import_assistant *ia)
+ {
+   struct file *f = &ia->file;
+   size_t i;
+   if (f->lines)
+     {
+       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;
+ }
index 0000000000000000000000000000000000000000,d3191e78cac89ed60f1c9680c0c16cdfd2a3a768..8d21e7c55c936e6d74a3a404d4e45205ed129803
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,244 +1,244 @@@
 -#include <gtk-contrib/psppire-sheet.h>
+ /* PSPPIRE - a graphical user interface for PSPP.
+    Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013  Free Software Foundation
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+ #include <config.h>
+ #include "page-first-line.h"
+ #include "ui/gui/text-data-import-dialog.h"
+ #include <errno.h>
+ #include <fcntl.h>
 -#include "ui/gui/psppire-var-store.h"
+ #include <gtk/gtk.h>
+ #include <limits.h>
+ #include <stdlib.h>
+ #include <sys/stat.h>
+ #include "data/data-in.h"
+ #include "data/data-out.h"
+ #include "data/format-guesser.h"
+ #include "data/value-labels.h"
+ #include "language/data-io/data-parser.h"
+ #include "language/lexer/lexer.h"
+ #include "libpspp/assertion.h"
+ #include "libpspp/i18n.h"
+ #include "libpspp/line-reader.h"
+ #include "libpspp/message.h"
+ #include "ui/gui/checkbox-treeview.h"
+ #include "ui/gui/dialog-common.h"
+ #include "ui/gui/executor.h"
+ #include "ui/gui/helper.h"
+ #include "ui/gui/builder-wrapper.h"
++#include "ui/gui/pspp-sheet-selection.h"
++#include "ui/gui/pspp-sheet-view-column.h"
++#include "ui/gui/pspp-sheet-view.h"
+ #include "ui/gui/psppire-data-window.h"
+ #include "ui/gui/psppire-dialog.h"
+ #include "ui/gui/psppire-encoding-selector.h"
+ #include "ui/gui/psppire-empty-list-store.h"
+ #include "ui/gui/psppire-var-sheet.h"
 -    GtkTreeView *tree_view;
+ #include "ui/gui/psppire-scanf.h"
+ #include "gl/error.h"
+ #include "gl/intprops.h"
+ #include "gl/xalloc.h"
+ #include "gettext.h"
+ #define _(msgid) gettext (msgid)
+ #define N_(msgid) msgid
+ /* The "first line" page of the assistant. */
+ /* Page where the user chooses the first line of data. */
+ struct first_line_page
+   {
+     GtkWidget *page;
 -static GtkTreeView *create_lines_tree_view (GtkContainer *parent_window,
 -                                            struct import_assistant *);
++    PsppSheetView *tree_view;
+     GtkWidget *variable_names_cb;
+   };
 -  gtk_tree_selection_set_mode (
 -    gtk_tree_view_get_selection (GTK_TREE_VIEW (p->tree_view)),
 -    GTK_SELECTION_BROWSE);
 -  g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (p->tree_view)),
++static PsppSheetView *create_lines_tree_view (GtkContainer *parent_window,
++                                              struct import_assistant *);
+ static void on_first_line_change (GtkTreeSelection *,
+                                   struct import_assistant *);
+ static void on_variable_names_cb_toggle (GtkToggleButton *,
+                                          struct import_assistant *);
+ static void set_first_line (struct import_assistant *);
+ static void get_first_line (struct import_assistant *);
+ /* Initializes IA's first_line substructure. */
+ struct first_line_page *
+ first_line_page_create (struct import_assistant *ia)
+ {
+   struct first_line_page *p = xzalloc (sizeof *p);
+   GtkBuilder *builder = ia->asst.builder;
+   p->page = add_page_to_assistant (ia, get_widget_assert (builder, "FirstLine"),
+                                    GTK_ASSISTANT_PAGE_CONTENT);
+   gtk_widget_destroy (get_widget_assert (builder, "first-line"));
+   p->tree_view = create_lines_tree_view (
+     GTK_CONTAINER (get_widget_assert (builder, "first-line-scroller")), ia);
+   p->variable_names_cb = get_widget_assert (builder, "variable-names");
 -render_line (GtkTreeViewColumn *tree_column,
++  pspp_sheet_selection_set_mode (
++    pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (p->tree_view)),
++    PSPP_SHEET_SELECTION_BROWSE);
++  pspp_sheet_view_set_rubber_banding (PSPP_SHEET_VIEW (p->tree_view), TRUE);
++  g_signal_connect (pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (p->tree_view)),
+                     "changed", G_CALLBACK (on_first_line_change), ia);
+   g_signal_connect (p->variable_names_cb, "toggled",
+                     G_CALLBACK (on_variable_names_cb_toggle), ia);
+   return p;
+ }
+ /* Resets the first_line page to its initial content. */
+ void
+ reset_first_line_page (struct import_assistant *ia)
+ {
+   ia->skip_lines = 0;
+   ia->variable_names = false;
+   set_first_line (ia);
+ }
+ static void
 -static GtkTreeView *
++render_line (PsppSheetViewColumn *tree_column,
+              GtkCellRenderer *cell,
+              GtkTreeModel *tree_model,
+              GtkTreeIter *iter,
+              gpointer data)
+ {
+   gint row = empty_list_store_iter_to_row (iter);
+   struct string *lines;
+   lines = g_object_get_data (G_OBJECT (tree_model), "lines");
+   g_return_if_fail (lines != NULL);
+   g_object_set (cell, "text", ds_cstr (&lines[row]), NULL);
+ }
+ /* Creates and returns a tree view that contains each of the
+    lines in IA's file as a row. */
 -  GtkTreeView *tree_view = NULL;
 -  GtkTreeViewColumn *column;
++static PsppSheetView *
+ create_lines_tree_view (GtkContainer *parent, struct import_assistant *ia)
+ {
 -  column = gtk_tree_view_column_new_with_attributes (
++  PsppSheetView *tree_view = NULL;
++  PsppSheetViewColumn *column;
+   size_t max_line_length;
+   gint content_width, header_width;
+   size_t i;
+   const gchar *title = _("Text");
+   make_tree_view (ia, 0, &tree_view);
 -  gtk_tree_view_column_set_cell_data_func (column, ia->asst.fixed_renderer,
++  column = pspp_sheet_view_column_new_with_attributes (
+      title, ia->asst.fixed_renderer, (void *) NULL);
 -  gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
++  pspp_sheet_view_column_set_cell_data_func (column, ia->asst.fixed_renderer,
+                                            render_line, NULL, NULL);
 -  gtk_tree_view_column_set_fixed_width (column, MAX (content_width,
++  pspp_sheet_view_column_set_resizable (column, TRUE);
+   max_line_length = 0;
+   for (i = 0; i < ia->file.line_cnt; i++)
+     {
+       size_t w = ds_length (&ia->file.lines[i]);
+       max_line_length = MAX (max_line_length, w);
+     }
+   content_width = get_monospace_width (tree_view, ia->asst.fixed_renderer,
+                                        max_line_length);
+   header_width = get_string_width (tree_view, ia->asst.prop_renderer, title);
 -  gtk_tree_view_append_column (tree_view, column);
 -
 -  gtk_tree_view_set_fixed_height_mode (tree_view, true);
++  pspp_sheet_view_column_set_fixed_width (column, MAX (content_width,
+                                                      header_width));
 -  gtk_tree_view_set_cursor (GTK_TREE_VIEW (ia->first_line->tree_view),
++  pspp_sheet_view_append_column (tree_view, column);
+   gtk_container_add (parent, GTK_WIDGET (tree_view));
+   gtk_widget_show (GTK_WIDGET (tree_view));
+   return tree_view;
+ }
+ /* Called when the line selected in the first_line tree view
+    changes. */
+ static void
+ on_first_line_change (GtkTreeSelection *selection UNUSED,
+                       struct import_assistant *ia)
+ {
+   get_first_line (ia);
+ }
+ /* Called when the checkbox that indicates whether variable
+    names are in the row above the first line is toggled. */
+ static void
+ on_variable_names_cb_toggle (GtkToggleButton *variable_names_cb UNUSED,
+                              struct import_assistant *ia)
+ {
+   get_first_line (ia);
+ }
+ /* Sets the widgets to match IA's first_line substructure. */
+ static void
+ set_first_line (struct import_assistant *ia)
+ {
+   GtkTreePath *path;
+   path = gtk_tree_path_new_from_indices (ia->skip_lines, -1);
 -  GtkTreeSelection *selection;
++  pspp_sheet_view_set_cursor (PSPP_SHEET_VIEW (ia->first_line->tree_view),
+                             path, NULL, false);
+   gtk_tree_path_free (path);
+   gtk_toggle_button_set_active (
+     GTK_TOGGLE_BUTTON (ia->first_line->variable_names_cb),
+     ia->variable_names);
+   gtk_widget_set_sensitive (ia->first_line->variable_names_cb,
+                             ia->skip_lines > 0);
+ }
+ /* Sets IA's first_line substructure to match the widgets. */
+ static void
+ get_first_line (struct import_assistant *ia)
+ {
 -  selection = gtk_tree_view_get_selection (ia->first_line->tree_view);
 -  if (gtk_tree_selection_get_selected (selection, &model, &iter))
++  PsppSheetSelection *selection;
+   GtkTreeIter iter;
+   GtkTreeModel *model;
++  selection = pspp_sheet_view_get_selection (ia->first_line->tree_view);
++  if (pspp_sheet_selection_get_selected (selection, &model, &iter))
+     {
+       GtkTreePath *path = gtk_tree_model_get_path (model, &iter);
+       int row = gtk_tree_path_get_indices (path)[0];
+       gtk_tree_path_free (path);
+       ia->skip_lines = row;
+       ia->variable_names =
+         (ia->skip_lines > 0
+          && gtk_toggle_button_get_active (
+            GTK_TOGGLE_BUTTON (ia->first_line->variable_names_cb)));
+     }
+   gtk_widget_set_sensitive (ia->first_line->variable_names_cb,
+                             ia->skip_lines > 0);
+ }
+ void
+ first_line_append_syntax (const struct import_assistant *ia, struct string *s)
+ {
+   if (ia->skip_lines > 0)
+     ds_put_format (s, "  /FIRSTCASE=%d\n", ia->skip_lines + 1);
+ }
index 0000000000000000000000000000000000000000,6a2109d44af9f1ddc7382447eaa1382e2f791c43..5b06bcb3d4fac2ad1d1d9d1d23dddc637621efcc
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,292 +1,288 @@@
 -#include <gtk-contrib/psppire-sheet.h>
+ /* PSPPIRE - a graphical user interface for PSPP.
+    Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013  Free Software Foundation
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+ #include <config.h>
+ #include "ui/gui/text-data-import-dialog.h"
+ #include <errno.h>
+ #include <fcntl.h>
 -#include "ui/gui/psppire-var-store.h"
+ #include <gtk/gtk.h>
+ #include <limits.h>
+ #include <stdlib.h>
+ #include <sys/stat.h>
+ #include "data/data-in.h"
+ #include "data/data-out.h"
+ #include "data/format-guesser.h"
+ #include "data/value-labels.h"
+ #include "language/data-io/data-parser.h"
+ #include "language/lexer/lexer.h"
+ #include "libpspp/assertion.h"
+ #include "libpspp/i18n.h"
+ #include "libpspp/line-reader.h"
+ #include "libpspp/message.h"
+ #include "ui/gui/checkbox-treeview.h"
+ #include "ui/gui/dialog-common.h"
+ #include "ui/gui/executor.h"
+ #include "ui/gui/helper.h"
+ #include "ui/gui/builder-wrapper.h"
+ #include "ui/gui/psppire-data-window.h"
+ #include "ui/gui/psppire-dialog.h"
+ #include "ui/gui/psppire-encoding-selector.h"
+ #include "ui/gui/psppire-empty-list-store.h"
+ #include "ui/gui/psppire-var-sheet.h"
 -    GtkTreeView *data_tree_view;
+ #include "ui/gui/psppire-scanf.h"
+ #include "gl/error.h"
+ #include "gl/intprops.h"
+ #include "gl/xalloc.h"
+ #include "gettext.h"
+ #define _(msgid) gettext (msgid)
+ #define N_(msgid) msgid
+ /* Page where the user verifies and adjusts input formats. */
+ struct formats_page
+   {
+     GtkWidget *page;
 -  p->data_tree_view = GTK_TREE_VIEW (get_widget_assert (builder, "data"));
++    PsppSheetView *data_tree_view;
+     PsppireDict *psppire_dict;
+     struct variable **modified_vars;
+     size_t modified_var_cnt;
+   };
+ /* The "formats" page of the assistant. */
+ static void on_variable_change (PsppireDict *dict, int idx,
+                                 struct import_assistant *);
+ static void clear_modified_vars (struct import_assistant *);
+ /* Initializes IA's formats substructure. */
+ struct formats_page *
+ formats_page_create (struct import_assistant *ia)
+ {
+   GtkBuilder *builder = ia->asst.builder;
+   struct formats_page *p = xzalloc (sizeof *p);
+   p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Formats"),
+                        GTK_ASSISTANT_PAGE_CONFIRM);
 -  PsppireVarStore *var_store;
++  p->data_tree_view = PSPP_SHEET_VIEW (get_widget_assert (builder, "data"));
+   p->modified_vars = NULL;
+   p->modified_var_cnt = 0;
+   return p;
+ }
+ /* Frees IA's formats substructure. */
+ void
+ destroy_formats_page (struct import_assistant *ia)
+ {
+   struct formats_page *p = ia->formats;
+   if (p->psppire_dict != NULL)
+     {
+       dict_destroy (p->psppire_dict->dict);
+       g_object_unref (p->psppire_dict);
+     }
+   clear_modified_vars (ia);
+ }
+ /* Called just before the formats page of the assistant is
+    displayed. */
+ void
+ prepare_formats_page (struct import_assistant *ia)
+ {
+   struct dictionary *dict;
+   PsppireDict *psppire_dict;
 -  var_store = psppire_var_store_new (psppire_dict);
 -  g_object_set (var_store,
 -                "format-type", PSPPIRE_VAR_STORE_INPUT_FORMATS,
 -                (void *) NULL);
+   GtkBin *vars_scroller;
+   GtkWidget *old_var_sheet;
+   PsppireVarSheet *var_sheet;
+   struct formats_page *p = ia->formats;
+   struct fmt_guesser *fg;
+   unsigned long int number = 0;
+   size_t column_idx;
+   push_watch_cursor (ia);
+   dict = dict_create (get_default_encoding ());
+   fg = fmt_guesser_create ();
+   for (column_idx = 0; column_idx < ia->column_cnt; column_idx++)
+     {
+       struct variable *modified_var = 
+       (column_idx < p->modified_var_cnt ? p->modified_vars[column_idx] : NULL);
+       if (modified_var == NULL)
+         {
+           struct column *column = &ia->columns[column_idx];
+           struct variable *var;
+           struct fmt_spec format;
+           char *name;
+           size_t row;
+           /* Choose variable name. */
+           name = dict_make_unique_var_name (dict, column->name, &number);
+           /* Choose variable format. */
+           fmt_guesser_clear (fg);
+           for (row = ia->skip_lines; row < ia->file.line_cnt; row++)
+             fmt_guesser_add (fg, column->contents[row]);
+           fmt_guesser_guess (fg, &format);
+           fmt_fix_input (&format);
+           /* Create variable. */
+           var = dict_create_var_assert (dict, name, fmt_var_width (&format));
+           var_set_both_formats (var, &format);
+           free (name);
+         }
+       else
+         {
+           char *name;
+           name = dict_make_unique_var_name (dict, var_get_name (modified_var),
+                                             &number);
+           dict_clone_var_as_assert (dict, modified_var, name);
+           free (name);
+         }
+     }
+   fmt_guesser_destroy (fg);
+   psppire_dict = psppire_dict_new_from_dict (dict);
+   g_signal_connect (psppire_dict, "variable_changed",
+                     G_CALLBACK (on_variable_change), ia);
+   ia->dict = dict;
+   ia->formats->psppire_dict = psppire_dict;
+   /* XXX: PsppireVarStore doesn't hold a reference to
+      psppire_dict for now, but it should.  After it does, we
+      should g_object_ref the psppire_dict here, since we also
+      hold a reference via ia->formats->dict. */
 -                "model", var_store,
+   var_sheet = PSPPIRE_VAR_SHEET (psppire_var_sheet_new ());
+   g_object_set (var_sheet,
 -  GtkTreeView *tv = ia->formats->data_tree_view;
++                "dictionary", psppire_dict,
+                 "may-create-vars", FALSE,
++                "may-delete-vars", FALSE,
++                "format-use", FMT_FOR_INPUT,
++                "enable-grid-lines", PSPP_SHEET_VIEW_GRID_LINES_BOTH,
+                 (void *) NULL);
+   vars_scroller = GTK_BIN (get_widget_assert (ia->asst.builder, "vars-scroller"));
+   old_var_sheet = gtk_bin_get_child (vars_scroller);
+   if (old_var_sheet != NULL)
+     gtk_widget_destroy (old_var_sheet);
+   gtk_container_add (GTK_CONTAINER (vars_scroller), GTK_WIDGET (var_sheet));
+   gtk_widget_show (GTK_WIDGET (var_sheet));
+   gtk_widget_destroy (GTK_WIDGET (ia->formats->data_tree_view));
+   ia->formats->data_tree_view = create_data_tree_view (
+     false,
+     GTK_CONTAINER (get_widget_assert (ia->asst.builder, "data-scroller")),
+     ia);
+   gtk_widget_show (ia->asst.paste_button);
+   pop_watch_cursor (ia);
+ }
+ /* Clears the set of user-modified variables from IA's formats
+    substructure.  This discards user modifications to variable
+    formats, thereby causing formats to revert to their
+    defaults.  */
+ static void
+ clear_modified_vars (struct import_assistant *ia)
+ {
+   struct formats_page *p = ia->formats;
+   size_t i;
+   for (i = 0; i < p->modified_var_cnt; i++)
+     var_destroy (p->modified_vars[i]);
+   free (p->modified_vars);
+   p->modified_vars = NULL;
+   p->modified_var_cnt = 0;
+ }
+ /* Resets the formats page to its defaults, discarding user
+    modifications. */
+ void
+ reset_formats_page (struct import_assistant *ia)
+ {
+   clear_modified_vars (ia);
+   prepare_formats_page (ia);
+ }
+ /* Called when the user changes one of the variables in the
+    dictionary. */
+ static void
+ on_variable_change (PsppireDict *dict, int dict_idx,
+                     struct import_assistant *ia)
+ {
+   struct formats_page *p = ia->formats;
 -  gtk_tree_view_remove_column (tv, gtk_tree_view_get_column (tv, column_idx));
 -  gtk_tree_view_insert_column (tv, make_data_column (ia, tv, false, dict_idx),
 -                               column_idx);
++  PsppSheetView *tv = ia->formats->data_tree_view;
+   gint column_idx = dict_idx + 1;
+   push_watch_cursor (ia);
+   /* Remove previous column and replace with new column. */
++  pspp_sheet_view_remove_column (tv, pspp_sheet_view_get_column (tv, column_idx));
++  pspp_sheet_view_insert_column (tv, make_data_column (ia, tv, false, dict_idx),
++                                 column_idx);
+   /* Save a copy of the modified variable in modified_vars, so
+      that its attributes will be preserved if we back up to the
+      previous page with the Prev button and then come back
+      here. */
+   if (dict_idx >= p->modified_var_cnt)
+     {
+       size_t i;
+       p->modified_vars = xnrealloc (p->modified_vars, dict_idx + 1,
+                                     sizeof *p->modified_vars);
+       for (i = 0; i <= dict_idx; i++)
+         p->modified_vars[i] = NULL;
+       p->modified_var_cnt = dict_idx + 1;
+     }
+   if (p->modified_vars[dict_idx])
+     var_destroy (p->modified_vars[dict_idx]);
+   p->modified_vars[dict_idx]
+     = var_clone (psppire_dict_get_variable (dict, dict_idx));
+   pop_watch_cursor (ia);
+ }
+ void
+ formats_append_syntax (const struct import_assistant *ia, struct string *s)
+ {
+   int i;
+   int var_cnt;
+   ds_put_cstr (s, "  /VARIABLES=\n");
+   
+   var_cnt = dict_get_var_cnt (ia->dict);
+   for (i = 0; i < var_cnt; i++)
+     {
+       struct variable *var = dict_get_var (ia->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 ? "." : "");
+     }
+ }
index 0000000000000000000000000000000000000000,e0d51f537c5066c902ee2d9c89dd5ef6993dc1b8..59c252b46cb2ade898b2808b2db41543a9e08c9f
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,200 +1,198 @@@
 -#include <gtk-contrib/psppire-sheet.h>
+ /* PSPPIRE - a graphical user interface for PSPP.
+    Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013  Free Software Foundation
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+ #include <config.h>
+ #include "page-intro.h"
+ #include "ui/gui/text-data-import-dialog.h"
+ #include <errno.h>
+ #include <fcntl.h>
 -#include "ui/gui/psppire-var-store.h"
+ #include <gtk/gtk.h>
+ #include <limits.h>
+ #include <stdlib.h>
+ #include <sys/stat.h>
+ #include "data/data-in.h"
+ #include "data/data-out.h"
+ #include "data/format-guesser.h"
+ #include "data/value-labels.h"
+ #include "language/data-io/data-parser.h"
+ #include "language/lexer/lexer.h"
+ #include "libpspp/assertion.h"
+ #include "libpspp/i18n.h"
+ #include "libpspp/line-reader.h"
+ #include "libpspp/message.h"
+ #include "ui/gui/checkbox-treeview.h"
+ #include "ui/gui/dialog-common.h"
+ #include "ui/gui/executor.h"
+ #include "ui/gui/helper.h"
+ #include "ui/gui/builder-wrapper.h"
+ #include "ui/gui/psppire-data-window.h"
+ #include "ui/gui/psppire-dialog.h"
+ #include "ui/gui/psppire-encoding-selector.h"
+ #include "ui/gui/psppire-empty-list-store.h"
+ #include "ui/gui/psppire-var-sheet.h"
+ #include "ui/gui/psppire-scanf.h"
+ #include "ui/syntax-gen.h"
+ #include "gl/error.h"
+ #include "gl/intprops.h"
+ #include "gl/xalloc.h"
+ #include "gettext.h"
+ #define _(msgid) gettext (msgid)
+ #define N_(msgid) msgid
+ struct import_assistant;
\f
+ /* The "intro" page of the assistant. */
+ /* The introduction page of the assistant. */
+ struct intro_page
+   {
+     GtkWidget *page;
+     GtkWidget *all_cases_button;
+     GtkWidget *n_cases_button;
+     GtkWidget *n_cases_spin;
+     GtkWidget *percent_button;
+     GtkWidget *percent_spin;
+   };
+ static void on_intro_amount_changed (struct intro_page *);
+ /* Initializes IA's intro substructure. */
+ struct intro_page *
+ intro_page_create (struct import_assistant *ia)
+ {
+   GtkBuilder *builder = ia->asst.builder;
+   struct string s;
+   GtkWidget *hbox_n_cases ;
+   GtkWidget *hbox_percent ;
+   GtkWidget *table ;
+   struct intro_page *p = xzalloc (sizeof (*p));
+   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), p);
+   g_signal_connect_swapped (p->n_cases_button, "toggled",
+                     G_CALLBACK (on_intro_amount_changed), p);
+   g_signal_connect_swapped (p->percent_button, "toggled",
+                     G_CALLBACK (on_intro_amount_changed), p);
+   on_intro_amount_changed (p);
+   ds_init_empty (&s);
+   ds_put_cstr (&s, _("This assistant will guide you through the process of "
+                      "importing data into PSPP from a text file with one line "
+                      "per case,  in which fields are separated by tabs, "
+                      "commas, or other delimiters.\n\n"));
+   if (ia->file.total_is_exact)
+     ds_put_format (
+       &s, ngettext ("The selected file contains %zu line of text.  ",
+                     "The selected file contains %zu lines of text.  ",
+                     ia->file.line_cnt),
+       ia->file.line_cnt);
+   else if (ia->file.total_lines > 0)
+     {
+       ds_put_format (
+         &s, ngettext (
+           "The selected file contains approximately %lu line of text.  ",
+           "The selected file contains approximately %lu lines of text.  ",
+           ia->file.total_lines),
+         ia->file.total_lines);
+       ds_put_format (
+         &s, ngettext (
+           "Only the first %zu line of the file will be shown for "
+           "preview purposes in the following screens.  ",
+           "Only the first %zu lines of the file will be shown for "
+           "preview purposes in the following screens.  ",
+           ia->file.line_cnt),
+         ia->file.line_cnt);
+     }
+   ds_put_cstr (&s, _("You may choose below how much of the file should "
+                      "actually be imported."));
+   gtk_label_set_text (GTK_LABEL (get_widget_assert (builder, "intro-label")),
+                       ds_cstr (&s));
+   ds_destroy (&s);
+   return p;
+ }
+ /* Resets IA's intro page to its initial state. */
+ void
+ reset_intro_page (struct import_assistant *ia)
+ {
+   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->intro->all_cases_button),
+                                 true);
+ }
+ /* Called when one of the radio buttons is clicked. */
+ static void
+ on_intro_amount_changed (struct intro_page *p)
+ {
+   gtk_widget_set_sensitive (p->n_cases_spin,
+                             gtk_toggle_button_get_active (
+                               GTK_TOGGLE_BUTTON (p->n_cases_button)));
+   gtk_widget_set_sensitive (p->percent_spin,
+                             gtk_toggle_button_get_active (
+                               GTK_TOGGLE_BUTTON (p->percent_button)));
+ }
+ void
+ intro_append_syntax (const struct intro_page *p, struct string *s)
+ {
+   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (p->n_cases_button)))
+     ds_put_format (s, "  /IMPORTCASES=FIRST %d\n",
+                  gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (p->n_cases_spin)));
+   else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (p->percent_button)))
+     ds_put_format (s, "  /IMPORTCASES=PERCENT %d\n",
+                  gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (p->percent_spin)));
+   else
+     ds_put_cstr (s, "  /IMPORTCASES=ALL\n");
+ }
index 0000000000000000000000000000000000000000,e147881730e710e6b70472806a2430c241071efa..f1e08a6b594b3e74731faa51fbb5d5acc80432a2
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,616 +1,614 @@@
 -#include <gtk-contrib/psppire-sheet.h>
+ /* PSPPIRE - a graphical user interface for PSPP.
+    Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013  Free Software Foundation
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+ #include <config.h>
+ #include "page-separators.h"
+ #include "ui/gui/text-data-import-dialog.h"
+ #include <errno.h>
+ #include <fcntl.h>
 -#include "ui/gui/psppire-var-store.h"
+ #include <gtk/gtk.h>
+ #include <limits.h>
+ #include <stdlib.h>
+ #include <sys/stat.h>
+ #include "data/data-in.h"
+ #include "data/data-out.h"
+ #include "data/format-guesser.h"
+ #include "data/value-labels.h"
+ #include "language/data-io/data-parser.h"
+ #include "language/lexer/lexer.h"
+ #include "libpspp/assertion.h"
+ #include "libpspp/i18n.h"
+ #include "libpspp/line-reader.h"
+ #include "libpspp/message.h"
+ #include "ui/gui/checkbox-treeview.h"
+ #include "ui/gui/dialog-common.h"
+ #include "ui/gui/executor.h"
+ #include "ui/gui/helper.h"
+ #include "ui/gui/builder-wrapper.h"
+ #include "ui/gui/psppire-data-window.h"
+ #include "ui/gui/psppire-dialog.h"
+ #include "ui/gui/psppire-encoding-selector.h"
+ #include "ui/gui/psppire-empty-list-store.h"
+ #include "ui/gui/psppire-var-sheet.h"
 -    GtkTreeView *fields_tree_view;
+ #include "ui/gui/psppire-scanf.h"
+ #include "ui/syntax-gen.h"
+ #include "gl/error.h"
+ #include "gl/intprops.h"
+ #include "gl/xalloc.h"
+ #include "gettext.h"
+ #define _(msgid) gettext (msgid)
+ #define N_(msgid) msgid
+ /* Page where the user chooses field separators. */
+ struct separators_page
+   {
+     /* How to break lines into columns. */
+     struct string separators;   /* Field separators. */
+     struct string quotes;       /* Quote characters. */
+     bool escape;                /* Doubled quotes yield a quote mark? */
+     GtkWidget *page;
+     GtkWidget *custom_cb;
+     GtkWidget *custom_entry;
+     GtkWidget *quote_cb;
+     GtkWidget *quote_combo;
+     GtkEntry *quote_entry;
+     GtkWidget *escape_cb;
 -  p->fields_tree_view = GTK_TREE_VIEW (get_widget_assert (builder, "fields"));
++    PsppSheetView *fields_tree_view;
+   };
+ /* The "separators" page of the assistant. */
+ static void revise_fields_preview (struct import_assistant *ia);
+ static void choose_likely_separators (struct import_assistant *ia);
+ static void find_commonest_chars (unsigned long int histogram[UCHAR_MAX + 1],
+                                   const char *targets, const char *def,
+                                   struct string *result);
+ static void clear_fields (struct import_assistant *ia);
+ static void revise_fields_preview (struct import_assistant *);
+ static void set_separators (struct import_assistant *);
+ static void get_separators (struct import_assistant *);
+ static void on_separators_custom_entry_notify (GObject *UNUSED,
+                                                GParamSpec *UNUSED,
+                                                struct import_assistant *);
+ static void on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
+                                             struct import_assistant *);
+ static void on_quote_combo_change (GtkComboBox *combo,
+                                    struct import_assistant *);
+ static void on_quote_cb_toggle (GtkToggleButton *quote_cb,
+                                 struct import_assistant *);
+ static void on_separator_toggle (GtkToggleButton *, struct import_assistant *);
+ /* A common field separator and its identifying name. */
+ struct separator
+   {
+     const char *name;           /* Name (for use with get_widget_assert). */
+     int c;                      /* Separator character. */
+   };
+ /* All the separators in the dialog box. */
+ static const struct separator separators[] =
+   {
+     {"space", ' '},
+     {"tab", '\t'},
+     {"bang", '!'},
+     {"colon", ':'},
+     {"comma", ','},
+     {"hyphen", '-'},
+     {"pipe", '|'},
+     {"semicolon", ';'},
+     {"slash", '/'},
+   };
+ #define SEPARATOR_CNT (sizeof separators / sizeof *separators)
+ static void
+ set_quote_list (GtkComboBoxEntry *cb)
+ {
+   GtkListStore *list =  gtk_list_store_new (1, G_TYPE_STRING);
+   GtkTreeIter iter;
+   gint i;
+   const gchar *seperator[3] = {"'\"", "\'", "\""};
+   for (i = 0; i < 3; i++)
+     {
+       const gchar *s = seperator[i];
+       /* Add a new row to the model */
+       gtk_list_store_append (list, &iter);
+       gtk_list_store_set (list, &iter,
+                           0, s,
+                           -1);
+     }
+   gtk_combo_box_set_model (GTK_COMBO_BOX (cb), GTK_TREE_MODEL (list));
+   g_object_unref (list);
+   gtk_combo_box_entry_set_text_column (cb, 0);
+ }
+ /* Initializes IA's separators substructure. */
+ struct separators_page *
+ separators_page_create (struct import_assistant *ia)
+ {
+   GtkBuilder *builder = ia->asst.builder;
+   size_t i;
+   struct separators_page *p = xzalloc (sizeof *p);
+   p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Separators"),
+                                    GTK_ASSISTANT_PAGE_CONTENT);
+   p->custom_cb = get_widget_assert (builder, "custom-cb");
+   p->custom_entry = get_widget_assert (builder, "custom-entry");
+   p->quote_combo = get_widget_assert (builder, "quote-combo");
+   p->quote_entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (p->quote_combo)));
+   p->quote_cb = get_widget_assert (builder, "quote-cb");
+   p->escape_cb = get_widget_assert (builder, "escape");
+   set_quote_list (GTK_COMBO_BOX_ENTRY (p->quote_combo));
++  p->fields_tree_view = PSPP_SHEET_VIEW (get_widget_assert (builder, "fields"));
+   g_signal_connect (p->quote_combo, "changed",
+                     G_CALLBACK (on_quote_combo_change), ia);
+   g_signal_connect (p->quote_cb, "toggled",
+                     G_CALLBACK (on_quote_cb_toggle), ia);
+   g_signal_connect (p->custom_entry, "notify::text",
+                     G_CALLBACK (on_separators_custom_entry_notify), ia);
+   g_signal_connect (p->custom_cb, "toggled",
+                     G_CALLBACK (on_separators_custom_cb_toggle), ia);
+   for (i = 0; i < SEPARATOR_CNT; i++)
+     g_signal_connect (get_widget_assert (builder, separators[i].name),
+                       "toggled", G_CALLBACK (on_separator_toggle), ia);
+   g_signal_connect (p->escape_cb, "toggled",
+                     G_CALLBACK (on_separator_toggle), ia);
+   return p;
+ }
+ /* Frees IA's separators substructure. */
+ void
+ destroy_separators_page (struct import_assistant *ia)
+ {
+   struct separators_page *s = ia->separators;
+   ds_destroy (&s->separators);
+   ds_destroy (&s->quotes);
+   clear_fields (ia);
+ }
+ /* Called just before the separators page becomes visible in the
+    assistant. */
+ void
+ prepare_separators_page (struct import_assistant *ia)
+ {
+   revise_fields_preview (ia);
+ }
+ /* Called when the Reset button is clicked on the separators
+    page, resets the separators to the defaults. */
+ void
+ reset_separators_page (struct import_assistant *ia)
+ {
+   choose_likely_separators (ia);
+   set_separators (ia);
+ }
+ /* Frees and clears the column data in IA's separators
+    substructure. */
+ static void
+ clear_fields (struct import_assistant *ia)
+ {
+   if (ia->column_cnt > 0)
+     {
+       struct column *col;
+       size_t row;
+       for (row = 0; row < ia->file.line_cnt; row++)
+         {
+           const struct string *line = &ia->file.lines[row];
+           const char *line_start = ds_data (line);
+           const char *line_end = ds_end (line);
+           for (col = ia->columns; col < &ia->columns[ia->column_cnt]; col++)
+             {
+               char *s = ss_data (col->contents[row]);
+               if (!(s >= line_start && s <= line_end))
+                 ss_dealloc (&col->contents[row]);
+             }
+         }
+       for (col = ia->columns; col < &ia->columns[ia->column_cnt]; col++)
+         {
+           free (col->name);
+           free (col->contents);
+         }
+       free (ia->columns);
+       ia->columns = NULL;
+       ia->column_cnt = 0;
+     }
+ }
+ /* Breaks the file data in IA into columns based on the
+    separators set in IA's separators substructure. */
+ static void
+ split_fields (struct import_assistant *ia)
+ {
+   struct separators_page *s = ia->separators;
+   size_t columns_allocated;
+   bool space_sep;
+   size_t row;
+   clear_fields (ia);
+   /* Is space in the set of separators? */
+   space_sep = ss_find_byte (ds_ss (&s->separators), ' ') != SIZE_MAX;
+   /* Split all the lines, not just those from
+      ia->first_line.skip_lines on, so that we split the line that
+      contains variables names if ia->first_line.variable_names is
+      true. */
+   columns_allocated = 0;
+   for (row = 0; row < ia->file.line_cnt; row++)
+     {
+       struct string *line = &ia->file.lines[row];
+       struct substring text = ds_ss (line);
+       size_t column_idx;
+       for (column_idx = 0; ; column_idx++)
+         {
+           struct substring field;
+           struct column *column;
+           if (space_sep)
+             ss_ltrim (&text, ss_cstr (" "));
+           if (ss_is_empty (text))
+             {
+               if (column_idx != 0)
+                 break;
+               field = text;
+             }
+           else if (!ds_is_empty (&s->quotes)
+                    && ds_find_byte (&s->quotes, text.string[0]) != SIZE_MAX)
+             {
+               int quote = ss_get_byte (&text);
+               if (!s->escape)
+                 ss_get_until (&text, quote, &field);
+               else
+                 {
+                   struct string s;
+                   int c;
+                   ds_init_empty (&s);
+                   while ((c = ss_get_byte (&text)) != EOF)
+                     if (c != quote)
+                       ds_put_byte (&s, c);
+                     else if (ss_match_byte (&text, quote))
+                       ds_put_byte (&s, quote);
+                     else
+                       break;
+                   field = ds_ss (&s);
+                 }
+             }
+           else
+             ss_get_bytes (&text, ss_cspan (text, ds_ss (&s->separators)),
+                           &field);
+           if (column_idx >= ia->column_cnt)
+             {
+               struct column *column;
+               if (ia->column_cnt >= columns_allocated)
+                 ia->columns = x2nrealloc (ia->columns, &columns_allocated,
+                                          sizeof *ia->columns);
+               column = &ia->columns[ia->column_cnt++];
+               column->name = NULL;
+               column->width = 0;
+               column->contents = xcalloc (ia->file.line_cnt,
+                                           sizeof *column->contents);
+             }
+           column = &ia->columns[column_idx];
+           column->contents[row] = field;
+           if (ss_length (field) > column->width)
+             column->width = ss_length (field);
+           if (space_sep)
+             ss_ltrim (&text, ss_cstr (" "));
+           if (ss_is_empty (text))
+             break;
+           if (ss_find_byte (ds_ss (&s->separators), ss_first (text))
+               != SIZE_MAX)
+             ss_advance (&text, 1);
+         }
+     }
+ }
+ /* Chooses a name for each column on the separators page */
+ static void
+ choose_column_names (struct import_assistant *ia)
+ {
+   struct dictionary *dict;
+   unsigned long int generated_name_count = 0;
+   struct column *col;
+   size_t name_row;
+   dict = dict_create (get_default_encoding ());
+   name_row = ia->variable_names && ia->skip_lines ? ia->skip_lines : 0;
+   for (col = ia->columns; col < &ia->columns[ia->column_cnt]; col++)
+     {
+       char *hint, *name;
+       hint = name_row ? ss_xstrdup (col->contents[name_row - 1]) : NULL;
+       name = dict_make_unique_var_name (dict, hint, &generated_name_count);
+       free (hint);
+       col->name = name;
+       dict_create_var_assert (dict, name, 0);
+     }
+   dict_destroy (dict);
+ }
+ /* Picks the most likely separator and quote characters based on
+    IA's file data. */
+ static void
+ choose_likely_separators (struct import_assistant *ia)
+ {
+   unsigned long int histogram[UCHAR_MAX + 1] = { 0 };
+   size_t row;
+   /* Construct a histogram of all the characters used in the
+      file. */
+   for (row = 0; row < ia->file.line_cnt; row++)
+     {
+       struct substring line = ds_ss (&ia->file.lines[row]);
+       size_t length = ss_length (line);
+       size_t i;
+       for (i = 0; i < length; i++)
+         histogram[(unsigned char) line.string[i]]++;
+     }
+   find_commonest_chars (histogram, "\"'", "", &ia->separators->quotes);
+   find_commonest_chars (histogram, ",;:/|!\t-", ",", &ia->separators->separators);
+   ia->separators->escape = true;
+ }
+ /* Chooses the most common character among those in TARGETS,
+    based on the frequency data in HISTOGRAM, and stores it in
+    RESULT.  If there is a tie for the most common character among
+    those in TARGETS, the earliest character is chosen.  If none
+    of the TARGETS appear at all, then DEF is used as a
+    fallback. */
+ static void
+ find_commonest_chars (unsigned long int histogram[UCHAR_MAX + 1],
+                       const char *targets, const char *def,
+                       struct string *result)
+ {
+   unsigned char max = 0;
+   unsigned long int max_count = 0;
+   for (; *targets != '\0'; targets++)
+     {
+       unsigned char c = *targets;
+       unsigned long int count = histogram[c];
+       if (count > max_count)
+         {
+           max = c;
+           max_count = count;
+         }
+     }
+   if (max_count > 0)
+     {
+       ds_clear (result);
+       ds_put_byte (result, max);
+     }
+   else
+     ds_assign_cstr (result, def);
+ }
+ /* Revises the contents of the fields tree view based on the
+    currently chosen set of separators. */
+ static void
+ revise_fields_preview (struct import_assistant *ia)
+ {
+   GtkWidget *w;
+   push_watch_cursor (ia);
+   w = GTK_WIDGET (ia->separators->fields_tree_view);
+   gtk_widget_destroy (w);
+   get_separators (ia);
+   split_fields (ia);
+   choose_column_names (ia);
+   ia->separators->fields_tree_view = create_data_tree_view (
+     true,
+     GTK_CONTAINER (get_widget_assert (ia->asst.builder, "fields-scroller")),
+     ia);
+   pop_watch_cursor (ia);
+ }
+ /* Sets the widgets to match IA's separators substructure. */
+ static void
+ set_separators (struct import_assistant *ia)
+ {
+   struct separators_page *s = ia->separators;
+   unsigned int seps;
+   struct string custom;
+   bool any_custom;
+   bool any_quotes;
+   size_t i;
+   ds_init_empty (&custom);
+   seps = 0;
+   for (i = 0; i < ds_length (&s->separators); i++)
+     {
+       unsigned char c = ds_at (&s->separators, i);
+       int j;
+       for (j = 0; j < SEPARATOR_CNT; j++)
+         {
+           const struct separator *s = &separators[j];
+           if (s->c == c)
+             {
+               seps += 1u << j;
+               goto next;
+             }
+         }
+       ds_put_byte (&custom, c);
+     next:;
+     }
+   for (i = 0; i < SEPARATOR_CNT; i++)
+     {
+       const struct separator *s = &separators[i];
+       GtkWidget *button = get_widget_assert (ia->asst.builder, s->name);
+       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
+                                     (seps & (1u << i)) != 0);
+     }
+   any_custom = !ds_is_empty (&custom);
+   gtk_entry_set_text (GTK_ENTRY (s->custom_entry), ds_cstr (&custom));
+   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->custom_cb),
+                                 any_custom);
+   gtk_widget_set_sensitive (s->custom_entry, any_custom);
+   ds_destroy (&custom);
+   any_quotes = !ds_is_empty (&s->quotes);
+   gtk_entry_set_text (s->quote_entry,
+                       any_quotes ? ds_cstr (&s->quotes) : "\"");
+   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->quote_cb),
+                                 any_quotes);
+   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->escape_cb),
+                                 s->escape);
+   gtk_widget_set_sensitive (s->quote_combo, any_quotes);
+   gtk_widget_set_sensitive (s->escape_cb, any_quotes);
+ }
+ /* Sets IA's separators substructure to match the widgets. */
+ static void
+ get_separators (struct import_assistant *ia)
+ {
+   struct separators_page *s = ia->separators;
+   int i;
+   ds_clear (&s->separators);
+   for (i = 0; i < SEPARATOR_CNT; i++)
+     {
+       const struct separator *sep = &separators[i];
+       GtkWidget *button = get_widget_assert (ia->asst.builder, sep->name);
+       if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
+         ds_put_byte (&s->separators, sep->c);
+     }
+   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->custom_cb)))
+     ds_put_cstr (&s->separators,
+                  gtk_entry_get_text (GTK_ENTRY (s->custom_entry)));
+   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->quote_cb)))
+     {
+       gchar *text = gtk_combo_box_get_active_text (
+                       GTK_COMBO_BOX (s->quote_combo));
+       ds_assign_cstr (&s->quotes, text);
+       g_free (text);
+     }
+   else
+     ds_clear (&s->quotes);
+   s->escape = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->escape_cb));
+ }
+ /* Called when the user changes the entry field for custom
+    separators. */
+ static void
+ on_separators_custom_entry_notify (GObject *gobject UNUSED,
+                                    GParamSpec *arg1 UNUSED,
+                                    struct import_assistant *ia)
+ {
+   revise_fields_preview (ia);
+ }
+ /* Called when the user toggles the checkbox that enables custom
+    separators. */
+ static void
+ on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
+                                 struct import_assistant *ia)
+ {
+   bool is_active = gtk_toggle_button_get_active (custom_cb);
+   gtk_widget_set_sensitive (ia->separators->custom_entry, is_active);
+   revise_fields_preview (ia);
+ }
+ /* Called when the user changes the selection in the combo box
+    that selects a quote character. */
+ static void
+ on_quote_combo_change (GtkComboBox *combo, struct import_assistant *ia)
+ {
+   revise_fields_preview (ia);
+ }
+ /* Called when the user toggles the checkbox that enables
+    quoting. */
+ static void
+ on_quote_cb_toggle (GtkToggleButton *quote_cb, struct import_assistant *ia)
+ {
+   bool is_active = gtk_toggle_button_get_active (quote_cb);
+   gtk_widget_set_sensitive (ia->separators->quote_combo, is_active);
+   gtk_widget_set_sensitive (ia->separators->escape_cb, is_active);
+   revise_fields_preview (ia);
+ }
+ /* Called when the user toggles one of the separators
+    checkboxes. */
+ static void
+ on_separator_toggle (GtkToggleButton *toggle UNUSED,
+                      struct import_assistant *ia)
+ {
+   revise_fields_preview (ia);
+ }
+ void 
+ separators_append_syntax (const struct import_assistant *ia, struct string *s)
+ {
+   int i;
+   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");
+ }
index 73c98b383efcd6624809471dffbdf12badcfc25d,500877bdd38470abdda490fb8747746cc00718f6..ce9309b1ce295fd4a8c657090db655b0143eb6a6
@@@ -938,9 -1026,13 +938,9 @@@ psppire_data_window_finish_init (Psppir
                              G_CALLBACK (enable_save), de);
    enable_save (de);
  
 -  connect_action (de, "edit_copy", G_CALLBACK (on_edit_copy));
 -
 -  connect_action (de, "edit_cut", G_CALLBACK (on_edit_cut));
 -
    connect_action (de, "file_new_data", G_CALLBACK (create_data_window));
  
-   connect_action (de, "file_import-text", G_CALLBACK (text_data_import_assistant));
+   connect_action (de, "file_import", G_CALLBACK (text_data_import_assistant));
  
    connect_action (de, "file_save", G_CALLBACK (psppire_window_save));
   
index 4badb6550dfe94b9ce026fd1a4871bce70312909,20bac652ccab69c153e753e2f4684b0922e4114a..ce23093feefa33ea88837236f77fc9a8f43423e7
  
  #include "ui/gui/text-data-import-dialog.h"
  
+ #include "page-intro.h"
+ #include "page-first-line.h"
+ #include "page-separators.h"
+ #include "page-formats.h"
  #include <errno.h>
  #include <fcntl.h>
 -#include <gtk-contrib/psppire-sheet.h>
  #include <gtk/gtk.h>
  #include <limits.h>
  #include <stdlib.h>
  #include "ui/gui/dialog-common.h"
  #include "ui/gui/executor.h"
  #include "ui/gui/helper.h"
+ #include "ui/gui/builder-wrapper.h"
 +#include "ui/gui/pspp-sheet-selection.h"
 +#include "ui/gui/pspp-sheet-view.h"
  #include "ui/gui/psppire-data-window.h"
  #include "ui/gui/psppire-dialog.h"
  #include "ui/gui/psppire-encoding-selector.h"
  #include "ui/gui/psppire-empty-list-store.h"
- #include "ui/gui/psppire-scanf.h"
  #include "ui/gui/psppire-var-sheet.h"
 -#include "ui/gui/psppire-var-store.h"
+ #include "ui/gui/psppire-scanf.h"
  #include "ui/syntax-gen.h"
  
  #include "gl/error.h"
@@@ -63,162 -68,8 +68,8 @@@ struct import_assistant
  static void apply_dict (const struct dictionary *, struct string *);
  static char *generate_syntax (const struct import_assistant *);
  
- static gboolean get_tooltip_location (GtkWidget *widget, gint wx, gint wy,
-                                       const struct import_assistant *,
-                                       size_t *row, size_t *column);
- static void make_tree_view (const struct import_assistant *ia,
-                             size_t first_line,
-                             PsppSheetView **tree_view);
  static void add_line_number_column (const struct import_assistant *,
 -                                    GtkTreeView *);
 +                                    PsppSheetView *);
- static gint get_monospace_width (PsppSheetView *, GtkCellRenderer *,
-                                  size_t char_cnt);
- static gint get_string_width (PsppSheetView *, GtkCellRenderer *,
-                               const char *string);
- static PsppSheetViewColumn *make_data_column (struct import_assistant *,
-                                             PsppSheetView *, bool input,
-                                             gint column_idx);
- static PsppSheetView *create_data_tree_view (bool input, GtkContainer *parent,
-                                            struct import_assistant *);
- static void push_watch_cursor (struct import_assistant *);
- static void pop_watch_cursor (struct import_assistant *);
  
  /* Pops up the Text Data Import assistant. */
  void
@@@ -354,701 -222,36 +222,36 @@@ static char 
  generate_syntax (const struct import_assistant *ia)
  {
    struct string s = DS_EMPTY_INITIALIZER;
-   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);
-   on_intro_amount_changed (ia);
-   ds_init_empty (&s);
-   ds_put_cstr (&s, _("This assistant will guide you through the process of "
-                      "importing data into PSPP from a text file with one line "
-                      "per case,  in which fields are separated by tabs, "
-                      "commas, or other delimiters.\n\n"));
-   if (ia->file.total_is_exact)
-     ds_put_format (
-       &s, ngettext ("The selected file contains %zu line of text.  ",
-                     "The selected file contains %zu lines of text.  ",
-                     ia->file.line_cnt),
-       ia->file.line_cnt);
-   else if (ia->file.total_lines > 0)
      {
-       ds_put_format (
-         &s, ngettext (
-           "The selected file contains approximately %lu line of text.  ",
-           "The selected file contains approximately %lu lines of text.  ",
-           ia->file.total_lines),
-         ia->file.total_lines);
-       ds_put_format (
-         &s, ngettext (
-           "Only the first %zu line of the file will be shown for "
-           "preview purposes in the following screens.  ",
-           "Only the first %zu lines of the file will be shown for "
-           "preview purposes in the following screens.  ",
-           ia->file.line_cnt),
-         ia->file.line_cnt);
-     }
-   ds_put_cstr (&s, _("You may choose below how much of the file should "
-                      "actually be imported."));
-   gtk_label_set_text (GTK_LABEL (get_widget_assert (builder, "intro-label")),
-                       ds_cstr (&s));
-   ds_destroy (&s);
- }
- /* Resets IA's intro page to its initial state. */
- static void
- reset_intro_page (struct import_assistant *ia)
- {
-   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->intro.all_cases_button),
-                                 true);
- }
- /* Called when one of the radio buttons is clicked. */
- static void
- on_intro_amount_changed (struct import_assistant *ia)
- {
-   struct intro_page *p = &ia->intro;
-   gtk_widget_set_sensitive (p->n_cases_spin,
-                             gtk_toggle_button_get_active (
-                               GTK_TOGGLE_BUTTON (p->n_cases_button)));
-   gtk_widget_set_sensitive (p->percent_spin,
-                             gtk_toggle_button_get_active (
-                               GTK_TOGGLE_BUTTON (p->percent_button)));
- }
\f
- /* The "first line" page of the assistant. */
- static PsppSheetView *create_lines_tree_view (GtkContainer *parent_window,
-                                             struct import_assistant *);
- static void on_first_line_change (PsppSheetSelection *,
-                                   struct import_assistant *);
- static void on_variable_names_cb_toggle (GtkToggleButton *,
-                                          struct import_assistant *);
- static void set_first_line (struct import_assistant *);
- static void get_first_line (struct import_assistant *);
- /* Initializes IA's first_line substructure. */
- static void
- init_first_line_page (struct import_assistant *ia)
- {
-   struct first_line_page *p = &ia->first_line;
-   GtkBuilder *builder = ia->asst.builder;
-   p->page = add_page_to_assistant (ia, get_widget_assert (builder, "FirstLine"),
-                                    GTK_ASSISTANT_PAGE_CONTENT);
-   gtk_widget_destroy (get_widget_assert (builder, "first-line"));
-   p->tree_view = create_lines_tree_view (
-     GTK_CONTAINER (get_widget_assert (builder, "first-line-scroller")), ia);
-   p->variable_names_cb = get_widget_assert (builder, "variable-names");
-   pspp_sheet_selection_set_mode (
-     pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (p->tree_view)),
-     PSPP_SHEET_SELECTION_BROWSE);
-   pspp_sheet_view_set_rubber_banding (PSPP_SHEET_VIEW (p->tree_view), TRUE);
-   set_first_line (ia);
-   g_signal_connect (pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (p->tree_view)),
-                     "changed", G_CALLBACK (on_first_line_change), ia);
-   g_signal_connect (p->variable_names_cb, "toggled",
-                     G_CALLBACK (on_variable_names_cb_toggle), ia);
- }
- /* Resets the first_line page to its initial content. */
- static void
- reset_first_line_page (struct import_assistant *ia)
- {
-   ia->first_line.skip_lines = 0;
-   ia->first_line.variable_names = false;
-   set_first_line (ia);
- }
+       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);
  
- static void
- render_line (PsppSheetViewColumn *tree_column,
-              GtkCellRenderer *cell,
-              GtkTreeModel *tree_model,
-              GtkTreeIter *iter,
-              gpointer data)
- {
-   gint row = empty_list_store_iter_to_row (iter);
-   struct string *lines;
  
-   lines = g_object_get_data (G_OBJECT (tree_model), "lines");
-   g_return_if_fail (lines != NULL);
+       intro_append_syntax (ia->intro, &s);
  
-   g_object_set (cell, "text", ds_cstr (&lines[row]), NULL);
- }
  
+       ds_put_cstr (&s,
+                  "  /ARRANGEMENT=DELIMITED\n"
+                  "  /DELCASE=LINE\n");
  
- /* Creates and returns a tree view that contains each of the
-    lines in IA's file as a row. */
- static PsppSheetView *
- create_lines_tree_view (GtkContainer *parent, struct import_assistant *ia)
- {
-   PsppSheetView *tree_view;
-   PsppSheetViewColumn *column;
-   size_t max_line_length;
-   gint content_width, header_width;
-   size_t i;
-   const gchar *title = _("Text");
-   make_tree_view (ia, 0, &tree_view);
-   column = pspp_sheet_view_column_new_with_attributes (
-     title, ia->asst.fixed_renderer, (void *) NULL);
-   pspp_sheet_view_column_set_cell_data_func (column, ia->asst.fixed_renderer,
-                                            render_line, NULL, NULL);
-   pspp_sheet_view_column_set_resizable (column, TRUE);
-   max_line_length = 0;
-   for (i = 0; i < ia->file.line_cnt; i++)
-     {
-       size_t w = ds_length (&ia->file.lines[i]);
-       max_line_length = MAX (max_line_length, w);
+       first_line_append_syntax (ia, &s);
+       separators_append_syntax (ia, &s);
+       formats_append_syntax (ia, &s);
+       apply_dict (ia->dict, &s);
      }
  
-   content_width = get_monospace_width (tree_view, ia->asst.fixed_renderer,
-                                        max_line_length);
-   header_width = get_string_width (tree_view, ia->asst.prop_renderer, title);
-   pspp_sheet_view_column_set_fixed_width (column, MAX (content_width,
-                                                      header_width));
-   pspp_sheet_view_append_column (tree_view, column);
-   gtk_container_add (parent, GTK_WIDGET (tree_view));
-   gtk_widget_show (GTK_WIDGET (tree_view));
-   return tree_view;
- }
- /* Called when the line selected in the first_line tree view
-    changes. */
- static void
- on_first_line_change (PsppSheetSelection *selection UNUSED,
-                       struct import_assistant *ia)
- {
-   get_first_line (ia);
- }
- /* Called when the checkbox that indicates whether variable
-    names are in the row above the first line is toggled. */
- static void
- on_variable_names_cb_toggle (GtkToggleButton *variable_names_cb UNUSED,
-                              struct import_assistant *ia)
- {
-   get_first_line (ia);
+   return ds_cstr (&s);
  }
  
- /* Sets the widgets to match IA's first_line substructure. */
- static void
- set_first_line (struct import_assistant *ia)
- {
-   GtkTreePath *path;
  
-   path = gtk_tree_path_new_from_indices (ia->first_line.skip_lines, -1);
-   pspp_sheet_view_set_cursor (PSPP_SHEET_VIEW (ia->first_line.tree_view),
-                             path, NULL, false);
-   gtk_tree_path_free (path);
-   gtk_toggle_button_set_active (
-     GTK_TOGGLE_BUTTON (ia->first_line.variable_names_cb),
-     ia->first_line.variable_names);
-   gtk_widget_set_sensitive (ia->first_line.variable_names_cb,
-                             ia->first_line.skip_lines > 0);
- }
- /* Sets IA's first_line substructure to match the widgets. */
- static void
- get_first_line (struct import_assistant *ia)
- {
-   PsppSheetSelection *selection;
-   GtkTreeIter iter;
-   GtkTreeModel *model;
  
-   selection = pspp_sheet_view_get_selection (ia->first_line.tree_view);
-   if (pspp_sheet_selection_get_selected (selection, &model, &iter))
-     {
-       GtkTreePath *path = gtk_tree_model_get_path (model, &iter);
-       int row = gtk_tree_path_get_indices (path)[0];
-       gtk_tree_path_free (path);
-       ia->first_line.skip_lines = row;
-       ia->first_line.variable_names =
-         (ia->first_line.skip_lines > 0
-          && gtk_toggle_button_get_active (
-            GTK_TOGGLE_BUTTON (ia->first_line.variable_names_cb)));
-     }
-   gtk_widget_set_sensitive (ia->first_line.variable_names_cb,
-                             ia->first_line.skip_lines > 0);
- }
\f
- /* The "separators" page of the assistant. */
- static void revise_fields_preview (struct import_assistant *ia);
- static void choose_likely_separators (struct import_assistant *ia);
- static void find_commonest_chars (unsigned long int histogram[UCHAR_MAX + 1],
-                                   const char *targets, const char *def,
-                                   struct string *result);
- static void clear_fields (struct import_assistant *ia);
- static void revise_fields_preview (struct import_assistant *);
- static void set_separators (struct import_assistant *);
- static void get_separators (struct import_assistant *);
- static void on_separators_custom_entry_notify (GObject *UNUSED,
-                                                GParamSpec *UNUSED,
-                                                struct import_assistant *);
- static void on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
-                                             struct import_assistant *);
- static void on_quote_combo_change (GtkComboBox *combo,
-                                    struct import_assistant *);
- static void on_quote_cb_toggle (GtkToggleButton *quote_cb,
-                                 struct import_assistant *);
- static void on_separator_toggle (GtkToggleButton *, struct import_assistant *);
 -static void render_input_cell (GtkTreeViewColumn *tree_column,
 +static void render_input_cell (PsppSheetViewColumn *tree_column,
                                 GtkCellRenderer *cell,
                                 GtkTreeModel *model, GtkTreeIter *iter,
                                 gpointer ia);
@@@ -1943,9 -468,9 +468,9 @@@ get_tooltip_location (GtkWidget *widget
    if (!gtk_widget_get_mapped (widget))
      return FALSE;
  
 -  gtk_tree_view_convert_widget_to_bin_window_coords (tree_view,
 -                                                     wx, wy, &bx, &by);
 -  if (!gtk_tree_view_get_path_at_pos (tree_view, bx, by,
 +  pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view,
-                                                      wx, wy, &bx, &by);
++                                                       wx, wy, &bx, &by);
 +  if (!pspp_sheet_view_get_path_at_pos (tree_view, bx, by,
                                        &path, &tree_column, NULL, NULL))
      return FALSE;
  
    return TRUE;
  }
  
static void
+ void
  make_tree_view (const struct import_assistant *ia,
                  size_t first_line,
 -                GtkTreeView **tree_view)
 +                PsppSheetView **tree_view)
  {
    GtkTreeModel *model;
  
 -  *tree_view = GTK_TREE_VIEW (gtk_tree_view_new ());
 +  *tree_view = PSPP_SHEET_VIEW (pspp_sheet_view_new ());
 +  pspp_sheet_view_set_grid_lines (*tree_view, PSPP_SHEET_VIEW_GRID_LINES_BOTH);
    model = GTK_TREE_MODEL (psppire_empty_list_store_new (
-                             ia->file.line_cnt - first_line));
+                                                       ia->file.line_cnt - first_line));
    g_object_set_data (G_OBJECT (model), "lines", ia->file.lines + first_line);
    g_object_set_data (G_OBJECT (model), "first-line",
                       GINT_TO_POINTER (first_line));
@@@ -2001,22 -525,23 +526,22 @@@ render_line_number (PsppSheetViewColum
  
  static void
  add_line_number_column (const struct import_assistant *ia,
 -                        GtkTreeView *treeview)
 +                        PsppSheetView *treeview)
  {
 -  GtkTreeViewColumn *column;
 -
 -  column = gtk_tree_view_column_new_with_attributes (
 -                                                   _("Line"), ia->asst.prop_renderer, (void *) NULL);
 -  gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
 -  gtk_tree_view_column_set_fixed_width (
 -                                      column, get_monospace_width (treeview, ia->asst.prop_renderer, 5));
 -  gtk_tree_view_column_set_resizable (column, TRUE);
 -  gtk_tree_view_column_set_cell_data_func (column, ia->asst.prop_renderer,
 -                                           render_line_number, NULL, NULL);
 -  gtk_tree_view_append_column (treeview, column);
 +  PsppSheetViewColumn *column;
 +
 +  column = pspp_sheet_view_column_new_with_attributes (
 +    _("Line"), ia->asst.prop_renderer, (void *) NULL);
 +  pspp_sheet_view_column_set_fixed_width (
 +    column, get_monospace_width (treeview, ia->asst.prop_renderer, 5));
 +  pspp_sheet_view_column_set_resizable (column, TRUE);
 +  pspp_sheet_view_column_set_cell_data_func (column, ia->asst.prop_renderer,
 +                                             render_line_number, NULL, NULL);
 +  pspp_sheet_view_append_column (treeview, column);
  }
  
static gint
+ gint
 -get_monospace_width (GtkTreeView *treeview, GtkCellRenderer *renderer,
 +get_monospace_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
                       size_t char_cnt)
  {
    struct string s;
    return width;
  }
  
static gint
+ gint
 -get_string_width (GtkTreeView *treeview, GtkCellRenderer *renderer,
 +get_string_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
                    const char *string)
  {
    gint width;
    return width;
  }
  
- static PsppSheetViewColumn *
 -GtkTreeViewColumn *
 -make_data_column (struct import_assistant *ia, GtkTreeView *tree_view,
++PsppSheetViewColumn *
 +make_data_column (struct import_assistant *ia, PsppSheetView *tree_view,
                    bool input, gint dict_idx)
  {
    struct variable *var = NULL;
    header_width = get_string_width (tree_view, ia->asst.prop_renderer,
                                     name);
  
 -  tree_column = gtk_tree_view_column_new ();
 +  tree_column = pspp_sheet_view_column_new ();
    g_object_set_data (G_OBJECT (tree_column), "column-number",
                       GINT_TO_POINTER (dict_idx));
 -  gtk_tree_view_column_set_title (tree_column, name);
 -  gtk_tree_view_column_pack_start (tree_column, ia->asst.fixed_renderer,
 -                                   FALSE);
 -  gtk_tree_view_column_set_cell_data_func (
 -                                         tree_column, ia->asst.fixed_renderer,
 -                                         input ? render_input_cell : render_output_cell, ia, NULL);
 -  gtk_tree_view_column_set_sizing (tree_column, GTK_TREE_VIEW_COLUMN_FIXED);
 -  gtk_tree_view_column_set_fixed_width (tree_column, MAX (content_width,
 -                                                          header_width));
 +  pspp_sheet_view_column_set_title (tree_column, name);
 +  pspp_sheet_view_column_pack_start (tree_column, ia->asst.fixed_renderer,
-                                    FALSE);
++                                     FALSE);
 +  pspp_sheet_view_column_set_cell_data_func (
 +    tree_column, ia->asst.fixed_renderer,
 +    input ? render_input_cell : render_output_cell, ia, NULL);
 +  pspp_sheet_view_column_set_fixed_width (tree_column, MAX (content_width,
-                                                           header_width));
-   pspp_sheet_view_column_set_resizable (tree_column, TRUE);
++                                                            header_width));
  
    free (name);
  
    return tree_column;
  }
  
- static PsppSheetView *
 -GtkTreeView *
++PsppSheetView *
  create_data_tree_view (bool input, GtkContainer *parent,
                         struct import_assistant *ia)
  {
 -  GtkTreeView *tree_view;
 +  PsppSheetView *tree_view;
    gint i;
  
-   make_tree_view (ia, ia->first_line.skip_lines, &tree_view);
+   make_tree_view (ia, ia->skip_lines, &tree_view);
 -  gtk_tree_selection_set_mode (gtk_tree_view_get_selection (tree_view),
 -                               GTK_SELECTION_NONE);
 +  pspp_sheet_selection_set_mode (pspp_sheet_view_get_selection (tree_view),
-                                PSPP_SHEET_SELECTION_NONE);
++                                 PSPP_SHEET_SELECTION_NONE);
  
-   for (i = 0; i < ia->separators.column_cnt; i++)
+   for (i = 0; i < ia->column_cnt; i++)
 -    gtk_tree_view_append_column (tree_view,
 -                                 make_data_column (ia, tree_view, input, i));
 +    pspp_sheet_view_append_column (tree_view,
-                                  make_data_column (ia, tree_view, input, i));
++                                   make_data_column (ia, tree_view, input, i));
  
    g_object_set (G_OBJECT (tree_view), "has-tooltip", TRUE, (void *) NULL);
    g_signal_connect (tree_view, "query-tooltip",
                      G_CALLBACK (input ? on_query_input_tooltip
                                  : on_query_output_tooltip), ia);
 -  gtk_tree_view_set_fixed_height_mode (tree_view, true);
 +
    gtk_container_add (parent, GTK_WIDGET (tree_view));
    gtk_widget_show (GTK_WIDGET (tree_view));
  
index 664249c9644ef46cbbae74b6acf9976a94ecedb1,0c026858fe04665f59331ba63ec372474773a2a5..59b5819c3ee873c944bca810623c0c64994de65c
@@@ -22,4 -114,54 +114,54 @@@ GtkWidget * add_page_to_assistant (stru
  
  void text_data_import_assistant (PsppireDataWindow *);
  
 -                            GtkTreeView **tree_view);
+ /* FIXME: Should this be private to first line page ? */
+ void make_tree_view (const struct import_assistant *ia,
+                             size_t first_line,
 -gint get_monospace_width (GtkTreeView *, GtkCellRenderer *,
++                            PsppSheetView **tree_view);
 -gint get_string_width (GtkTreeView *, GtkCellRenderer *,
++gint get_monospace_width (PsppSheetView *, GtkCellRenderer *,
+                                  size_t char_cnt);
 -GtkTreeView *create_data_tree_view (bool input, GtkContainer *parent,
 -                                           struct import_assistant *);
++gint get_string_width (PsppSheetView *, GtkCellRenderer *,
+                               const char *string);
+ void push_watch_cursor (struct import_assistant *);
+ void pop_watch_cursor (struct import_assistant *);
 -GtkTreeViewColumn *make_data_column (struct import_assistant *,
 -                                            GtkTreeView *, bool input,
 -                                            gint column_idx);
++PsppSheetView *create_data_tree_view (bool input, GtkContainer *parent,
++                                      struct import_assistant *);
++PsppSheetViewColumn *make_data_column (struct import_assistant *,
++                                       PsppSheetView *, bool input,
++                                       gint column_idx);
+ void  update_assistant (struct import_assistant *ia);
+ bool init_file (struct import_assistant *ia, GtkWindow *parent_window);
+ void destroy_file (struct import_assistant *ia);
+ void prepare_sheet_spec_page (struct import_assistant *ia);
+ void reset_sheet_spec_page (struct import_assistant *);
+ void post_sheet_spec_page (struct import_assistant *ia);
+ void prepare_first_line_page (struct import_assistant *ia);
+ void reset_first_line_page (struct import_assistant *);
+ void reset_intro_page (struct import_assistant *ia);
+ void prepare_separators_page (struct import_assistant *ia);
+ void reset_separators_page (struct import_assistant *);
+ void destroy_separators_page (struct import_assistant *ia);
+ void prepare_formats_page (struct import_assistant *ia);
+ void reset_formats_page (struct import_assistant *);
+ void destroy_formats_page (struct import_assistant *ia);
+ struct import_assistant * init_assistant (GtkWindow *);
+ void destroy_assistant (struct import_assistant *);
  #endif