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

diff --combined src/ui/gui/automake.mk
index b8d574ffddb0059683c1455f00677dcf81dee0a4,e29e30bb711a337605c2974d73c428b656eff061..ce7864ee2b47416f3e68d1bbbe0f9f01cc4f8f53
@@@ -1,5 -1,7 +1,5 @@@
  ## Process this file with automake to produce Makefile.in  -*- makefile -*-
  
 -include $(top_srcdir)/src/ui/gui/sheet/automake.mk
 -
  UI_FILES = \
        src/ui/gui/aggregate.ui \
        src/ui/gui/autorecode.ui \
@@@ -9,7 -11,6 +9,7 @@@
        src/ui/gui/count.ui \
        src/ui/gui/crosstabs.ui \
        src/ui/gui/chi-square.ui \
 +      src/ui/gui/data-sheet.ui \
        src/ui/gui/descriptives.ui \
        src/ui/gui/entry-dialog.ui \
        src/ui/gui/examine.ui \
@@@ -44,7 -45,6 +44,7 @@@
        src/ui/gui/data-editor.ui \
        src/ui/gui/output-viewer.ui \
        src/ui/gui/syntax-editor.ui \
 +      src/ui/gui/var-sheet.ui \
        src/ui/gui/var-type-dialog.ui
  
  EXTRA_DIST += \
@@@ -74,7 -74,8 +74,7 @@@ endi
  
  
  src_ui_gui_psppire_LDADD = \
 -        src/ui/gui/sheet/libsheet.la \
 -      lib/gtk-contrib/libgtksheet.a \
 +      lib/gtk-contrib/libxpaned.a \
        src/ui/libuicommon.la \
        src/libpspp.la \
        src/libpspp-core.la \
@@@ -187,6 -188,8 +187,6 @@@ src_ui_gui_psppire_SOURCES = 
        src/ui/gui/chi-square-dialog.h \
        src/ui/gui/count-dialog.c \
        src/ui/gui/count-dialog.h \
 -      src/ui/gui/customentry.c \
 -      src/ui/gui/customentry.h \
        src/ui/gui/dialog-common.c \
        src/ui/gui/dialog-common.h \
        src/ui/gui/dict-display.h \
        src/ui/gui/psppire-conf.h \
        src/ui/gui/psppire-data-editor.c \
        src/ui/gui/psppire-data-editor.h \
 +      src/ui/gui/psppire-data-sheet.c \
 +      src/ui/gui/psppire-data-sheet.h \
        src/ui/gui/psppire-data-store.c \
        src/ui/gui/psppire-data-store.h \
        src/ui/gui/psppire-data-window.c \
        src/ui/gui/psppire-var-ptr.h \
        src/ui/gui/psppire-var-sheet.c \
        src/ui/gui/psppire-var-sheet.h \
 -      src/ui/gui/psppire-var-store.c \
 -      src/ui/gui/psppire-var-store.h \
        src/ui/gui/psppire-vbuttonbox.h \
        src/ui/gui/psppire-window.c \
        src/ui/gui/psppire-window.h \
        src/ui/gui/select-cases-dialog.h \
        src/ui/gui/split-file-dialog.c \
        src/ui/gui/split-file-dialog.h \
+       src/ui/gui/page-assistant.c \
+       src/ui/gui/page-intro.c \
+       src/ui/gui/page-intro.h \
+       src/ui/gui/page-file.c \
+       src/ui/gui/page-first-line.c \
+       src/ui/gui/page-first-line.h \
+       src/ui/gui/page-formats.c \
+       src/ui/gui/page-formats.h \
+       src/ui/gui/page-separators.c \
+       src/ui/gui/page-separators.h \
        src/ui/gui/text-data-import-dialog.c \
        src/ui/gui/text-data-import-dialog.h \
        src/ui/gui/transpose-dialog.c \
index 0b1378e8422b557741bcaa9569ff0520b98112c4,2a5289477977ff180b2453e82389dfab1464f66c..2f0dd03adbca214df7926958335bc3656a363920
@@@ -1,6 -1,20 +1,6 @@@
  <?xml version="1.0"?>
  <interface>
    <object class="GtkUIManager" id="uimanager1">
 -    <child>
 -      <object class="GtkActionGroup" id="actiongroup2">
 -        <child>
 -          <object class="GtkAction" id="sort-up">
 -            <property name="stock-id">gtk-sort-ascending</property>
 -          </object>
 -        </child>
 -        <child>
 -          <object class="GtkAction" id="sort-down">
 -            <property name="stock-id">gtk-sort-descending</property>
 -          </object>
 -        </child>
 -      </object>
 -    </child>
      <child>
        <object class="GtkActionGroup" id="actiongroup1">
          <child>
@@@ -35,9 -49,9 +35,9 @@@
            </object>
          </child>
          <child>
-           <object class="GtkAction" id="file_import-text">
-             <property name="name">file_import-text</property>
-             <property name="label" translatable="yes">_Import Delimited Text Data...</property>
+           <object class="GtkAction" id="file_import">
+             <property name="name">file_import</property>
+             <property name="label" translatable="yes">I_mport Data...</property>
            <property name="stock-id">gtk-convert</property>
            </object>
          </child>
              <property name="label" translatable="yes">_Edit</property>
            </object>
          </child>
 -        <child>
 -          <object class="GtkAction" id="action_insert-variable">
 -            <property name="name">action_insert-variable</property>
 -            <property name="label" translatable="yes">Insert Variable</property>
 -            <property name="tooltip" translatable="yes">Create a new variable at the current position</property>
 -          <property name="stock-id">pspp-insert-variable</property>
 -          </object>
 -        </child>
 -        <child>
 -          <object class="GtkAction" id="edit_insert-case">
 -            <property name="name">edit_insert-case</property>
 -            <property name="label" translatable="yes">Insert Cases</property>
 -            <property name="tooltip" translatable="yes">Create a new case at the current position</property>
 -          <property name="stock-id">pspp-insert-case</property>
 -          </object>
 -        </child>
 -        <child>
 -          <object class="GtkAction" id="edit_goto-case">
 -            <property name="label" translatable="yes">Go To Case...</property>
 -            <property name="name">edit_goto-case</property>
 -            <property name="tooltip" translatable="yes">Jump to a case in the data sheet</property>
 -          <property name="stock-id">gtk-jump-to</property>
 -
 -          </object>
 -        </child>
 -        <child>
 -          <object class="GtkAction" id="edit_cut">
 -            <property name="stock-id">gtk-cut</property>
 -            <property name="name">edit_cut</property>
 -          </object>
 -        </child>
 -        <child>
 -          <object class="GtkAction" id="edit_copy">
 -            <property name="stock-id">gtk-copy</property>
 -            <property name="name">edit_copy</property>
 -          </object>
 -        </child>
 -        <child>
 -          <object class="GtkAction" id="edit_paste">
 -            <property name="stock-id">gtk-paste</property>
 -            <property name="name">edit_paste</property>
 -          </object>
 -        </child>
 -        <child>
 -          <object class="GtkAction" id="edit_clear-variables">
 -            <property name="name">edit_clear-variables</property>
 -            <property name="label" translatable="yes">Cl_ear Variables</property>
 -          <property name="tooltip" translatable="yes">Delete the variables at the selected position(s)</property>
 -            <property name="stock-id">gtk-clear</property>
 -          </object>
 -        </child>
 -        <child>
 -          <object class="GtkAction" id="edit_clear-cases">
 -            <property name="name">edit_clear-cases</property>
 -            <property name="stock-id">gtk-clear</property>
 -            <property name="label" translatable="yes">_Clear Cases</property>
 -            <property name="tooltip" translatable="yes">Delete the cases at the selected position(s)</property>
 -          </object>
 -        </child>
 -        <child>
 -          <object class="GtkAction" id="edit_find">
 -            <property name="stock-id">gtk-find</property>
 -            <property name="name">edit_find</property>
 -            <property name="label" translatable="yes">_Find...</property>
 -          </object>
 -        </child>
          <child>
            <object class="GtkAction" id="view">
              <property name="name">view</property>
              <menuitem action="file_new_data"/>
            </menu>
            <menuitem action="file_open"/>
-           <menuitem action="file_import-text"/>
+           <menuitem action="file_import"/>
            <separator/>
            <menuitem action="file_save"/>
            <menuitem action="file_save_as"/>
            <separator/>
            <menuitem action="file_quit"/>
          </menu>
 -        <menu action="edit">
 -          <menuitem action="action_insert-variable"/>
 -          <menuitem action="edit_insert-case"/>
 -          <menuitem action="edit_goto-case"/>
 -          <separator/>
 -          <menuitem action="edit_cut"/>
 -          <menuitem action="edit_copy"/>
 -          <menuitem action="edit_paste"/>
 -          <menuitem action="edit_clear-variables"/>
 -          <menuitem action="edit_clear-cases"/>
 -          <separator/>
 -          <menuitem action="edit_find"/>
 -        </menu>
 +      <placeholder name="DataSheetEditMenu"/>
 +      <placeholder name="VarSheetEditMenu"/>
          <menu action="view">
            <menuitem action="view_statusbar"/>
            <separator/>
          </menu>
        </menubar>
        <toolbar action="toolbar">
 -        <placeholder name="tool-items">
 -          <toolitem name="toolbar_open" action="file_open"/>
 -          <toolitem name="toolbar_save" action="file_save"/>
 -        <separator/>
 -          <toolitem name="toolbar_goto-case" action="edit_goto-case"/>
 -          <toolitem name="toolbar_goto-variable" action="utilities_variables"/>
 -        <separator/>
 -          <toolitem name="toolbar_find" action="edit_find"/>
 -        <separator/>
 -        <toolitem name="toolbar_insert-cases" action="edit_insert-case"/>
 -        <toolitem name="toolbar_insert-variable" action="action_insert-variable"/>
 -        <separator/>
 -        <toolitem name="toolbar_split-file" action="data_split-file"/>
 -        <toolitem name="toolbar_weight-cases" action="data_weight-cases"/>
 -        <toolitem name="toolbar_select-cases" action="data_select-cases"/>
 -        <separator/>
 -        <toolitem name="toolbar_select-cases" action="view_value-labels"/>
 -        </placeholder>
 -      </toolbar>
 -      <popup name="datasheet-variable-popup">
 -      <menuitem action="action_insert-variable"/>
 -      <separator/>
 -      <menuitem action="edit_clear-variables"/>
 +      <toolitem name="toolbar_open" action="file_open"/>
 +      <toolitem name="toolbar_save" action="file_save"/>
        <separator/>
 -        <menuitem action="sort-up"/>
 -        <menuitem action="sort-down"/>
 -      </popup>
 -      <popup name="varsheet-variable-popup">
 -      <menuitem action="action_insert-variable"/>
 +      <placeholder name="DataSheetToolItems"/>
 +      <placeholder name="VarSheetToolItems"/>
        <separator/>
 -      <menuitem action="edit_clear-variables"/>
 -      </popup>
 -      <popup name="datasheet-cases-popup">
 -      <menuitem action="edit_insert-case"/>
 +      <toolitem name="toolbar_split-file" action="data_split-file"/>
 +      <toolitem name="toolbar_weight-cases" action="data_weight-cases"/>
 +      <toolitem name="toolbar_select-cases" action="data_select-cases"/>
        <separator/>
 -      <menuitem action="edit_clear-cases"/>
 -      </popup>
 +      <toolitem name="toolbar_select-cases" action="view_value-labels"/>
 +      </toolbar>
      </ui>
    </object>
    <!-- interface-requires gtk+ 2.6 -->
    <object class="GtkMenuBar" constructor="uimanager1" id="menubar">
      <property name="visible">True</property>
    </object>
 -  <object class="GtkMenu" constructor="uimanager1" id="datasheet-variable-popup">
 -    <property name="visible">True</property>
 -  </object>
 -  <object class="GtkMenu" constructor="uimanager1" id="varsheet-variable-popup">
 -    <property name="visible">True</property>
 -  </object>
 -  <object class="GtkMenu" constructor="uimanager1" id="datasheet-cases-popup">
 -    <property name="visible">True</property>
 -  </object>
    <object class="GtkHandleBox" id="handlebox1">
      <property name="visible">True</property>
      <child>
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));
+ }
diff --combined src/ui/gui/page-file.c
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 ? "." : "");
+     }
+ }
diff --combined src/ui/gui/page-intro.c
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
@@@ -33,6 -33,8 +33,6 @@@
  #include "ui/gui/count-dialog.h"
  #include "ui/gui/entry-dialog.h"
  #include "ui/gui/executor.h"
 -#include "ui/gui/find-dialog.h"
 -#include "ui/gui/goto-case-dialog.h"
  #include "ui/gui/help-menu.h"
  #include "ui/gui/helper.h"
  #include "ui/gui/helper.h"
@@@ -40,7 -42,6 +40,7 @@@
  #include "ui/gui/npar-two-sample-related.h"
  #include "ui/gui/oneway-anova-dialog.h"
  #include "ui/gui/psppire-data-window.h"
 +#include "ui/gui/psppire-dialog-action.h"
  #include "ui/gui/psppire-syntax-window.h"
  #include "ui/gui/psppire-window.h"
  #include "ui/gui/psppire.h"
@@@ -85,10 -86,6 +85,10 @@@ static void psppire_data_window_get_pro
                                                GValue          *value,
                                                GParamSpec      *pspec);
  
 +static guint psppire_data_window_add_ui (PsppireDataWindow *, GtkUIManager *);
 +static void psppire_data_window_remove_ui (PsppireDataWindow *,
 +                                           GtkUIManager *, guint);
 +
  GType
  psppire_data_window_get_type (void)
  {
@@@ -154,6 -151,24 +154,6 @@@ psppire_data_window_class_init (Psppire
                            G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
  }
  \f
 -static void
 -set_paste_menuitem_sensitivity (PsppireDataWindow *de, gboolean x)
 -{
 -  GtkAction *edit_paste = get_action_assert (de->builder, "edit_paste");
 -
 -  gtk_action_set_sensitive (edit_paste, x);
 -}
 -
 -static void
 -set_cut_copy_menuitem_sensitivity (PsppireDataWindow *de, gboolean x)
 -{
 -  GtkAction *edit_copy = get_action_assert (de->builder, "edit_copy");
 -  GtkAction *edit_cut = get_action_assert (de->builder, "edit_cut");
 -
 -  gtk_action_set_sensitive (edit_copy, x);
 -  gtk_action_set_sensitive (edit_cut, x);
 -}
 -
  /* Run the EXECUTE command. */
  static void
  execute (PsppireDataWindow *dw)
@@@ -200,11 -215,13 +200,11 @@@ on_filter_change (GObject *o, gint filt
      }
    else
      {
 -      PsppireVarStore *vs = NULL;
        PsppireDict *dict = NULL;
        struct variable *var ;
        gchar *text ;
  
 -      g_object_get (de->data_editor, "var-store", &vs, NULL);
 -      g_object_get (vs, "dictionary", &dict, NULL);
 +      g_object_get (de->data_editor, "dictionary", &dict, NULL);
  
        var = psppire_dict_get_variable (dict, filter_index);
  
@@@ -271,10 -288,12 +271,10 @@@ on_weight_change (GObject *o, gint weig
    else
      {
        struct variable *var ;
 -      PsppireVarStore *vs = NULL;
        PsppireDict *dict = NULL;
        gchar *text;
  
 -      g_object_get (de->data_editor, "var-store", &vs, NULL);
 -      g_object_get (vs, "dictionary", &dict, NULL);
 +      g_object_get (de->data_editor, "dictionary", &dict, NULL);
  
        var = psppire_dict_get_variable (dict, weight_index);
  
@@@ -410,6 -429,19 +410,6 @@@ save_file (PsppireWindow *w
  }
  
  
 -static void
 -insert_case (PsppireDataWindow *dw)
 -{
 -  psppire_data_editor_insert_case (dw->data_editor);
 -}
 -
 -static void
 -on_insert_variable (PsppireDataWindow *dw)
 -{
 -  psppire_data_editor_insert_variable (dw->data_editor);
 -}
 -
 -
  static void
  display_dict (PsppireDataWindow *de)
  {
@@@ -594,6 -626,27 +594,6 @@@ on_rename_dataset (PsppireDataWindow *d
    free (new_name);
  }
  
 -static void
 -on_edit_paste (PsppireDataWindow  *de)
 -{
 -  psppire_data_editor_clip_paste (de->data_editor);
 -}
 -
 -static void
 -on_edit_copy (PsppireDataWindow  *de)
 -{
 -  psppire_data_editor_clip_copy (de->data_editor);
 -}
 -
 -
 -
 -static void
 -on_edit_cut (PsppireDataWindow  *de)
 -{
 -  psppire_data_editor_clip_cut (de->data_editor);
 -}
 -
 -
  static void
  status_bar_activate (PsppireDataWindow  *de, GtkToggleAction *action)
  {
@@@ -688,6 -741,7 +688,6 @@@ file_quit (PsppireDataWindow *de
    psppire_quit ();
  }
  
 -
  static void
  on_recent_data_select (GtkMenuShell *menushell,
                       PsppireWindow *window)
@@@ -772,52 -826,75 +772,52 @@@ on_recent_files_select (GtkMenuShell *m
    g_free (file);
  }
  
 -
 -
  static void
 -enable_delete_cases (GtkWidget *w, gint case_num, gpointer data)
 +set_unsaved (gpointer w)
  {
 -  PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
 -
 -  gtk_action_set_visible (de->delete_cases, case_num != -1);
 +  psppire_window_set_unsaved (PSPPIRE_WINDOW (w));
  }
  
 -
  static void
 -enable_delete_variables (GtkWidget *w, gint var, gpointer data)
 +on_switch_page (PsppireDataEditor *de, GtkNotebookPage *p,
 +              gint pagenum, PsppireDataWindow *dw)
  {
 -  PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
 -
 -  gtk_action_set_visible (de->delete_variables, var != -1);
 +  GtkWidget *page_menu_item;
 +  gboolean is_ds;
 +  const char *path;
 +
 +  is_ds = pagenum == PSPPIRE_DATA_EDITOR_DATA_VIEW;
 +  path = (is_ds
 +          ? "/ui/menubar/view/view_data"
 +          : "/ui/menubar/view/view_variables");
 +  page_menu_item = gtk_ui_manager_get_widget (dw->ui_manager, path);
 +  gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (page_menu_item), TRUE);
  }
  
 -/* Callback for when the datasheet/varsheet is selected */
  static void
 -on_switch_sheet (GtkNotebook *notebook,
 -               GtkNotebookPage *page,
 -               guint page_num,
 -               gpointer user_data)
 +on_ui_manager_changed (PsppireDataEditor *de,
 +                       GParamSpec *pspec UNUSED,
 +                       PsppireDataWindow *dw)
  {
 -  PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (user_data);
 -
 -  GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
 -
 -  GtkWidget *view_data =
 -    gtk_ui_manager_get_widget (uim,"/ui/menubar/view/view_data");
 -
 -  GtkWidget *view_variables =
 -    gtk_ui_manager_get_widget (uim,"/ui/menubar/view/view_variables");
 +  GtkUIManager *uim = psppire_data_editor_get_ui_manager (de);
 +  if (uim == dw->uim)
 +    return;
  
 -  switch (page_num)
 +  if (dw->uim)
      {
 -    case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
 -      gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (view_variables),
 -                                      TRUE);
 -      gtk_action_set_sensitive (de->insert_variable, TRUE);
 -      gtk_action_set_sensitive (de->insert_case, FALSE);
 -      gtk_action_set_sensitive (de->invoke_goto_dialog, FALSE);
 -      break;
 -    case PSPPIRE_DATA_EDITOR_DATA_VIEW:
 -      gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (view_data), TRUE);
 -      gtk_action_set_sensitive (de->invoke_goto_dialog, TRUE);
 -      gtk_action_set_sensitive (de->insert_case, TRUE);
 -      break;
 -    default:
 -      g_assert_not_reached ();
 -      break;
 +      psppire_data_window_remove_ui (dw, dw->uim, dw->merge_id);
 +      g_object_unref (dw->uim);
 +      dw->uim = NULL;
      }
  
 -#if 0
 -  update_paste_menuitem (de, page_num);
 -#endif
 -}
 -
 -
 -
 -static void
 -set_unsaved (gpointer w)
 -{
 -  psppire_window_set_unsaved (PSPPIRE_WINDOW (w));
 +  dw->uim = uim;
 +  if (dw->uim)
 +    {
 +      g_object_ref (dw->uim);
 +      dw->merge_id = psppire_data_window_add_ui (dw, dw->uim);
 +    }
  }
  
 -
  /* Connects the action called ACTION_NAME to HANDLER passing DW as the auxilliary data.
     Returns a pointer to the action
  */
@@@ -836,7 -913,8 +836,7 @@@ connect_action (PsppireDataWindow *dw, 
  static void
  enable_save (PsppireDataWindow *dw)
  {
 -  PsppireDict *dict = dw->var_store->dictionary;
 -  gboolean enable = psppire_dict_get_var_cnt (dict) > 0;
 +  gboolean enable = psppire_dict_get_var_cnt (dw->dict) > 0;
  
    gtk_action_set_sensitive (get_action_assert (dw->builder, "file_save"),
                              enable);
  static void
  psppire_data_window_init (PsppireDataWindow *de)
  {
 -  GtkUIManager *uim;
 -
    de->builder = builder_new ("data-editor.ui");
  
 -  uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
 +  de->ui_manager = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
  
    PSPPIRE_WINDOW (de)->menu =
 -    GTK_MENU_SHELL (gtk_ui_manager_get_widget (uim,"/ui/menubar/windows/windows_minimise_all")->parent);
 +    GTK_MENU_SHELL (gtk_ui_manager_get_widget (de->ui_manager, "/ui/menubar/windows/windows_minimise_all")->parent);
 +
 +  de->uim = NULL;
 +  de->merge_id = 0;
  }
  
  static void
@@@ -875,6 -952,8 +875,6 @@@ psppire_data_window_finish_init (Psppir
        transformation_change_callback, /* transformations_changed */
      };
  
 -  PsppireDict *dict;
 -
    GtkWidget *menubar;
    GtkWidget *hb ;
    GtkWidget *sb ;
    GtkWidget *box = gtk_vbox_new (FALSE, 0);
  
    de->dataset = ds;
 -  dict = psppire_dict_new_from_dict (dataset_dict (ds));
 -  de->var_store = psppire_var_store_new (dict);
 -  g_object_unref (dict);
 -  de->data_store = psppire_data_store_new (dict);
 +  de->dict = psppire_dict_new_from_dict (dataset_dict (ds));
 +  de->data_store = psppire_data_store_new (de->dict);
    psppire_data_store_set_reader (de->data_store, NULL);
  
    menubar = get_widget_assert (de->builder, "menubar");
    hb = get_widget_assert (de->builder, "handlebox1");
    sb = get_widget_assert (de->builder, "status-bar");
  
 +  de->uim = NULL;
 +  de->merge_id = 0;
 +
    de->data_editor =
 -    PSPPIRE_DATA_EDITOR (psppire_data_editor_new (de, de->var_store,
 -                                                  de->data_store));
 +    PSPPIRE_DATA_EDITOR (psppire_data_editor_new (de->dict, de->data_store));
 +  g_signal_connect (de->data_editor, "switch-page",
 +                    G_CALLBACK (on_switch_page), de);
  
    g_signal_connect_swapped (de->data_store, "case-changed",
                            G_CALLBACK (set_unsaved), de);
  
    gtk_container_add (GTK_CONTAINER (de), box);
  
 -  set_cut_copy_menuitem_sensitivity (de, FALSE);
 -
 -  g_signal_connect_swapped (de->data_editor, "data-selection-changed",
 -                          G_CALLBACK (set_cut_copy_menuitem_sensitivity), de);
 -
 -
 -  set_paste_menuitem_sensitivity (de, FALSE);
 -
 -  g_signal_connect_swapped (de->data_editor, "data-available-changed",
 -                          G_CALLBACK (set_paste_menuitem_sensitivity), de);
 -
 -  g_signal_connect (dict, "weight-changed",
 +  g_signal_connect (de->dict, "weight-changed",
                    G_CALLBACK (on_weight_change),
                    de);
  
 -  g_signal_connect (dict, "filter-changed",
 +  g_signal_connect (de->dict, "filter-changed",
                    G_CALLBACK (on_filter_change),
                    de);
  
 -  g_signal_connect (dict, "split-changed",
 +  g_signal_connect (de->dict, "split-changed",
                    G_CALLBACK (on_split_change),
                    de);
  
 -  g_signal_connect_swapped (dict, "backend-changed",
 +  g_signal_connect_swapped (de->dict, "backend-changed",
                              G_CALLBACK (enable_save), de);
 -  g_signal_connect_swapped (dict, "variable-inserted",
 +  g_signal_connect_swapped (de->dict, "variable-inserted",
                              G_CALLBACK (enable_save), de);
 -  g_signal_connect_swapped (dict, "variable-deleted",
 +  g_signal_connect_swapped (de->dict, "variable-deleted",
                              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));
   
  
    connect_action (de, "file_information_external-file", G_CALLBACK (sysfile_info));
  
 -  connect_action (de, "edit_paste", G_CALLBACK (on_edit_paste));
 -
 -  de->insert_case = connect_action (de, "edit_insert-case", G_CALLBACK (insert_case));
 -
 -  de->insert_variable = connect_action (de, "action_insert-variable", G_CALLBACK (on_insert_variable));
 -
 -  de->invoke_goto_dialog = connect_action (de, "edit_goto-case", G_CALLBACK (goto_case_dialog));
 -
    g_signal_connect_swapped (get_action_assert (de->builder, "view_value-labels"), "toggled", G_CALLBACK (toggle_value_labels), de);
  
 -  {
 -    de->delete_cases = get_action_assert (de->builder, "edit_clear-cases");
 -
 -    g_signal_connect_swapped (de->delete_cases, "activate", G_CALLBACK (psppire_data_editor_delete_cases), de->data_editor);
 -
 -    gtk_action_set_visible (de->delete_cases, FALSE);
 -  }
 -
 -
 -  {
 -    de->delete_variables = get_action_assert (de->builder, "edit_clear-variables");
 -
 -    g_signal_connect_swapped (de->delete_variables, "activate", G_CALLBACK (psppire_data_editor_delete_variables), de->data_editor);
 -
 -    gtk_action_set_visible (de->delete_variables, FALSE);
 -  }
 -
 -
    connect_action (de, "data_transpose", G_CALLBACK (transpose_dialog));
    connect_action (de, "data_select-cases", G_CALLBACK (select_cases_dialog));
    connect_action (de, "data_aggregate", G_CALLBACK (aggregate_dialog));
    connect_action (de, "transform_compute", G_CALLBACK (compute_dialog));
    connect_action (de, "transform_autorecode", G_CALLBACK (autorecode_dialog));
 -  connect_action (de, "edit_find", G_CALLBACK (find_dialog));
    connect_action (de, "data_split-file", G_CALLBACK (split_file_dialog));
    connect_action (de, "data_weight-cases", G_CALLBACK (weight_cases_dialog));
    connect_action (de, "oneway-anova", G_CALLBACK (oneway_anova_dialog));
    connect_action (de, "two-related-samples", G_CALLBACK (two_related_dialog));
  
    {
 -    GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
 -
      GtkWidget *recent_data =
 -      gtk_ui_manager_get_widget (uim,"/ui/menubar/file/file_recent-data");
 +      gtk_ui_manager_get_widget (de->ui_manager, "/ui/menubar/file/file_recent-data");
  
      GtkWidget *recent_files =
 -      gtk_ui_manager_get_widget (uim,"/ui/menubar/file/file_recent-files");
 +      gtk_ui_manager_get_widget (de->ui_manager, "/ui/menubar/file/file_recent-files");
  
  
      GtkWidget *menu_data = gtk_recent_chooser_menu_new_for_manager (
    connect_action (de, "file_new_syntax", G_CALLBACK (create_syntax_window));
  
  
 -  g_signal_connect (de->data_editor,
 -                  "cases-selected",
 -                  G_CALLBACK (enable_delete_cases),
 -                  de);
 -
 -  g_signal_connect (de->data_editor,
 -                  "variables-selected",
 -                  G_CALLBACK (enable_delete_variables),
 -                  de);
 -
 -
 -  g_signal_connect (de->data_editor,
 -                  "switch-page",
 -                  G_CALLBACK (on_switch_sheet), de);
 -
    gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
    gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
  
  
    g_signal_connect_swapped (get_action_assert (de->builder, "windows_split"), "toggled", G_CALLBACK (toggle_split_window), de);
  
 -  {
 -    GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
 -
 -    merge_help_menu (uim);
 -  }
 -
 -  {
 -    GtkWidget *data_sheet_cases_popup_menu = get_widget_assert (de->builder,
 -                                                              "datasheet-cases-popup");
 +  merge_help_menu (de->ui_manager);
  
 -    GtkWidget *var_sheet_variable_popup_menu = get_widget_assert (de->builder,
 -                                                                "varsheet-variable-popup");
 -
 -    GtkWidget *data_sheet_variable_popup_menu = get_widget_assert (de->builder,
 -                                                                 "datasheet-variable-popup");
 -
 -    g_signal_connect_swapped (get_action_assert (de->builder, "sort-up"), "activate",
 -                            G_CALLBACK (psppire_data_editor_sort_ascending),
 -                            de->data_editor);
 -
 -    g_signal_connect_swapped (get_action_assert (de->builder, "sort-down"), "activate",
 -                            G_CALLBACK (psppire_data_editor_sort_descending),
 -                            de->data_editor);
 -
 -    g_object_set (de->data_editor,
 -                "datasheet-column-menu", data_sheet_variable_popup_menu,
 -                "datasheet-row-menu", data_sheet_cases_popup_menu,
 -                "varsheet-row-menu", var_sheet_variable_popup_menu,
 -                NULL);
 -  }
 +  g_signal_connect (de->data_editor, "notify::ui-manager",
 +                    G_CALLBACK (on_ui_manager_changed), de);
 +  on_ui_manager_changed (de->data_editor, NULL, de);
  
    gtk_widget_show (GTK_WIDGET (de->data_editor));
    gtk_widget_show (box);
@@@ -1068,23 -1229,16 +1068,23 @@@ psppire_data_window_dispose (GObject *o
  {
    PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (object);
  
 +  if (dw->uim)
 +    {
 +      psppire_data_window_remove_ui (dw, dw->uim, dw->merge_id);
 +      g_object_unref (dw->uim);
 +      dw->uim = NULL;
 +    }
 +
    if (dw->builder != NULL)
      {
        g_object_unref (dw->builder);
        dw->builder = NULL;
      }
  
 -  if (dw->var_store)
 +  if (dw->dict)
      {
 -      g_object_unref (dw->var_store);
 -      dw->var_store = NULL;
 +      g_object_unref (dw->dict);
 +      dw->dict = NULL;
      }
  
    if (dw->data_store)
@@@ -1162,65 -1316,6 +1162,65 @@@ psppire_data_window_get_property (GObje
      };
  }
  
 +static guint
 +psppire_data_window_add_ui (PsppireDataWindow *pdw, GtkUIManager *uim)
 +{
 +  gchar *ui_string;
 +  guint merge_id;
 +  GList *list;
 +
 +  ui_string = gtk_ui_manager_get_ui (uim);
 +  merge_id = gtk_ui_manager_add_ui_from_string (pdw->ui_manager, ui_string,
 +                                                -1, NULL);
 +  g_free (ui_string);
 +
 +  g_return_val_if_fail (merge_id != 0, 0);
 +
 +  list = gtk_ui_manager_get_action_groups (uim);
 +  for (; list != NULL; list = list->next)
 +    {
 +      GtkActionGroup *action_group = list->data;
 +      GList *actions = gtk_action_group_list_actions (action_group);
 +      GList *action;
 +
 +      for (action = actions; action != NULL; action = action->next)
 +        {
 +          GtkAction *a = action->data;
 +
 +          if (PSPPIRE_IS_DIALOG_ACTION (a))
 +            g_object_set (a, "manager", pdw->ui_manager, NULL);
 +        }
 +
 +      gtk_ui_manager_insert_action_group (pdw->ui_manager, action_group, 0);
 +    }
 +
 +  gtk_window_add_accel_group (GTK_WINDOW (pdw),
 +                              gtk_ui_manager_get_accel_group (uim));
 +
 +  return merge_id;
 +}
 +
 +static void
 +psppire_data_window_remove_ui (PsppireDataWindow *pdw,
 +                               GtkUIManager *uim, guint merge_id)
 +{
 +  GList *list;
 +
 +  g_return_if_fail (merge_id != 0);
 +
 +  gtk_ui_manager_remove_ui (pdw->ui_manager, merge_id);
 +
 +  list = gtk_ui_manager_get_action_groups (uim);
 +  for (; list != NULL; list = list->next)
 +    {
 +      GtkActionGroup *action_group = list->data;
 +      gtk_ui_manager_remove_action_group (pdw->ui_manager, action_group);
 +    }
 +
 +  gtk_window_remove_accel_group (GTK_WINDOW (pdw),
 +                                 gtk_ui_manager_get_accel_group (uim));
 +}
 +
  GtkWidget*
  psppire_data_window_new (struct dataset *ds)
  {
  bool
  psppire_data_window_is_empty (PsppireDataWindow *dw)
  {
 -  return psppire_var_store_get_var_cnt (dw->var_store) == 0;
 +  return psppire_dict_get_var_cnt (dw->dict) == 0;
  }
  
  static void
@@@ -1298,18 -1393,6 +1298,18 @@@ psppire_data_window_for_dataset (struc
    return NULL;
  }
  
 +PsppireDataWindow *
 +psppire_data_window_for_data_store (PsppireDataStore *data_store)
 +{
 +  PsppireDataWindow *pdw;
 +
 +  ll_for_each (pdw, PsppireDataWindow, ll, &all_data_windows)
 +    if (pdw->data_store == data_store)
 +      return pdw;
 +
 +  return NULL;
 +}
 +
  void
  create_data_window (void)
  {
@@@ -1323,14 -1406,10 +1323,14 @@@ open_data_window (PsppireWindow *victim
  
    if (PSPPIRE_IS_DATA_WINDOW (victim)
        && psppire_data_window_is_empty (PSPPIRE_DATA_WINDOW (victim)))
 -    window = GTK_WIDGET (victim);
 +    {
 +      window = GTK_WIDGET (victim);
 +      gtk_widget_hide (GTK_WIDGET (PSPPIRE_DATA_WINDOW (window)->data_editor));
 +    }
    else
      window = psppire_data_window_new (NULL);
  
    psppire_window_load (PSPPIRE_WINDOW (window), file_name);
 -  gtk_widget_show (window);
 +  gtk_widget_show_all (window);
  }
 +
index 4badb6550dfe94b9ce026fd1a4871bce70312909,20bac652ccab69c153e753e2f4684b0922e4114a..ce23093feefa33ea88837236f77fc9a8f43423e7
@@@ -1,5 -1,5 +1,5 @@@
  /* PSPPIRE - a graphical user interface for PSPP.
-    Copyright (C) 2008, 2009, 2010, 2011, 2012  Free Software Foundation
+    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
  
  #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 "libpspp/i18n.h"
  #include "libpspp/line-reader.h"
  #include "libpspp/message.h"
- #include "ui/gui/builder-wrapper.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.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"
  
  struct import_assistant;
  
- /* The file to be imported. */
- struct file
-   {
-     char *file_name;        /* File name. */
-     gchar *encoding;        /* Encoding. */
-     unsigned long int total_lines; /* Number of lines in file. */
-     bool total_is_exact;    /* Is total_lines exact (or an estimate)? */
-     /* The first several lines of the file. */
-     struct string *lines;
-     size_t line_cnt;
-   };
- static bool init_file (struct import_assistant *, GtkWindow *parent);
- static void destroy_file (struct import_assistant *);
- /* The main body of the GTK+ assistant and related data. */
- struct assistant
-   {
-     GtkBuilder *builder;
-     GtkAssistant *assistant;
-     GMainLoop *main_loop;
-     GtkWidget *paste_button;
-     GtkWidget *reset_button;
-     int response;
-     int watch_cursor;
-     GtkCellRenderer *prop_renderer;
-     GtkCellRenderer *fixed_renderer;
-   };
- static void init_assistant (struct import_assistant *, GtkWindow *);
- static void destroy_assistant (struct import_assistant *);
- static GtkWidget *add_page_to_assistant (struct import_assistant *,
-                                          GtkWidget *page,
-                                          GtkAssistantPageType);
- /* 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 init_intro_page (struct import_assistant *);
- static void reset_intro_page (struct import_assistant *);
- /* Page where the user chooses the first line of data. */
- struct first_line_page
-   {
-     int skip_lines;    /* Number of initial lines to skip? */
-     bool variable_names; /* Variable names above first line of data? */
-     GtkWidget *page;
-     PsppSheetView *tree_view;
-     GtkWidget *variable_names_cb;
-   };
- static void init_first_line_page (struct import_assistant *);
- static void reset_first_line_page (struct import_assistant *);
- /* 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? */
-     /* The columns produced thereby. */
-     struct column *columns;     /* Information about each column. */
-     size_t column_cnt;          /* Number of columns. */
-     GtkWidget *page;
-     GtkWidget *custom_cb;
-     GtkWidget *custom_entry;
-     GtkWidget *quote_cb;
-     GtkWidget *quote_combo;
-     GtkEntry *quote_entry;
-     GtkWidget *escape_cb;
-     PsppSheetView *fields_tree_view;
-   };
- /* The columns that the separators divide the data into. */
- struct column
-   {
-     /* Variable name for this column.  This is the variable name
-        used on the separators page; it can be overridden by the
-        user on the formats page. */
-     char *name;
-     /* Maximum length of any row in this column. */
-     size_t width;
-     /* Contents of this column: contents[row] is the contents for
-        the given row.
-        A null substring indicates a missing column for that row
-        (because the line contains an insufficient number of
-        separators).
-        contents[] elements may be substrings of the lines[]
-        strings that represent the whole lines of the file, to
-        save memory.  Other elements are dynamically allocated
-        with ss_alloc_substring. */
-     struct substring *contents;
-   };
- static void init_separators_page (struct import_assistant *);
- static void destroy_separators_page (struct import_assistant *);
- static void prepare_separators_page (struct import_assistant *);
- static void reset_separators_page (struct import_assistant *);
- /* Page where the user verifies and adjusts input formats. */
- struct formats_page
-   {
-     struct dictionary *dict;
-     GtkWidget *page;
-     PsppSheetView *data_tree_view;
-     PsppireDict *psppire_dict;
-     struct variable **modified_vars;
-     size_t modified_var_cnt;
-   };
- static void init_formats_page (struct import_assistant *);
- static void destroy_formats_page (struct import_assistant *);
- static void prepare_formats_page (struct import_assistant *);
- static void reset_formats_page (struct import_assistant *);
- struct import_assistant
-   {
-     struct file file;
-     struct assistant asst;
-     struct intro_page intro;
-     struct first_line_page first_line;
-     struct separators_page separators;
-     struct formats_page formats;
-   };
  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
  text_data_import_assistant (PsppireDataWindow *dw)
  {
    GtkWindow *parent_window = GTK_WINDOW (dw);
-   struct import_assistant *ia;
+   struct import_assistant *ia = init_assistant (parent_window);
  
-   ia = xzalloc (sizeof *ia);
    if (!init_file (ia, parent_window))
      {
        free (ia);
        return;
      }
  
-   init_assistant (ia, parent_window);
-   init_intro_page (ia);
-   init_first_line_page (ia);
-   init_separators_page (ia);
-   init_formats_page (ia);
+     {
+       ia->intro = intro_page_create (ia);
+       ia->first_line = first_line_page_create (ia);
+       ia->separators = separators_page_create (ia);
+     }
+   ia->formats = formats_page_create (ia);
  
    gtk_widget_show_all (GTK_WIDGET (ia->asst.assistant));
  
    ia->asst.main_loop = g_main_loop_new (NULL, false);
-   g_main_loop_run (ia->asst.main_loop);
+   {  
+   /*
+     Instead of this block,
+     A simple     g_main_loop_run (ia->asst.main_loop);  should work here.  But it seems to crash.
+     I have no idea why.
+   */
+     GMainContext *ctx = g_main_loop_get_context (ia->asst.main_loop);
+     ia->asst.loop_done = false;
+     while (! ia->asst.loop_done)
+       {
+       g_main_context_iteration (ctx, TRUE);
+       }
+   }
    g_main_loop_unref (ia->asst.main_loop);
  
    switch (ia->asst.response)
        break;
      }
  
-   destroy_formats_page (ia);
-   destroy_separators_page (ia);
+     {
+       destroy_formats_page (ia);
+       destroy_separators_page (ia);
+     }
    destroy_assistant (ia);
    destroy_file (ia);
    free (ia);
@@@ -354,1204 -222,51 +222,51 @@@ 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);
  static gboolean on_query_input_tooltip (GtkWidget *widget, gint wx, gint wy,
                                          gboolean keyboard_mode UNUSED,
                                          GtkTooltip *tooltip,
                                          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. */
- static void
- init_separators_page (struct import_assistant *ia)
- {
-   GtkBuilder *builder = ia->asst.builder;
-   struct separators_page *p = &ia->separators;
-   size_t i;
-   choose_likely_separators (ia);
-   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_separators (ia);
-   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);
- }
- /* Frees IA's separators substructure. */
- static 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. */
- static 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. */
- static 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)
- {
-   struct separators_page *s = &ia->separators;
-   if (s->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 = s->columns; col < &s->columns[s->column_cnt]; col++)
-             {
-               char *s = ss_data (col->contents[row]);
-               if (!(s >= line_start && s <= line_end))
-                 ss_dealloc (&col->contents[row]);
-             }
-         }
-       for (col = s->columns; col < &s->columns[s->column_cnt]; col++)
-         {
-           free (col->name);
-           free (col->contents);
-         }
-       free (s->columns);
-       s->columns = NULL;
-       s->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 >= s->column_cnt)
-             {
-               struct column *column;
-               if (s->column_cnt >= columns_allocated)
-                 s->columns = x2nrealloc (s->columns, &columns_allocated,
-                                          sizeof *s->columns);
-               column = &s->columns[s->column_cnt++];
-               column->name = NULL;
-               column->width = 0;
-               column->contents = xcalloc (ia->file.line_cnt,
-                                           sizeof *column->contents);
-             }
-           column = &s->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)
- {
-   const struct first_line_page *f = &ia->first_line;
-   struct separators_page *s = &ia->separators;
-   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 = f->variable_names && f->skip_lines ? f->skip_lines : 0;
-   for (col = s->columns; col < &s->columns[s->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);
- }
  
  /* Called to render one of the cells in the fields preview tree
     view. */
  static void
 -render_input_cell (GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
 +render_input_cell (PsppSheetViewColumn *tree_column, GtkCellRenderer *cell,
                     GtkTreeModel *model, GtkTreeIter *iter,
                     gpointer ia_)
  {
  
    column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
                                                 "column-number"));
-   row = empty_list_store_iter_to_row (iter) + ia->first_line.skip_lines;
-   field = ia->separators.columns[column].contents[row];
+   row = empty_list_store_iter_to_row (iter) + ia->skip_lines;
+   field = ia->columns[column].contents[row];
    if (field.string != NULL)
      {
        GValue text = {0, };
                    (void *) NULL);
  }
  
+ static gboolean
+ get_tooltip_location (GtkWidget *widget, gint wx, gint wy,
+                       const struct import_assistant *ia,
+                       size_t *row, size_t *column);
  /* Called to render a tooltip on one of the cells in the fields
     preview tree view. */
  static gboolean
@@@ -1593,7 -314,7 +314,7 @@@ on_query_input_tooltip (GtkWidget *widg
    if (!get_tooltip_location (widget, wx, wy, ia, &row, &column))
      return FALSE;
  
-   if (ia->separators.columns[column].contents[row].string != NULL)
+   if (ia->columns[column].contents[row].string != NULL)
      return FALSE;
  
    gtk_tooltip_set_text (tooltip,
                            "to fill in this field."));
    return TRUE;
  }
\f
- /* 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. */
- static void
- init_formats_page (struct import_assistant *ia)
- {
-   GtkBuilder *builder = ia->asst.builder;
-   struct formats_page *p = &ia->formats;
-   p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Formats"),
-                                    GTK_ASSISTANT_PAGE_CONFIRM);
-   p->data_tree_view = PSPP_SHEET_VIEW (get_widget_assert (builder, "data"));
-   p->modified_vars = NULL;
-   p->modified_var_cnt = 0;
-   p->dict = NULL;
- }
- /* Frees IA's formats substructure. */
- static 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. */
- static void
- prepare_formats_page (struct import_assistant *ia)
- {
-   struct dictionary *dict;
-   PsppireDict *psppire_dict;
-   GtkBin *vars_scroller;
-   GtkWidget *old_var_sheet;
-   PsppireVarSheet *var_sheet;
-   struct separators_page *s = &ia->separators;
-   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 < s->column_cnt; column_idx++)
-     {
-       struct variable *modified_var;
-       modified_var = (column_idx < p->modified_var_cnt
-                       ? p->modified_vars[column_idx] : NULL);
-       if (modified_var == NULL)
-         {
-           struct column *column = &s->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->first_line.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->formats.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. */
-   var_sheet = PSPPIRE_VAR_SHEET (psppire_var_sheet_new ());
-   g_object_set (var_sheet,
-                 "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);
-   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. */
- static 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;
-   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);
- }
  
  /* Parses the contents of the field at (ROW,COLUMN) according to
     its variable format.  If OUTPUTP is non-null, then *OUTPUTP
@@@ -1810,16 -336,15 +336,15 @@@ parse_field (struct import_assistant *i
               size_t row, size_t column,
               char **outputp, char **tooltipp)
  {
-   struct substring field;
-   union value val;
-   struct variable *var;
    const struct fmt_spec *in;
    struct fmt_spec out;
    char *tooltip;
    bool ok;
  
-   field = ia->separators.columns[column].contents[row];
-   var = dict_get_var (ia->formats.dict, column);
+   struct substring field = ia->columns[column].contents[row];
+   struct variable *var = dict_get_var (ia->dict, column);
+   union value val;
    value_init (&val, var_get_width (var));
    in = var_get_print_format (var);
    out = fmt_for_output_from_input (in);
        char *error;
  
        error = data_in (field, "UTF-8", in->type, &val, var_get_width (var),
-                        dict_get_encoding (ia->formats.dict));
+                        dict_get_encoding (ia->dict));
        if (error != NULL)
          {
            tooltip = xasprintf (_("Cannot parse field content `%.*s' as "
      }
    if (outputp != NULL)
      {
-       *outputp = data_out (&val, dict_get_encoding (ia->formats.dict),  &out);
+       *outputp = data_out (&val, dict_get_encoding (ia->dict),  &out);
      }
    value_destroy (&val, var_get_width (var));
  
  /* Called to render one of the cells in the data preview tree
     view. */
  static void
 -render_output_cell (GtkTreeViewColumn *tree_column,
 +render_output_cell (PsppSheetViewColumn *tree_column,
                      GtkCellRenderer *cell,
                      GtkTreeModel *model,
                      GtkTreeIter *iter,
  
    ok = parse_field (ia,
                      (empty_list_store_iter_to_row (iter)
-                      + ia->first_line.skip_lines),
+                      + ia->skip_lines),
                      GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
                                                          "column-number")),
                      &output, NULL);
     preview tree view. */
  static gboolean
  on_query_output_tooltip (GtkWidget *widget, gint wx, gint wy,
-                         gboolean keyboard_mode UNUSED,
-                         GtkTooltip *tooltip, struct import_assistant *ia)
+                        gboolean keyboard_mode UNUSED,
+                        GtkTooltip *tooltip, struct import_assistant *ia)
  {
    size_t row, column;
    char *text;
@@@ -1922,11 -447,11 +447,11 @@@ get_tooltip_location (GtkWidget *widget
                        const struct import_assistant *ia,
                        size_t *row, size_t *column)
  {
 -  GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
 +  PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
    gint bx, by;
    GtkTreePath *path;
    GtkTreeIter iter;
 -  GtkTreeViewColumn *tree_column;
 +  PsppSheetViewColumn *tree_column;
    GtkTreeModel *tree_model;
    bool ok;
  
    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;
  
    *column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
                                                  "column-number"));
  
 -  tree_model = gtk_tree_view_get_model (tree_view);
 +  tree_model = pspp_sheet_view_get_model (tree_view);
    ok = gtk_tree_model_get_iter (tree_model, &iter, path);
    gtk_tree_path_free (path);
    if (!ok)
      return FALSE;
  
-   *row = empty_list_store_iter_to_row (&iter) + ia->first_line.skip_lines;
+   *row = empty_list_store_iter_to_row (&iter) + ia->skip_lines;
    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));
 -  gtk_tree_view_set_model (*tree_view, model);
 +  pspp_sheet_view_set_model (*tree_view, model);
    g_object_unref (model);
  
    add_line_number_column (ia, *tree_view);
  }
  
  static void
 -render_line_number (GtkTreeViewColumn *tree_column,
 +render_line_number (PsppSheetViewColumn *tree_column,
                      GtkCellRenderer *cell,
                      GtkTreeModel *tree_model,
                      GtkTreeIter *iter,
  
  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;
    struct column *column = NULL;
    size_t char_cnt;
    gint content_width, header_width;
 -  GtkTreeViewColumn *tree_column;
 +  PsppSheetViewColumn *tree_column;
    char *name;
  
    if (input)
-     column = &ia->separators.columns[dict_idx];
+     column = &ia->columns[dict_idx];
    else
-     var = dict_get_var (ia->formats.dict, dict_idx);
+     var = dict_get_var (ia->dict, dict_idx);
  
    name = escape_underscores (input ? column->name : var_get_name (var));
    char_cnt = input ? column->width : var_get_print_format (var)->w;
    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));
  
  /* Increments the "watch cursor" level, setting the cursor for
     the assistant window to a watch face to indicate to the user
     that the ongoing operation may take some time. */
static void
+ void
  push_watch_cursor (struct import_assistant *ia)
  {
    if (++ia->asst.watch_cursor == 1)
  
  /* Decrements the "watch cursor" level.  If the level reaches
     zero, the cursor is reset to its default shape. */
static void
+ void
  pop_watch_cursor (struct import_assistant *ia)
  {
    if (--ia->asst.watch_cursor == 0)
index 664249c9644ef46cbbae74b6acf9976a94ecedb1,0c026858fe04665f59331ba63ec372474773a2a5..59b5819c3ee873c944bca810623c0c64994de65c
@@@ -1,5 -1,5 +1,5 @@@
  /* PSPPIRE - a graphical user interface for PSPP.
-    Copyright (C) 2008, 2010, 2011  Free Software Foundation
+    Copyright (C) 2008, 2010, 2011, 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
  
  #include <glib-object.h>
  #include "ui/gui/psppire-data-window.h"
+ #include "data/spreadsheet-reader.h"
+ #include "libpspp/str.h"
+ enum { MAX_PREVIEW_LINES = 1000 }; /* Max number of lines to read. */
+ struct assist_page
+ {
+   GtkWidget *page;
+ };
+ /* The file to be imported. */
+ struct file
+   {
+     char *file_name;        /* File name. */
+     /* Relevant only for text files */
+     gchar *encoding;        /* Encoding. */
+     unsigned long int total_lines; /* Number of lines in file. */
+     bool total_is_exact;    /* Is total_lines exact (or an estimate)? */
+     /* The first several lines of the file. */
+     struct string *lines;
+     size_t line_cnt;
+   };
+ /* The main body of the GTK+ assistant and related data. */
+ struct assistant
+   {
+     GtkBuilder *builder;
+     GtkAssistant *assistant;
+     GMainLoop *main_loop;
+     bool loop_done;
+     GtkWidget *paste_button;
+     GtkWidget *reset_button;
+     int response;
+     int watch_cursor;
+     GtkCellRenderer *prop_renderer;
+     GtkCellRenderer *fixed_renderer;
+   };
+ struct import_assistant
+   {
+     struct file file;
+     struct assistant asst;
+     struct intro_page *intro;
+     struct sheet_spec_page *sheet_spec;
+     struct first_line_page *first_line;
+     struct separators_page *separators;
+     struct formats_page *formats;
+     /* The columns produced. */
+     struct column *columns;     /* Information about each column. */
+     size_t column_cnt;          /* Number of columns. */
+     int skip_lines;             /* Number of initial lines to skip? */
+     bool variable_names;        /* Variable names above first line of data? */
+     struct dictionary *dict;
+     struct spreadsheet *spreadsheet;
+   };
+ struct column
+   {
+     /* Variable name for this column.  This is the variable name
+        used on the separators page; it can be overridden by the
+        user on the formats page. */
+     char *name;
+     /* Maximum length of any row in this column. */
+     size_t width;
+     /* Contents of this column: contents[row] is the contents for
+        the given row.
+        A null substring indicates a missing column for that row
+        (because the line contains an insufficient number of
+        separators).
+        contents[] elements may be substrings of the lines[]
+        strings that represent the whole lines of the file, to
+        save memory.  Other elements are dynamically allocated
+        with ss_alloc_substring. */
+     struct substring *contents;
+   };
+ GtkWidget * add_page_to_assistant (struct import_assistant *ia,
+                                  GtkWidget *page, GtkAssistantPageType type);
  
  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