gnucash master: Multiple changes pushed

Geert Janssens gjanssens at code.gnucash.org
Mon Feb 20 14:45:55 EST 2017


Updated	 via  https://github.com/Gnucash/gnucash/commit/2c5f6b9c (commit)
	 via  https://github.com/Gnucash/gnucash/commit/1d552fbe (commit)
	 via  https://github.com/Gnucash/gnucash/commit/558d4b43 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/cd66fb2e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/a2956802 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/372c46cc (commit)
	 via  https://github.com/Gnucash/gnucash/commit/61244301 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/5d96e47b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/8d03abac (commit)
	 via  https://github.com/Gnucash/gnucash/commit/6657e666 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/4687eb64 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/9fbbd736 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/449db629 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/59535435 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/9ecde3a0 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/88a482c5 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/92969b4e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/e92c5eba (commit)
	 via  https://github.com/Gnucash/gnucash/commit/1660276e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/5ba4764a (commit)
	 via  https://github.com/Gnucash/gnucash/commit/5b446cd9 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/f8470ffa (commit)
	 via  https://github.com/Gnucash/gnucash/commit/f85e52be (commit)
	 via  https://github.com/Gnucash/gnucash/commit/d2098bfc (commit)
	 via  https://github.com/Gnucash/gnucash/commit/b629fc97 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/b13718ee (commit)
	 via  https://github.com/Gnucash/gnucash/commit/cfeb1f63 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/522b75ee (commit)
	 via  https://github.com/Gnucash/gnucash/commit/1a1a3fc9 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/e4fc93ff (commit)
	 via  https://github.com/Gnucash/gnucash/commit/0d721b79 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/4032b553 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/26e59c4e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/e5a175a2 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/e6e36d64 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/550a431c (commit)
	 via  https://github.com/Gnucash/gnucash/commit/7a381cd8 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/91c4202f (commit)
	 via  https://github.com/Gnucash/gnucash/commit/7fdf135a (commit)
	 via  https://github.com/Gnucash/gnucash/commit/718a755f (commit)
	 via  https://github.com/Gnucash/gnucash/commit/a0320d3a (commit)
	 via  https://github.com/Gnucash/gnucash/commit/0b345d47 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/847b140b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/35ba4ec9 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/49bbbca1 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/95d7e17c (commit)
	 via  https://github.com/Gnucash/gnucash/commit/24929310 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/d2597ef1 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/848c7b8f (commit)
	 via  https://github.com/Gnucash/gnucash/commit/443237f2 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/5950b902 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/6d304d3c (commit)
	 via  https://github.com/Gnucash/gnucash/commit/1fc4b3cd (commit)
	 via  https://github.com/Gnucash/gnucash/commit/9e70166b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/e8d24e19 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/7a69d552 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/dcce2d79 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/861bff3f (commit)
	 via  https://github.com/Gnucash/gnucash/commit/8e20c640 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/4f2980ef (commit)
	 via  https://github.com/Gnucash/gnucash/commit/056d20c1 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/854ee319 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/90e5e96f (commit)
	 via  https://github.com/Gnucash/gnucash/commit/d642d080 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/ab6dc0f5 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/91df5eda (commit)
	 via  https://github.com/Gnucash/gnucash/commit/ffa68c6b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/0d4d92fb (commit)
	 via  https://github.com/Gnucash/gnucash/commit/f64d217e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/43087577 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/d64c66e6 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/b9e73d92 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/bbac6aa1 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/26380562 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/c0d518e8 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/9525d9b8 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/a6aa76fc (commit)
	 via  https://github.com/Gnucash/gnucash/commit/bce5eaaa (commit)
	 via  https://github.com/Gnucash/gnucash/commit/da0120d5 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/c1d798d0 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/e0caec0e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/6adbab1d (commit)
	 via  https://github.com/Gnucash/gnucash/commit/bbeb351a (commit)
	 via  https://github.com/Gnucash/gnucash/commit/737cbfb3 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/f47d12c3 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/b425a5e7 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/1b44310b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/d557c01c (commit)
	 via  https://github.com/Gnucash/gnucash/commit/05c18796 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/11ff8273 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/bebc871f (commit)
	 via  https://github.com/Gnucash/gnucash/commit/42c2f94b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/2d42bf59 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/f26d3cea (commit)
	 via  https://github.com/Gnucash/gnucash/commit/38b0b356 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/e6d9a614 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/8f9b3d32 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/5c9f9059 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/0b73a56c (commit)
	 via  https://github.com/Gnucash/gnucash/commit/6f15805c (commit)
	 via  https://github.com/Gnucash/gnucash/commit/c21cf188 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/b9646e9b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/142fb617 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/0f6dc53c (commit)
	 via  https://github.com/Gnucash/gnucash/commit/d85de012 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/17b2b466 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/8f9d2ee8 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/bcae6628 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/e95b1e2c (commit)
	 via  https://github.com/Gnucash/gnucash/commit/533b5eb7 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/cecfe9ec (commit)
	 via  https://github.com/Gnucash/gnucash/commit/ce63d8aa (commit)
	 via  https://github.com/Gnucash/gnucash/commit/1507ec0c (commit)
	 via  https://github.com/Gnucash/gnucash/commit/3a0f0dff (commit)
	 via  https://github.com/Gnucash/gnucash/commit/9ff993bb (commit)
	 via  https://github.com/Gnucash/gnucash/commit/c6043ccc (commit)
	 via  https://github.com/Gnucash/gnucash/commit/48cfbc23 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/efcd2669 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/ed7b863d (commit)
	 via  https://github.com/Gnucash/gnucash/commit/83da5187 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/d57a7cee (commit)
	 via  https://github.com/Gnucash/gnucash/commit/0dc6fdc7 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/baf10bb7 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/d858c7d5 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/20eecb05 (commit)
	from  https://github.com/Gnucash/gnucash/commit/01d31c71 (commit)



commit 2c5f6b9cc33b215a5384ef4c401ddca68811951f
Merge: 01d31c7 1d552fb
Author: Geert Janssens <geert at kobaltwit.be>
Date:   Mon Feb 20 20:42:21 2017 +0100

    Merge branch 'csv_imp_cpp'
    
    This feature branch is about the c++ conversion of the
    csv transaction importer.


commit 1d552fbe7fd7f5e96b44ad5e34ff6b8610406d40
Author: Geert Janssens <geert at kobaltwit.be>
Date:   Mon Feb 20 20:22:45 2017 +0100

    Use boost::locale:conv:utf_to_uft instead of codecvt_utf8
    
    The latter is not available until gcc 5.x and we still depend on
    gcc 4.8 on some platforms (RHEL/Centos, Travis/Ubuntu 14.04, Windows).

diff --git a/src/import-export/csv-imp/gnc-fw-tokenizer.cpp b/src/import-export/csv-imp/gnc-fw-tokenizer.cpp
index acd5c27..0f8d671 100644
--- a/src/import-export/csv-imp/gnc-fw-tokenizer.cpp
+++ b/src/import-export/csv-imp/gnc-fw-tokenizer.cpp
@@ -1,6 +1,5 @@
 #include "gnc-fw-tokenizer.hpp"
 
-#include <codecvt>
 #include <iostream>
 #include <fstream>      // fstream
 #include <vector>
@@ -162,13 +161,14 @@ void GncFwTokenizer::load_file(const std::string& path)
  */
 int GncFwTokenizer::tokenize()
 {
+    using boost::locale::conv::utf_to_utf;
     using Tokenizer = boost::tokenizer< boost::offset_separator,
             std::wstring::const_iterator, std::wstring > ;
 
     boost::offset_separator sep(m_col_vec.begin(), m_col_vec.end(), false);
 
-    std::wstring_convert<std::codecvt_utf8<wchar_t>> conv;
-    std::wstring wchar_contents = conv.from_bytes(m_utf8_contents);
+    std::wstring wchar_contents = utf_to_utf<wchar_t>(m_utf8_contents.c_str(),
+        m_utf8_contents.c_str() + m_utf8_contents.size());
 
     StrVec vec;
     std::wstring line;
@@ -181,7 +181,12 @@ int GncFwTokenizer::tokenize()
         Tokenizer tok(line, sep);
         vec.clear();
         for (auto token : tok)
-            vec.push_back (conv.to_bytes(boost::trim_copy(token))); // strips newlines as well as whitespace
+        {
+            auto stripped = boost::trim_copy(token); // strips newlines as well as whitespace
+            auto narrow = utf_to_utf<char>(stripped.c_str(), stripped.c_str()
+                + stripped.size());
+            vec.push_back (narrow);
+        }
         m_tokenized_contents.push_back(vec);
         line.clear(); // clear here, next check could fail
     }

commit 558d4b43b2966d842cdf943a54f86749b13a379b
Author: Geert Janssens <geert at kobaltwit.be>
Date:   Mon Feb 20 13:16:38 2017 +0100

    Properly test for invalid dates (gnc-date C interface no longer throws - rightfully so)

diff --git a/src/import-export/csv-imp/gnc-trans-props.cpp b/src/import-export/csv-imp/gnc-trans-props.cpp
index 57fdf9f..812a33f 100644
--- a/src/import-export/csv-imp/gnc-trans-props.cpp
+++ b/src/import-export/csv-imp/gnc-trans-props.cpp
@@ -196,6 +196,9 @@ time64 parse_date (const std::string &date_str, int format)
     }
 
     auto ts = gnc_dmy2timespec_neutral(day, month, year);
+    if (ts.tv_sec == INT64_MAX)
+        throw std::invalid_argument (_("Value can't be parsed into a date using the selected date format."));
+
     return ts.tv_sec;
 }
 

commit cd66fb2e2db884ab435d6ab1a211d5af73b87a40
Author: Geert Janssens <geert at kobaltwit.be>
Date:   Mon Feb 20 12:52:18 2017 +0100

    Some ui refinement
    
    - Adjust description on the assistant's start page to match current behavior
    - Hide separator buttons in case the file format is set to fixed width
    - For fixed width display some instructions on how to manipulate columns
    - Move the error messages to below the preview table

diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index 67bd855..4af46aa 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -149,7 +149,9 @@ private:
     GtkWidget       *fixed_button;                  /**< The widget for the Fixed Width button */
     GtkWidget       *multi_split_cbutton;           /**< The widget for Multi-split */
     GOCharmapSel    *encselector;                   /**< The widget for selecting the encoding */
+    GtkWidget       *separator_table;               /**< Container for the separator checkboxes */
     GtkCheckButton  *sep_button[SEP_NUM_OF_TYPES];  /**< Checkbuttons for common separators */
+    GtkWidget       *fw_instructions_hbox;          /**< Container for fixed-width instructions */
     GtkCheckButton  *custom_cbutton;                /**< The checkbutton for a custom separator */
     GtkEntry        *custom_entry;                  /**< The entry for custom separators */
     GtkComboBoxText *date_format_combo;             /**< The Combo Text widget for selecting the date format */
@@ -467,6 +469,8 @@ CsvImpTransAssist::CsvImpTransAssist ()
         skip_alt_rows_button = GTK_WIDGET(gtk_builder_get_object (builder, "skip_rows"));
         skip_errors_button = GTK_WIDGET(gtk_builder_get_object (builder, "skip_errors_button"));
         multi_split_cbutton = GTK_WIDGET(gtk_builder_get_object (builder, "multi_split_button"));
+        separator_table = GTK_WIDGET(gtk_builder_get_object (builder, "separator_table"));
+        fw_instructions_hbox = GTK_WIDGET(gtk_builder_get_object (builder, "fw_instructions_hbox"));
 
         /* Load the separator buttons from the glade builder file into the
          * sep_buttons array. */
@@ -961,6 +965,8 @@ void CsvImpTransAssist::preview_update_file_format ()
             tx_imp->file_format (GncImpFileFormat::CSV);
             g_signal_handlers_disconnect_by_func(G_OBJECT(treeview),
                     (gpointer)csv_tximp_preview_treeview_clicked_cb, (gpointer)this);
+            gtk_widget_set_visible (separator_table, true);
+            gtk_widget_set_visible (fw_instructions_hbox, false);
         }
         else
         {
@@ -968,6 +974,8 @@ void CsvImpTransAssist::preview_update_file_format ()
             /* Enable context menu for adding/removing columns. */
             g_signal_connect (G_OBJECT(treeview), "button-press-event",
                     G_CALLBACK(csv_tximp_preview_treeview_clicked_cb), (gpointer)this);
+            gtk_widget_set_visible (separator_table, false);
+            gtk_widget_set_visible (fw_instructions_hbox, true);
 
         }
 
diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.glade b/src/import-export/csv-imp/assistant-csv-trans-import.glade
index 2ac667d..a71d32e 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.glade
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.glade
@@ -12,6 +12,16 @@
       <column type="gpointer"/>
     </columns>
   </object>
+  <object class="GtkAdjustment" id="end_row_adj">
+    <property name="upper">1000</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
+  <object class="GtkAdjustment" id="start_row_adj">
+    <property name="upper">1000</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
   <object class="GtkAssistant" id="CSV Transaction Assistant">
     <property name="can_focus">False</property>
     <property name="border_width">12</property>
@@ -19,32 +29,32 @@
     <property name="default_width">400</property>
     <property name="default_height">500</property>
     <signal name="close" handler="csv_tximp_assist_close_cb" swapped="no"/>
+    <signal name="destroy" handler="csv_tximp_assist_destroy_cb" swapped="no"/>
     <signal name="apply" handler="csv_tximp_assist_finish_cb" swapped="no"/>
     <signal name="prepare" handler="csv_tximp_assist_prepare_cb" swapped="no"/>
     <signal name="cancel" handler="csv_tximp_assist_cancel_cb" swapped="no"/>
-    <signal name="destroy" handler="csv_tximp_assist_destroy_cb" swapped="no"/>
     <child>
       <object class="GtkLabel" id="start_page">
         <property name="visible">True</property>
         <property name="can_focus">False</property>
-        <property name="label" translatable="yes">This assistant will help you import a delimited file containing a list of transactions.
+        <property name="label" translatable="yes">This assistant will help you import a delimited file containing a list of transactions. It supports both token separated files (such as comma separated or semi-colon separated) and fixed width data.
 
-There is a minimum number of columns that have to be present for a successful import, these are Date, Description and one of Balance, Deposit or Withdrawal.
+For a successful import three columns have to be available in the import data:
+• a Date column
+• a Description column
+• a Deposit or Withdrawal column
 
-If there is no Account column, all transactions imported will be associated to a selected account. If an Account column is used with a Balance column, then this column should only equate to one account.
+If there is no Account data available, a base account can be selected to which all data will be imported.
 
-If you have an Other Memo column, you must have an Other Account column.
+Apart from a choice of delimiter, there are several options to tweak the importer. For example a number of lines can be skipped at the start or the end of the data, as well as odd rows. Several date and number formats are supported. The file encoding can be defined.
 
-Various options exist for specifying the delimiter as well as a fixed width option. With the fixed width option, double click on the bar above the displayed rows to set the column width.
+The importer can handle files where transactions are split over multiple lines, with each line representing one split.
 
-There is an option for specifying the start row, end row and an option to skip alternate rows beginning from the start row. These can be used if you have some header text, a points collected status row or multiple accounts in the same file.
-
-On the preview page you can Load and Save the settings. To save the settings, select a previously saved entry or replace the text and press the Save Settings button.</property>
+Lastly, for repeated imports the preview page has buttons to Load and Save the settings. To save the settings, tweak the settings to your preferences (optionally starting from an existing preset), then (optionally change the settings name and press the Save Settings button. Note you can't save to built-in presets.</property>
         <property name="wrap">True</property>
       </object>
       <packing>
         <property name="page_type">intro</property>
-        <property name="title" translatable="yes">Transaction Import Assistant</property>
         <property name="complete">True</property>
       </packing>
     </child>
@@ -192,231 +202,354 @@ Select location and file name for the Import, then click 'OK'...
                     <property name="left_padding">5</property>
                     <property name="right_padding">5</property>
                     <child>
-                      <object class="GtkTable" id="table2">
+                      <object class="GtkVBox" id="vbox1">
                         <property name="visible">True</property>
                         <property name="can_focus">False</property>
-                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                        <property name="n_rows">7</property>
-                        <property name="n_columns">3</property>
-                        <property name="column_spacing">3</property>
-                        <child>
-                          <object class="GtkCheckButton" id="space_cbutton">
-                            <property name="label" translatable="yes">Space</property>
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="receives_default">False</property>
-                            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                            <property name="draw_indicator">True</property>
-                            <signal name="toggled" handler="csv_tximp_preview_sep_button_cb" swapped="no"/>
-                          </object>
-                          <packing>
-                            <property name="top_attach">2</property>
-                            <property name="bottom_attach">3</property>
-                            <property name="x_options">GTK_FILL</property>
-                            <property name="y_options">GTK_FILL</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <object class="GtkCheckButton" id="tab_cbutton">
-                            <property name="label" translatable="yes">Tab</property>
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="receives_default">False</property>
-                            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                            <property name="draw_indicator">True</property>
-                            <signal name="toggled" handler="csv_tximp_preview_sep_button_cb" swapped="no"/>
-                          </object>
-                          <packing>
-                            <property name="left_attach">1</property>
-                            <property name="right_attach">2</property>
-                            <property name="top_attach">2</property>
-                            <property name="bottom_attach">3</property>
-                            <property name="x_options">GTK_FILL</property>
-                            <property name="y_options">GTK_FILL</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <object class="GtkCheckButton" id="comma_cbutton">
-                            <property name="label" translatable="yes">Comma (,)</property>
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="receives_default">False</property>
-                            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                            <property name="active">True</property>
-                            <property name="draw_indicator">True</property>
-                            <signal name="toggled" handler="csv_tximp_preview_sep_button_cb" swapped="no"/>
-                          </object>
-                          <packing>
-                            <property name="left_attach">2</property>
-                            <property name="right_attach">3</property>
-                            <property name="top_attach">2</property>
-                            <property name="bottom_attach">3</property>
-                            <property name="x_options">GTK_FILL</property>
-                            <property name="y_options">GTK_FILL</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <object class="GtkCheckButton" id="colon_cbutton">
-                            <property name="label" translatable="yes">Colon (:)</property>
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="receives_default">False</property>
-                            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                            <property name="draw_indicator">True</property>
-                            <signal name="toggled" handler="csv_tximp_preview_sep_button_cb" swapped="no"/>
-                          </object>
-                          <packing>
-                            <property name="top_attach">3</property>
-                            <property name="bottom_attach">4</property>
-                            <property name="x_options">GTK_FILL</property>
-                            <property name="y_options">GTK_FILL</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <object class="GtkCheckButton" id="semicolon_cbutton">
-                            <property name="label" translatable="yes">Semicolon (;)</property>
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="receives_default">False</property>
-                            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                            <property name="draw_indicator">True</property>
-                            <signal name="toggled" handler="csv_tximp_preview_sep_button_cb" swapped="no"/>
-                          </object>
-                          <packing>
-                            <property name="left_attach">1</property>
-                            <property name="right_attach">2</property>
-                            <property name="top_attach">3</property>
-                            <property name="bottom_attach">4</property>
-                            <property name="x_options">GTK_FILL</property>
-                            <property name="y_options">GTK_FILL</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <object class="GtkCheckButton" id="hyphen_cbutton">
-                            <property name="label" translatable="yes">Hyphen (-)</property>
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="receives_default">False</property>
-                            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                            <property name="draw_indicator">True</property>
-                            <signal name="toggled" handler="csv_tximp_preview_sep_button_cb" swapped="no"/>
-                          </object>
-                          <packing>
-                            <property name="left_attach">2</property>
-                            <property name="right_attach">3</property>
-                            <property name="top_attach">3</property>
-                            <property name="bottom_attach">4</property>
-                            <property name="x_options">GTK_FILL</property>
-                            <property name="y_options">GTK_FILL</property>
-                          </packing>
-                        </child>
                         <child>
-                          <object class="GtkCheckButton" id="custom_cbutton">
-                            <property name="label" translatable="yes">Custom</property>
+                          <object class="GtkTable" id="table4">
                             <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="receives_default">False</property>
-                            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                            <property name="draw_indicator">True</property>
-                            <signal name="toggled" handler="csv_tximp_preview_sep_button_cb" swapped="no"/>
-                          </object>
-                          <packing>
-                            <property name="top_attach">4</property>
-                            <property name="bottom_attach">5</property>
-                            <property name="x_options">GTK_FILL</property>
-                            <property name="y_options">GTK_FILL</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <object class="GtkEntry" id="custom_entry">
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                            <property name="invisible_char">●</property>
-                            <property name="invisible_char_set">True</property>
-                            <property name="primary_icon_activatable">False</property>
-                            <property name="secondary_icon_activatable">False</property>
-                            <property name="primary_icon_sensitive">True</property>
-                            <property name="secondary_icon_sensitive">True</property>
-                            <signal name="changed" handler="csv_tximp_preview_sep_button_cb" swapped="no"/>
+                            <property name="can_focus">False</property>
+                            <property name="n_rows">2</property>
+                            <property name="n_columns">2</property>
+                            <child>
+                              <object class="GtkRadioButton" id="csv_button">
+                                <property name="label" translatable="yes">Separators</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">False</property>
+                                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                                <property name="active">True</property>
+                                <property name="draw_indicator">True</property>
+                                <signal name="toggled" handler="csv_tximp_preview_sep_fixed_sel_cb" swapped="no"/>
+                              </object>
+                            </child>
+                            <child>
+                              <object class="GtkRadioButton" id="fixed_button">
+                                <property name="label" translatable="yes">Fixed-Width</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">False</property>
+                                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                                <property name="draw_indicator">True</property>
+                                <property name="group">csv_button</property>
+                              </object>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="right_attach">2</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkHSeparator" id="hseparator1">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                              </object>
+                              <packing>
+                                <property name="right_attach">2</property>
+                                <property name="top_attach">1</property>
+                                <property name="bottom_attach">2</property>
+                                <property name="y_options">GTK_FILL</property>
+                              </packing>
+                            </child>
                           </object>
                           <packing>
-                            <property name="left_attach">1</property>
-                            <property name="right_attach">3</property>
-                            <property name="top_attach">4</property>
-                            <property name="bottom_attach">5</property>
-                            <property name="x_options">GTK_FILL</property>
-                            <property name="y_options">GTK_FILL</property>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">0</property>
                           </packing>
                         </child>
                         <child>
-                          <object class="GtkRadioButton" id="csv_button">
-                            <property name="label" translatable="yes">Separators</property>
+                          <object class="GtkTable" id="separator_table">
                             <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="receives_default">False</property>
-                            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                            <property name="active">True</property>
-                            <property name="draw_indicator">True</property>
-                            <signal name="toggled" handler="csv_tximp_preview_sep_fixed_sel_cb" swapped="no"/>
-                          </object>
-                        </child>
-                        <child>
-                          <object class="GtkRadioButton" id="fixed_button">
-                            <property name="label" translatable="yes">Fixed-Width</property>
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="receives_default">False</property>
+                            <property name="can_focus">False</property>
                             <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                            <property name="draw_indicator">True</property>
-                            <property name="group">csv_button</property>
+                            <property name="n_rows">3</property>
+                            <property name="n_columns">3</property>
+                            <property name="column_spacing">3</property>
+                            <child>
+                              <object class="GtkCheckButton" id="space_cbutton">
+                                <property name="label" translatable="yes">Space</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">False</property>
+                                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                                <property name="draw_indicator">True</property>
+                                <signal name="toggled" handler="csv_tximp_preview_sep_button_cb" swapped="no"/>
+                              </object>
+                              <packing>
+                                <property name="x_options">GTK_FILL</property>
+                                <property name="y_options">GTK_FILL</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkCheckButton" id="tab_cbutton">
+                                <property name="label" translatable="yes">Tab</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">False</property>
+                                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                                <property name="draw_indicator">True</property>
+                                <signal name="toggled" handler="csv_tximp_preview_sep_button_cb" swapped="no"/>
+                              </object>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="right_attach">2</property>
+                                <property name="x_options">GTK_FILL</property>
+                                <property name="y_options">GTK_FILL</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkCheckButton" id="comma_cbutton">
+                                <property name="label" translatable="yes">Comma (,)</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">False</property>
+                                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                                <property name="active">True</property>
+                                <property name="draw_indicator">True</property>
+                                <signal name="toggled" handler="csv_tximp_preview_sep_button_cb" swapped="no"/>
+                              </object>
+                              <packing>
+                                <property name="left_attach">2</property>
+                                <property name="right_attach">3</property>
+                                <property name="x_options">GTK_FILL</property>
+                                <property name="y_options">GTK_FILL</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkCheckButton" id="colon_cbutton">
+                                <property name="label" translatable="yes">Colon (:)</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">False</property>
+                                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                                <property name="draw_indicator">True</property>
+                                <signal name="toggled" handler="csv_tximp_preview_sep_button_cb" swapped="no"/>
+                              </object>
+                              <packing>
+                                <property name="top_attach">1</property>
+                                <property name="bottom_attach">2</property>
+                                <property name="x_options">GTK_FILL</property>
+                                <property name="y_options">GTK_FILL</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkCheckButton" id="semicolon_cbutton">
+                                <property name="label" translatable="yes">Semicolon (;)</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">False</property>
+                                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                                <property name="draw_indicator">True</property>
+                                <signal name="toggled" handler="csv_tximp_preview_sep_button_cb" swapped="no"/>
+                              </object>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="right_attach">2</property>
+                                <property name="top_attach">1</property>
+                                <property name="bottom_attach">2</property>
+                                <property name="x_options">GTK_FILL</property>
+                                <property name="y_options">GTK_FILL</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkCheckButton" id="hyphen_cbutton">
+                                <property name="label" translatable="yes">Hyphen (-)</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">False</property>
+                                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                                <property name="draw_indicator">True</property>
+                                <signal name="toggled" handler="csv_tximp_preview_sep_button_cb" swapped="no"/>
+                              </object>
+                              <packing>
+                                <property name="left_attach">2</property>
+                                <property name="right_attach">3</property>
+                                <property name="top_attach">1</property>
+                                <property name="bottom_attach">2</property>
+                                <property name="x_options">GTK_FILL</property>
+                                <property name="y_options">GTK_FILL</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkCheckButton" id="custom_cbutton">
+                                <property name="label" translatable="yes">Custom</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">False</property>
+                                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                                <property name="draw_indicator">True</property>
+                                <signal name="toggled" handler="csv_tximp_preview_sep_button_cb" swapped="no"/>
+                              </object>
+                              <packing>
+                                <property name="top_attach">2</property>
+                                <property name="bottom_attach">3</property>
+                                <property name="x_options">GTK_FILL</property>
+                                <property name="y_options">GTK_FILL</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkEntry" id="custom_entry">
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                                <property name="invisible_char">●</property>
+                                <property name="invisible_char_set">True</property>
+                                <property name="primary_icon_activatable">False</property>
+                                <property name="secondary_icon_activatable">False</property>
+                                <property name="primary_icon_sensitive">True</property>
+                                <property name="secondary_icon_sensitive">True</property>
+                                <signal name="changed" handler="csv_tximp_preview_sep_button_cb" swapped="no"/>
+                              </object>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="right_attach">3</property>
+                                <property name="top_attach">2</property>
+                                <property name="bottom_attach">3</property>
+                                <property name="x_options">GTK_FILL</property>
+                                <property name="y_options">GTK_FILL</property>
+                              </packing>
+                            </child>
                           </object>
                           <packing>
-                            <property name="left_attach">1</property>
-                            <property name="right_attach">2</property>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">1</property>
                           </packing>
                         </child>
                         <child>
-                          <object class="GtkHSeparator" id="hseparator1">
-                            <property name="visible">True</property>
+                          <object class="GtkHBox" id="fw_instructions_hbox">
                             <property name="can_focus">False</property>
+                            <property name="no_show_all">True</property>
+                            <child>
+                              <object class="GtkImage" id="instructions_image1">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                                <property name="yalign">0</property>
+                                <property name="stock">gtk-dialog-info</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">True</property>
+                                <property name="padding">2</property>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkTable" id="table2">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="n_rows">2</property>
+                                <property name="n_columns">2</property>
+                                <child>
+                                  <object class="GtkLabel" id="label2">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">False</property>
+                                    <property name="yalign">0</property>
+                                    <property name="xpad">5</property>
+                                    <property name="label" translatable="yes">•</property>
+                                    <property name="use_markup">True</property>
+                                    <property name="wrap">True</property>
+                                  </object>
+                                </child>
+                                <child>
+                                  <object class="GtkLabel" id="label3">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">False</property>
+                                    <property name="xalign">0</property>
+                                    <property name="label" translatable="yes">Double-click anywhere on the table below to insert a column break</property>
+                                    <property name="use_markup">True</property>
+                                    <property name="wrap">True</property>
+                                  </object>
+                                  <packing>
+                                    <property name="left_attach">1</property>
+                                    <property name="right_attach">2</property>
+                                    <property name="y_options"/>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <object class="GtkLabel" id="label4">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">False</property>
+                                    <property name="yalign">0</property>
+                                    <property name="xpad">5</property>
+                                    <property name="label" translatable="yes">•</property>
+                                    <property name="use_markup">True</property>
+                                    <property name="wrap">True</property>
+                                  </object>
+                                  <packing>
+                                    <property name="top_attach">1</property>
+                                    <property name="bottom_attach">2</property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <object class="GtkLabel" id="label5">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">False</property>
+                                    <property name="xalign">0</property>
+                                    <property name="label" translatable="yes">Right-click anywhere in a column to modify it (widen, narrow, merge)</property>
+                                    <property name="use_markup">True</property>
+                                    <property name="wrap">True</property>
+                                  </object>
+                                  <packing>
+                                    <property name="left_attach">1</property>
+                                    <property name="right_attach">2</property>
+                                    <property name="top_attach">1</property>
+                                    <property name="bottom_attach">2</property>
+                                    <property name="y_options"/>
+                                  </packing>
+                                </child>
+                              </object>
+                              <packing>
+                                <property name="expand">True</property>
+                                <property name="fill">True</property>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
                           </object>
                           <packing>
-                            <property name="right_attach">3</property>
-                            <property name="top_attach">1</property>
-                            <property name="bottom_attach">2</property>
-                            <property name="y_options">GTK_FILL</property>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">2</property>
                           </packing>
                         </child>
                         <child>
-                          <object class="GtkHSeparator" id="hseparator4">
+                          <object class="GtkTable" id="table5">
                             <property name="visible">True</property>
                             <property name="can_focus">False</property>
-                          </object>
-                          <packing>
-                            <property name="right_attach">3</property>
-                            <property name="top_attach">5</property>
-                            <property name="bottom_attach">6</property>
-                            <property name="y_options">GTK_FILL</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <object class="GtkCheckButton" id="multi_split_button">
-                            <property name="label" translatable="yes">Multi-split</property>
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="receives_default">False</property>
-                            <property name="tooltip_text" translatable="yes">Normally the importer will assume each line in the input file will correspond to one transaction. Each line can have information for one transaction and one or two splits.
+                            <property name="n_rows">2</property>
+                            <child>
+                              <object class="GtkHSeparator" id="hseparator4">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                              </object>
+                              <packing>
+                                <property name="y_options">GTK_FILL</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkCheckButton" id="multi_split_button">
+                                <property name="label" translatable="yes">Multi-split</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">False</property>
+                                <property name="tooltip_text" translatable="yes">Normally the importer will assume each line in the input file will correspond to one transaction. Each line can have information for one transaction and one or two splits.
 
 When Multi-split is enabled the importer will assume multiple consecutive lines together hold the information for one transaction. Each line provides information for exactly one split. The first line should also provide the information for the transaction.
 To know which lines belong to the same transaction, the importer will compare the provided transaction information in each line. If that information is empty or the same as the first transaction line the importer will consider this line part of the same transaction.</property>
-                            <property name="draw_indicator">True</property>
-                            <signal name="toggled" handler="csv_tximp_preview_multisplit_cb" swapped="no"/>
+                                <property name="draw_indicator">True</property>
+                                <signal name="toggled" handler="csv_tximp_preview_multisplit_cb" swapped="no"/>
+                              </object>
+                              <packing>
+                                <property name="top_attach">1</property>
+                                <property name="bottom_attach">2</property>
+                              </packing>
+                            </child>
                           </object>
                           <packing>
-                            <property name="right_attach">3</property>
-                            <property name="top_attach">6</property>
-                            <property name="bottom_attach">7</property>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">3</property>
                           </packing>
                         </child>
                         <child>
@@ -752,47 +885,6 @@ For example
           </packing>
         </child>
         <child>
-          <object class="GtkHBox" id="hbox13">
-            <property name="visible">True</property>
-            <property name="can_focus">False</property>
-            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-            <child>
-              <object class="GtkImage" id="instructions_image">
-                <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                <property name="stock">gtk-dialog-info</property>
-              </object>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">True</property>
-                <property name="padding">2</property>
-                <property name="position">0</property>
-              </packing>
-            </child>
-            <child>
-              <object class="GtkLabel" id="instructions_label">
-                <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                <property name="xalign">0</property>
-                <property name="label" translatable="yes">Select the type of each column below.</property>
-              </object>
-              <packing>
-                <property name="expand">True</property>
-                <property name="fill">True</property>
-                <property name="position">1</property>
-              </packing>
-            </child>
-          </object>
-          <packing>
-            <property name="expand">False</property>
-            <property name="fill">False</property>
-            <property name="padding">5</property>
-            <property name="position">1</property>
-          </packing>
-        </child>
-        <child>
           <object class="GtkScrolledWindow" id="scrolledwindow2">
             <property name="visible">True</property>
             <property name="can_focus">True</property>
@@ -841,6 +933,48 @@ For example
           <packing>
             <property name="expand">True</property>
             <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkHBox" id="hbox13">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+            <child>
+              <object class="GtkImage" id="instructions_image">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                <property name="yalign">0</property>
+                <property name="stock">gtk-dialog-info</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="padding">2</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="instructions_label">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Select the type of each column to import.</property>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="padding">5</property>
             <property name="position">2</property>
           </packing>
         </child>
@@ -916,6 +1050,7 @@ For example
                 <property name="rules_hint">True</property>
                 <property name="enable_search">False</property>
                 <property name="enable_tree_lines">True</property>
+                <signal name="button-press-event" handler="csv_tximp_acct_match_view_clicked_cb" swapped="no"/>
                 <child>
                   <object class="GtkTreeViewColumn" id="treeviewcolumn1">
                     <property name="resizable">True</property>
@@ -941,7 +1076,6 @@ For example
                     </child>
                   </object>
                 </child>
-                <signal name="button_press_event" handler="csv_tximp_acct_match_view_clicked_cb" swapped="no"/>
               </object>
             </child>
           </object>
@@ -1081,14 +1215,4 @@ More information can be displayed by using the help button.</property>
       </packing>
     </child>
   </object>
-  <object class="GtkAdjustment" id="end_row_adj">
-    <property name="upper">1000</property>
-    <property name="step_increment">1</property>
-    <property name="page_increment">10</property>
-  </object>
-  <object class="GtkAdjustment" id="start_row_adj">
-    <property name="upper">1000</property>
-    <property name="step_increment">1</property>
-    <property name="page_increment">10</property>
-  </object>
 </interface>

commit a29568021291748bc51cea891d60ce252853a212
Author: Geert Janssens <geert at kobaltwit.be>
Date:   Mon Feb 20 11:40:36 2017 +0100

    Cleanup - use less ambiguous variable name for an iterator

diff --git a/src/import-export/csv-imp/gnc-tx-import.cpp b/src/import-export/csv-imp/gnc-tx-import.cpp
index 392e321..b7f73f4 100644
--- a/src/import-export/csv-imp/gnc-tx-import.cpp
+++ b/src/import-export/csv-imp/gnc-tx-import.cpp
@@ -186,10 +186,10 @@ void GncTxImport::base_account (Account* base_account)
 
     if (m_settings.m_base_account)
     {
-        auto col_type = std::find (m_settings.m_column_types.begin(),
+        auto col_type_it = std::find (m_settings.m_column_types.begin(),
                 m_settings.m_column_types.end(), GncTransPropType::ACCOUNT);
-        if (col_type != m_settings.m_column_types.end())
-            set_column_type(col_type -m_settings.m_column_types.begin(),
+        if (col_type_it != m_settings.m_column_types.end())
+            set_column_type(col_type_it - m_settings.m_column_types.begin(),
                             GncTransPropType::NONE);
 
         /* Set default account for each line's split properties */

commit 372c46cc0f2dcb1f275f167d9c6fd4c00e09ffe3
Author: Geert Janssens <geert at kobaltwit.be>
Date:   Mon Feb 20 11:38:28 2017 +0100

    Fix base account reset when an account column is selected
    
    This got broken somehow while refactoring the assistant.

diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index b5867cb..67bd855 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -1521,6 +1521,12 @@ void CsvImpTransAssist::preview_refresh_table ()
     g_object_unref (store);
     g_object_unref (combostore);
 
+    /* Also reset the base account combo box as it's value may have changed due to column changes here */
+    g_signal_handlers_block_by_func (acct_selector, (gpointer) csv_tximp_preview_acct_sel_cb, this);
+    gnc_account_sel_set_account(GNC_ACCOUNT_SEL(acct_selector),
+            tx_imp->base_account() , false);
+    g_signal_handlers_unblock_by_func (acct_selector, (gpointer) csv_tximp_preview_acct_sel_cb, this);
+
     /* Make the things actually appear. */
     gtk_widget_show_all (GTK_WIDGET(treeview));
 }
@@ -1550,6 +1556,7 @@ CsvImpTransAssist::preview_refresh ()
     // Set multi-split indicator
     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(multi_split_cbutton),
             tx_imp->multi_split());
+    gtk_widget_set_sensitive (acct_selector, !tx_imp->multi_split());
 
     // Set Import Format
     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(csv_button),
@@ -1564,10 +1571,6 @@ CsvImpTransAssist::preview_refresh ()
             tx_imp->currency_format());
     go_charmap_sel_set_encoding (encselector, tx_imp->encoding().c_str());
 
-    gnc_account_sel_set_account(GNC_ACCOUNT_SEL(acct_selector),
-            tx_imp->base_account() , false);
-    gtk_widget_set_sensitive (acct_selector, !tx_imp->multi_split());
-
     // Handle separator checkboxes and custom field, only relevant if the file format is csv
     if (tx_imp->file_format() == GncImpFileFormat::CSV)
     {

commit 61244301f22df436515fd9fb3988e0714ecf8f57
Author: Geert Janssens <geert at kobaltwit.be>
Date:   Sat Feb 18 22:34:02 2017 +0100

    Recover separators or column_widths when juggling file format more than once

diff --git a/src/import-export/csv-imp/gnc-tx-import.cpp b/src/import-export/csv-imp/gnc-tx-import.cpp
index 01fe06a..392e321 100644
--- a/src/import-export/csv-imp/gnc-tx-import.cpp
+++ b/src/import-export/csv-imp/gnc-tx-import.cpp
@@ -97,6 +97,12 @@ void GncTxImport::file_format(GncImpFileFormat format)
     {
         new_encoding = m_tokenizer->encoding();
         new_imp_file = m_tokenizer->current_file();
+        if (file_format() == GncImpFileFormat::FIXED_WIDTH)
+        {
+            auto fwtok = dynamic_cast<GncFwTokenizer*>(m_tokenizer.get());
+            if (!fwtok->get_columns().empty())
+                m_settings.m_column_widths = fwtok->get_columns();
+        }
     }
 
     m_settings.m_file_format = format;
@@ -106,6 +112,18 @@ void GncTxImport::file_format(GncImpFileFormat format)
     // recovered from old tokenizer
     m_tokenizer->encoding(new_encoding);
     load_file(new_imp_file);
+
+    // Restore potentially previously set separators or column_widths
+    if ((file_format() == GncImpFileFormat::CSV)
+        && !m_settings.m_separators.empty())
+        separators (m_settings.m_separators);
+    else if ((file_format() == GncImpFileFormat::FIXED_WIDTH)
+        && !m_settings.m_column_widths.empty())
+    {
+        auto fwtok = dynamic_cast<GncFwTokenizer*>(m_tokenizer.get());
+        fwtok->columns (m_settings.m_column_widths);
+    }
+
 }
 
 GncImpFileFormat GncTxImport::file_format()

commit 5d96e47b3956b76eff38c57de15be9fbf565a69d
Author: Geert Janssens <geert at kobaltwit.be>
Date:   Fri Feb 17 21:58:48 2017 +0100

    Prevent multi-byte characters from erroneously being split in fixed-width mode
    
    This happens because in case std::string is the base type for the tokenizer
    the offsets are interpreted as byte offsets although some characters
    may consist of more that one byte in utf-8. This is not so for std::wstring
    so to solve this issue the fixed width tokenizer is changed to use
    wide character strings internally.

diff --git a/src/import-export/csv-imp/gnc-fw-tokenizer.cpp b/src/import-export/csv-imp/gnc-fw-tokenizer.cpp
index c4b6270..acd5c27 100644
--- a/src/import-export/csv-imp/gnc-fw-tokenizer.cpp
+++ b/src/import-export/csv-imp/gnc-fw-tokenizer.cpp
@@ -1,5 +1,6 @@
 #include "gnc-fw-tokenizer.hpp"
 
+#include <codecvt>
 #include <iostream>
 #include <fstream>      // fstream
 #include <vector>
@@ -153,26 +154,34 @@ void GncFwTokenizer::load_file(const std::string& path)
     }
 }
 
-
+/* Fixed width tokenizer uses wide characters internally instead of
+ * narrow (possibly multi-byte) characters. With multi-byte characters
+ * the character offsets are incorrectly interpreted as byte offsets and
+ * multi-byte characters (like the € sign in utf-8) could be inadvertently
+ * split. This doesn't happen with wide characters.
+ */
 int GncFwTokenizer::tokenize()
 {
-    using Tokenizer = boost::tokenizer< boost::offset_separator > ;
+    using Tokenizer = boost::tokenizer< boost::offset_separator,
+            std::wstring::const_iterator, std::wstring > ;
 
     boost::offset_separator sep(m_col_vec.begin(), m_col_vec.end(), false);
 
+    std::wstring_convert<std::codecvt_utf8<wchar_t>> conv;
+    std::wstring wchar_contents = conv.from_bytes(m_utf8_contents);
+
     StrVec vec;
-    std::string line;
-    std::string buffer;
+    std::wstring line;
 
     m_tokenized_contents.clear();
-    std::istringstream in_stream(m_utf8_contents);
+    std::wistringstream in_stream(wchar_contents);
 
     while (std::getline (in_stream, line))
     {
         Tokenizer tok(line, sep);
         vec.clear();
         for (auto token : tok)
-            vec.push_back (boost::trim_copy(token)); // strips newlines as well as whitespace
+            vec.push_back (conv.to_bytes(boost::trim_copy(token))); // strips newlines as well as whitespace
         m_tokenized_contents.push_back(vec);
         line.clear(); // clear here, next check could fail
     }

commit 8d03abac19e5e9fb439409acee35196c2f0eae56
Author: Geert Janssens <geert at kobaltwit.be>
Date:   Wed Feb 15 22:12:14 2017 +0100

    Include glib.h as everywhere else

diff --git a/src/libqof/qof/test/test-qofbackend.c b/src/libqof/qof/test/test-qofbackend.c
index a2bb369..23500b0 100644
--- a/src/libqof/qof/test/test-qofbackend.c
+++ b/src/libqof/qof/test/test-qofbackend.c
@@ -20,7 +20,7 @@
  * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
 \********************************************************************/
 
-#include <glib/glib.h>
+#include <glib.h>
 
 static const gchar *suitename = "qof/qofbackend";
 

commit 6657e6660bfa6adb2de11f3b19b55954db3e6e39
Author: Geert Janssens <geert at kobaltwit.be>
Date:   Sat Feb 11 11:27:35 2017 +0100

    Remove some obsolete comments and commented out code

diff --git a/src/import-export/csv-imp/gnc-csv-tokenizer.cpp b/src/import-export/csv-imp/gnc-csv-tokenizer.cpp
index f6daddc..54532c7 100644
--- a/src/import-export/csv-imp/gnc-csv-tokenizer.cpp
+++ b/src/import-export/csv-imp/gnc-csv-tokenizer.cpp
@@ -59,14 +59,8 @@ int GncCsvTokenizer::tokenize()
 
         Tokenizer tok(line, sep);
         vec.assign(tok.begin(),tok.end());
-
-        line.clear(); // clear here, next check could fail
-
-        // example checking
-        // for correctly parsed 3 fields per record
-        // if (vec.size() < 3) continue;
-
         m_tokenized_contents.push_back(vec);
+        line.clear();
     }
 
     return 0;
diff --git a/src/import-export/csv-imp/gnc-fw-tokenizer.cpp b/src/import-export/csv-imp/gnc-fw-tokenizer.cpp
index 35f20b7..c4b6270 100644
--- a/src/import-export/csv-imp/gnc-fw-tokenizer.cpp
+++ b/src/import-export/csv-imp/gnc-fw-tokenizer.cpp
@@ -172,20 +172,9 @@ int GncFwTokenizer::tokenize()
         Tokenizer tok(line, sep);
         vec.clear();
         for (auto token : tok)
-            vec.push_back (boost::trim_copy(token));
-        //vec.assign(tok.begin(),tok.end());
-
-        // Trim all leading and trailing whitespace
-        //for (auto token : vec)
-        //    boost::trim(token);
-
-        line.clear(); // clear here, next check could fail
-
-        // example checking
-        // for correctly parsed 3 fields per record
-        // if (vec.size() < 3) continue;
-
+            vec.push_back (boost::trim_copy(token)); // strips newlines as well as whitespace
         m_tokenized_contents.push_back(vec);
+        line.clear(); // clear here, next check could fail
     }
 
     return 0;

commit 4687eb649b4a9ade874d57fd69606e5c6f62e31c
Author: Geert Janssens <geert at kobaltwit.be>
Date:   Sat Feb 11 11:13:39 2017 +0100

    Trim newlines from csv data
    
    If the newline happens in the middle some field, replace it with a space.
    We can't import newlines in gnucash.

diff --git a/src/import-export/csv-imp/gnc-csv-tokenizer.cpp b/src/import-export/csv-imp/gnc-csv-tokenizer.cpp
index 6a24826..f6daddc 100644
--- a/src/import-export/csv-imp/gnc-csv-tokenizer.cpp
+++ b/src/import-export/csv-imp/gnc-csv-tokenizer.cpp
@@ -9,6 +9,7 @@
 
 #include <boost/tokenizer.hpp>
 #include <boost/locale.hpp>
+#include <boost/algorithm/string.hpp>
 
 void
 GncCsvTokenizer::set_separators(const std::string& separators)
@@ -36,6 +37,7 @@ int GncCsvTokenizer::tokenize()
     while (std::getline (in_stream, buffer))
     {
         // --- deal with line breaks in quoted strings
+        buffer = boost::trim_copy (buffer); // Removes trailing newline and spaces
         last_quote = buffer.find_first_of('"');
         while (last_quote != std::string::npos)
         {
@@ -50,7 +52,7 @@ int GncCsvTokenizer::tokenize()
         line.append(buffer);
         if (inside_quotes)
         {
-            line.append("\n");
+            line.append(" ");
             continue;
         }
         // ---

commit 9fbbd7366c5efbbd0da8b19b25261752dae1a366
Author: Geert Janssens <geert at kobaltwit.be>
Date:   Fri Feb 10 21:34:17 2017 +0100

    Fix crash when adding columns by double-clicking the preview data
    
    Each column change would cause the code to completely delete the
    existing columns and then add new ones from scratch. For some
    reason his upsets the MOVE_NOTIFY event, which is triggered
    whenever you move the mouse pointer around. I suspect it internally
    keeps a link to the last hovered column to check if the move action
    moves the mouse to a different column. As the code removed the columns
    this internal references becomes invalid.
    
    I have worked around this by no longer completely recreating the
    columns. Instead columns are added or removed as needed to follow
    the changes in the data model.
    
    At the same time the refresh function has been split into several
    smaller ones.

diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index ab9272f..b5867cb 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -100,7 +100,7 @@ public:
     void preview_update_date_format ();
     void preview_update_currency_format ();
     void preview_update_col_type (GtkComboBox* cbox);
-    bool preview_update_fw_columns (GtkTreeView* treeview, GdkEventButton* event);
+    void preview_update_fw_columns (GtkTreeView* treeview, GdkEventButton* event);
 
     void preview_populate_settings_combo();
     void preview_handle_save_del_sensitivity (GtkComboBox* combo);
@@ -118,12 +118,16 @@ public:
     fixed_context_menu_handler (GnumericPopupMenuElement const *element,
             gpointer userdata);
 private:
-    /* member functions to manage the context menu for fixed with columns */
+    /* helper functions to manage the context menu for fixed with columns */
     uint get_new_col_rel_pos (GtkTreeViewColumn *tcol, int dx);
     void fixed_context_menu (GdkEventButton *event, int col, int dx);
-    /* member function to calculate row colors for the preview table (to visualize status) */
+    /* helper function to calculate row colors for the preview table (to visualize status) */
     void preview_row_fill_state_cells (GtkListStore *store, GtkTreeIter *iter,
             std::string& err_msg, bool skip);
+    /* helper function to create preview header cell combo boxes listing available column types */
+    GtkWidget* preview_cbox_factory (GtkTreeModel* model, uint colnum);
+    /* helper function to set rendering parameters for preview data columns */
+    void preview_style_column (uint col_num, GtkTreeModel* model);
 
     GtkAssistant    *csv_imp_asst;
 
@@ -358,7 +362,8 @@ gboolean
 csv_tximp_preview_treeview_clicked_cb (GtkTreeView* treeview, GdkEventButton* event,
                                         CsvImpTransAssist* info)
 {
-    return info->preview_update_fw_columns(treeview, event);
+    info->preview_update_fw_columns(treeview, event);
+    return false;
 }
 
 
@@ -961,7 +966,7 @@ void CsvImpTransAssist::preview_update_file_format ()
         {
             tx_imp->file_format (GncImpFileFormat::FIXED_WIDTH);
             /* Enable context menu for adding/removing columns. */
-            g_signal_connect (G_OBJECT(treeview), "button_press_event",
+            g_signal_connect (G_OBJECT(treeview), "button-press-event",
                     G_CALLBACK(csv_tximp_preview_treeview_clicked_cb), (gpointer)this);
 
         }
@@ -1065,7 +1070,7 @@ enum PreviewDataTableCols {
 
 /** Event handler for the user selecting a new column type. When the
  * user selects a new column type, that column's text must be changed
- * to that selection, and any other columns containing that selection
+ * to the selection, and any other columns containing that selection
  * must be changed to "None" because we don't allow duplicates.
  * @param renderer The renderer of the column the user changed
  * @param path There is only 1 row in info->ctreeview, so this is always 0.
@@ -1269,12 +1274,12 @@ CsvImpTransAssist::preview_split_column (int col, int offset)
  * @returns true if further processing of this even should stop, false
  *               if other event handlers can have a go at this as well
  */
-bool
+void
 CsvImpTransAssist::preview_update_fw_columns (GtkTreeView* treeview, GdkEventButton* event)
 {
     /* Nothing to do if this was not triggered on our treeview body */
     if (event->window != gtk_tree_view_get_bin_window (treeview))
-        return false;
+        return;
 
     /* Find the column that was clicked. */
     GtkTreeViewColumn *tcol = nullptr;
@@ -1283,7 +1288,7 @@ CsvImpTransAssist::preview_update_fw_columns (GtkTreeView* treeview, GdkEventBut
             (int)event->x, (int)event->y,
             nullptr, &tcol, &cell_x, nullptr);
     if (!success)
-        return false;
+        return;
 
     /* Stop if no column found in this treeview (-1) or
      * if column is the error messages column (0) */
@@ -1291,7 +1296,7 @@ CsvImpTransAssist::preview_update_fw_columns (GtkTreeView* treeview, GdkEventBut
     auto tcol_num = g_list_index (tcol_list, tcol);
     g_list_free (tcol_list);
     if (tcol_num <= 0)
-        return false;
+        return;
 
     /* Data columns in the treeview are offset by one
      * because the first column is the error column
@@ -1299,16 +1304,11 @@ CsvImpTransAssist::preview_update_fw_columns (GtkTreeView* treeview, GdkEventBut
     auto dcol = tcol_num - 1;
     auto offset = get_new_col_rel_pos (tcol, cell_x);
     if (event->type == GDK_2BUTTON_PRESS && event->button == 1)
-    {
         /* Double clicks can split columns. */
         preview_split_column (dcol, offset);
-        return true; // Stop processing here !
-    }
     else if (event->type == GDK_BUTTON_PRESS && event->button == 3)
         /* Right clicking brings up a context menu. */
         fixed_context_menu (event, dcol, offset);
-
-    return false;
 }
 
 
@@ -1340,8 +1340,8 @@ CsvImpTransAssist::preview_row_fill_state_cells (GtkListStore *store, GtkTreeIte
 /* Helper function that creates a combo_box using a model
  * with valid column types and selects the given column type
  */
-static GtkWidget*
-cbox_factory (GtkTreeModel* model, GncTransPropType col_type)
+GtkWidget*
+CsvImpTransAssist::preview_cbox_factory (GtkTreeModel* model, uint colnum)
 {
     GtkTreeIter iter;
     auto cbox = gtk_combo_box_new_with_model(model);
@@ -1359,20 +1359,86 @@ cbox_factory (GtkTreeModel* model, GncTransPropType col_type)
         gint stored_col_type;
         gtk_tree_model_get (model, &iter,
                 COL_TYPE_ID, &stored_col_type, -1);
-        if (stored_col_type == static_cast<int>(col_type))
+        if (stored_col_type == static_cast<int>( tx_imp->column_types()[colnum]))
             break;
         valid = gtk_tree_model_iter_next(model, &iter);
     }
     if (valid)
         gtk_combo_box_set_active_iter (GTK_COMBO_BOX(cbox), &iter);
 
+    g_object_set_data (G_OBJECT(cbox), "col-num", GUINT_TO_POINTER(colnum));
+    g_signal_connect (G_OBJECT(cbox), "changed",
+                     G_CALLBACK(csv_tximp_preview_col_type_changed_cb), (gpointer)this);
+
     gtk_widget_show (cbox);
     return cbox;
 }
 
-/* Loads the preview's data into its data treeview.
- *
- * @param info The data being previewed
+void
+CsvImpTransAssist::preview_style_column (uint col_num, GtkTreeModel* model)
+{
+    auto col = gtk_tree_view_get_column (treeview, col_num);
+    auto renderer = static_cast<GtkCellRenderer*>(gtk_tree_view_column_get_cell_renderers(col)->data);
+    /* First column -the error status column- is rendered differently */
+    if (col_num == 0)
+    {
+        gtk_tree_view_column_set_attributes (col, renderer,
+                "stock-id", PREV_COL_ERR_ICON,
+                "cell-background", PREV_COL_BCOLOR, nullptr);
+        g_object_set (G_OBJECT(renderer), "stock-size", GTK_ICON_SIZE_MENU, nullptr);
+        g_object_set (G_OBJECT(col), "sizing", GTK_TREE_VIEW_COLUMN_FIXED,
+                "fixed-width", 20, nullptr);
+        gtk_tree_view_column_set_resizable (col, false);
+    }
+    else
+    {
+        gtk_tree_view_column_set_attributes (col, renderer,
+                "foreground", PREV_COL_FCOLOR,
+                "background", PREV_COL_BCOLOR,
+                "strikethrough", PREV_COL_STRIKE,
+                "text", col_num + PREV_N_FIXED_COLS -1, nullptr);
+
+        /* We want a monospace font fixed-width data is properly displayed. */
+        g_object_set (G_OBJECT(renderer), "family", "monospace", nullptr);
+
+        /* Add a combobox to select column types as column header. Each uses the same
+         * common model for the dropdown list. The selected value is taken
+         * from the column_types vector. */
+        auto cbox = preview_cbox_factory (GTK_TREE_MODEL(model), col_num - 1);
+        gtk_tree_view_column_set_widget (col, cbox);
+
+        /* Enable resizing of the columns. */
+        gtk_tree_view_column_set_resizable (col, true);
+        gtk_tree_view_column_set_clickable (col, true);
+    }
+
+}
+
+/* Helper to create a shared store for the header comboboxes in the preview treeview.
+ * It holds the possible column types */
+GtkTreeModel*
+make_column_header_model (bool multi_split)
+{
+    auto combostore = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
+    for (auto col_type : gnc_csv_col_type_strs)
+    {
+        /* Only add column types that make sense in
+         * the chosen import mode (multi-split vs two-split).
+         */
+        if (sanitize_trans_prop(col_type.first, multi_split) == col_type.first)
+        {
+            GtkTreeIter iter;
+            gtk_list_store_append (combostore, &iter);
+            gtk_list_store_set (combostore, &iter,
+                    COL_TYPE_NAME, _(col_type.second),
+                    COL_TYPE_ID, static_cast<int>(col_type.first), -1);
+        }
+    }
+    return GTK_TREE_MODEL(combostore);
+}
+
+/* Updates the preview treeview to show the data as parsed based on the user's
+ * import parameters.
  */
 void CsvImpTransAssist::preview_refresh_table ()
 {
@@ -1412,70 +1478,44 @@ void CsvImpTransAssist::preview_refresh_table ()
     gtk_tree_view_set_model (treeview, GTK_TREE_MODEL(store));
     gtk_tree_view_set_tooltip_column (treeview, PREV_COL_ERROR);
 
-    // Set up the file data treeview using the created model above
-    auto column = gtk_tree_view_get_column (treeview, 0);
-    while (column)
-    {
-        gtk_tree_view_remove_column (treeview, column);
-        column = gtk_tree_view_get_column (treeview, 0);
-    }
+    /* Adjust treeview to go with the just created model. This consists of adding
+     * or removing columns and resetting any parameters related to how
+     * the columns and data should be rendered.
+     */
 
-    /* combostore is a shared store for the header combocells in the header row.
-     * It holds the possible column types */
-    auto combostore = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
-    for (auto col_type : gnc_csv_col_type_strs)
+    /* Start with counting the current number of columns (ntcols)
+     * we have in the treeview */
+    auto columns = gtk_tree_view_get_columns (treeview);
+    auto ntcols = g_list_length(columns);
+    g_list_free (columns);
+
+    /* Drop redundant columns if the model has less data columns than the new model
+     * ntcols = n° of columns in treeview (1 error column + x data columns)
+     * ncols = n° of columns in model (fixed state columns + x data columns)
+     */
+    while (ntcols > ncols - PREV_N_FIXED_COLS + 1)
     {
-        /* Only add column types that make sense in
-         * the chosen import mode (multi-split vs two-split).
-         */
-        if (sanitize_trans_prop(col_type.first, tx_imp->multi_split()) == col_type.first)
-        {
-            GtkTreeIter iter;
-            gtk_list_store_append (combostore, &iter);
-            gtk_list_store_set (combostore, &iter,
-                    COL_TYPE_NAME, _(col_type.second),
-                    COL_TYPE_ID, static_cast<int>(col_type.first), -1);
-        }
+        auto col = gtk_tree_view_get_column (treeview, ntcols - 1);
+        gtk_tree_view_column_clear (col);
+        ntcols = gtk_tree_view_remove_column(treeview, col);
     }
 
-    /* Insert columns into the data treeview. */
-    for (uint i = 0; i < tx_imp->column_types().size() ; i++)
+    /* Insert columns if the model has more data columns than the treeview. */
+    while (ntcols < ncols - PREV_N_FIXED_COLS + 1)
     {
-        /* The file data treeview cells are simple text cells. */
+        /* Default cell renderer is text, except for the first (error) column */
         auto renderer = gtk_cell_renderer_text_new();
-        /* We want a monospace font for the data in case of fixed-width data. */
-        g_object_set (G_OBJECT(renderer), "family", "monospace", nullptr);
-
-        /* Add a single column for the treeview. */
-        auto col = gtk_tree_view_column_new_with_attributes ("", renderer,
-                "foreground", PREV_COL_FCOLOR,
-                "background", PREV_COL_BCOLOR,
-                "strikethrough", PREV_COL_STRIKE,
-                "text", PREV_N_FIXED_COLS + i, nullptr);
-        gtk_tree_view_append_column (treeview, col);
-        /* Enable resizing of the columns. */
-        gtk_tree_view_column_set_resizable (col, true);
-        gtk_tree_view_column_set_clickable (col, true);
-
-        /* Add a combobox to select column types as column header. Each uses the same
-         * common model for the dropdown list. The selected value is taken
-         * from the column_types vector. */
-        auto cbox = cbox_factory (GTK_TREE_MODEL(combostore), tx_imp->column_types()[i]);
-        g_object_set_data (G_OBJECT(cbox), "col-num", GUINT_TO_POINTER(i));
-        g_signal_connect (G_OBJECT(cbox), "changed",
-                         G_CALLBACK(csv_tximp_preview_col_type_changed_cb), (gpointer)this);
-        gtk_tree_view_column_set_widget (col, cbox);
+        if (ntcols == 0)
+            renderer = gtk_cell_renderer_pixbuf_new(); // Error column uses an icon
+        auto col = gtk_tree_view_column_new ();
+        gtk_tree_view_column_pack_start (col, renderer, false);
+        ntcols = gtk_tree_view_append_column (treeview, col);
     }
 
-    /* Add a column for the error messages */
-    auto renderer = gtk_cell_renderer_pixbuf_new();
-    g_object_set (G_OBJECT(renderer), "stock-size", GTK_ICON_SIZE_MENU, nullptr);
-    auto col = gtk_tree_view_column_new_with_attributes (nullptr, renderer,
-            "stock-id", PREV_COL_ERR_ICON, nullptr);
-    g_object_set (G_OBJECT(col), "sizing", GTK_TREE_VIEW_COLUMN_FIXED,
-            "fixed-width", 20, nullptr);
-    gtk_tree_view_insert_column (treeview, col, 0);
-    gtk_tree_view_column_set_resizable (col, true);
+    /* Reset column attributes as they are undefined after recreating the model */
+    auto combostore = make_column_header_model (tx_imp->multi_split());
+    for (uint i = 0; i < ntcols; i++)
+        preview_style_column (i, combostore);
 
     /* Release our reference for the stores to allow proper memory management. */
     g_object_unref (store);

commit 449db629213ce38a6a034c7221ec795ac48ac512
Author: Geert Janssens <geert at kobaltwit.be>
Date:   Thu Feb 9 19:35:23 2017 +0100

    Fix settings loading and saving
    
    - fixed width columns widths weren't saved
    - column types were lost when loading a fixed width preset

diff --git a/src/import-export/csv-imp/gnc-fw-tokenizer.hpp b/src/import-export/csv-imp/gnc-fw-tokenizer.hpp
index 0222180..975b2ac 100644
--- a/src/import-export/csv-imp/gnc-fw-tokenizer.hpp
+++ b/src/import-export/csv-imp/gnc-fw-tokenizer.hpp
@@ -77,7 +77,7 @@ public:
 
 private:
     std::vector<uint> m_col_vec;
-    uint m_longest_line;
+    uint m_longest_line = 0;
 };
 
 #endif
diff --git a/src/import-export/csv-imp/gnc-tx-import.cpp b/src/import-export/csv-imp/gnc-tx-import.cpp
index 04acc07..01fe06a 100644
--- a/src/import-export/csv-imp/gnc-tx-import.cpp
+++ b/src/import-export/csv-imp/gnc-tx-import.cpp
@@ -286,12 +286,21 @@ std::string GncTxImport::separators () { return m_settings.m_separators; }
 
 void GncTxImport::settings (const CsvTransSettings& settings)
 {
+    /* First apply file format as this may recreate the tokenizer */
+    file_format (settings.m_file_format);
+    /* Only then apply the other settings */
     m_settings = settings;
-    file_format (m_settings.m_file_format);
     multi_split (m_settings.m_multi_split);
     base_account (m_settings.m_base_account);
     encoding (m_settings.m_encoding);
-    separators (m_settings.m_separators);
+
+    if (file_format() == GncImpFileFormat::CSV)
+        separators (m_settings.m_separators);
+    else if (file_format() == GncImpFileFormat::FIXED_WIDTH)
+    {
+        auto fwtok = dynamic_cast<GncFwTokenizer*>(m_tokenizer.get());
+        fwtok->columns (m_settings.m_column_widths);
+    }
     try
     {
         tokenize(false);
@@ -299,6 +308,13 @@ void GncTxImport::settings (const CsvTransSettings& settings)
     catch (...)
     { };
 
+    /* Tokenizing will clear column types, reset them here
+     * based on the loaded settings.
+     */
+    std::copy_n (settings.m_column_types.begin(),
+            std::min (m_settings.m_column_types.size(), settings.m_column_types.size()),
+            m_settings.m_column_types.begin());
+
 }
 
 bool GncTxImport::save_settings ()
@@ -306,6 +322,17 @@ bool GncTxImport::save_settings ()
 
     if (trans_preset_is_reserved_name (m_settings.m_name))
         return true;
+
+    /* separators are already copied to m_settings in the separators
+     * function above. However this is not the case for the column
+     * widths in fw mode, so do this now.
+     */
+    if (file_format() == GncImpFileFormat::FIXED_WIDTH)
+    {
+        auto fwtok = dynamic_cast<GncFwTokenizer*>(m_tokenizer.get());
+        m_settings.m_column_widths = fwtok->get_columns();
+    }
+
     return m_settings.save();
 }
 

commit 5953543573515d1d9a9795b405bf76f737f67d5a
Author: Geert Janssens <geert at kobaltwit.be>
Date:   Wed Feb 8 17:11:32 2017 +0100

    Improve visual feedback when previewing importable data

diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index 5e50f1a..ab9272f 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -104,7 +104,6 @@ public:
 
     void preview_populate_settings_combo();
     void preview_handle_save_del_sensitivity (GtkComboBox* combo);
-    void preview_row_sel_update ();
     void preview_split_column (int col, int offset);
     void preview_refresh_table ();
     void preview_refresh ();
@@ -122,6 +121,9 @@ private:
     /* member functions to manage the context menu for fixed with columns */
     uint get_new_col_rel_pos (GtkTreeViewColumn *tcol, int dx);
     void fixed_context_menu (GdkEventButton *event, int col, int dx);
+    /* member function to calculate row colors for the preview table (to visualize status) */
+    void preview_row_fill_state_cells (GtkListStore *store, GtkTreeIter *iter,
+            std::string& err_msg, bool skip);
 
     GtkAssistant    *csv_imp_asst;
 
@@ -1053,7 +1055,13 @@ enum PreviewHeaderComboCols { COL_TYPE_NAME, COL_TYPE_ID };
 /* Internally used enum to access the first two (fixed) columns
  * in the model used to display the prased data.
  */
-enum PreviewDataTableCols { PREV_COL_COLOR, PREV_COL_ERROR, PREV_N_FIXED_COLS };
+enum PreviewDataTableCols {
+    PREV_COL_FCOLOR,
+    PREV_COL_BCOLOR,
+    PREV_COL_STRIKE,
+    PREV_COL_ERROR,
+    PREV_COL_ERR_ICON,
+    PREV_N_FIXED_COLS };
 
 /** Event handler for the user selecting a new column type. When the
  * user selects a new column type, that column's text must be changed
@@ -1304,28 +1312,29 @@ CsvImpTransAssist::preview_update_fw_columns (GtkTreeView* treeview, GdkEventBut
 }
 
 
-/* visualize skipped lines
- */
-void CsvImpTransAssist::preview_row_sel_update ()
-{
-    GtkTreeIter iter;
-    auto store = GTK_LIST_STORE(gtk_tree_view_get_model (treeview));
-
-    /* Colorize rows that will be skipped */
-    int i = 0;
-    for (auto parsed_line : tx_imp->m_parsed_lines)
+/* Convert state info (errors/skipped) in visual feedback to decorate the preview table */
+void
+CsvImpTransAssist::preview_row_fill_state_cells (GtkListStore *store, GtkTreeIter *iter,
+        std::string& err_msg, bool skip)
+{
+    /* Extract error status for all non-skipped lines */
+    const char *c_err_msg = nullptr;
+    const char *icon_name = nullptr;
+    const char *fcolor = nullptr;
+    const char *bcolor = nullptr;
+    if (!skip && !err_msg.empty())
     {
-        const char *color = nullptr;
-        if ((std::get<4>(parsed_line)))
-            color = "pink";
-        else
-            color = nullptr;                                          // all other rows
-
-        bool valid = gtk_tree_model_iter_nth_child (GTK_TREE_MODEL(store), &iter, nullptr, i);
-        if (valid)
-            gtk_list_store_set (store, &iter, PREV_COL_COLOR, color, -1);
-        i++;
+        fcolor = "black";
+        bcolor = "pink";
+        c_err_msg = err_msg.c_str();
+        icon_name = GTK_STOCK_DIALOG_ERROR;
     }
+    gtk_list_store_set (store, iter,
+            PREV_COL_FCOLOR, fcolor,
+            PREV_COL_BCOLOR, bcolor,
+            PREV_COL_STRIKE, skip,
+            PREV_COL_ERROR, c_err_msg,
+            PREV_COL_ERR_ICON, icon_name, -1);
 }
 
 /* Helper function that creates a combo_box using a model
@@ -1369,42 +1378,39 @@ void CsvImpTransAssist::preview_refresh_table ()
 {
     preview_validate_settings ();
 
-    /* ncols is the number of columns in the file data. */
+    /* Create a new liststore to hold status and data from the file being imported.
+       The first columns hold status information (row-color, row-errors, row-error-icon,...
+       All following columns represent the tokenized data as strings. */
     auto ncols = PREV_N_FIXED_COLS + tx_imp->column_types().size();
-
-    // Set up file data liststore
-
-    /* store is a liststore to hold the data from the file being imported.
-       it contains only strings. */
     auto model_col_types = g_new (GType, ncols);
-    for (guint i = 0; i <  ncols; i++)
+    model_col_types[PREV_COL_FCOLOR] = G_TYPE_STRING;
+    model_col_types[PREV_COL_BCOLOR] = G_TYPE_STRING;
+    model_col_types[PREV_COL_ERROR] = G_TYPE_STRING;
+    model_col_types[PREV_COL_ERR_ICON] = G_TYPE_STRING;
+    model_col_types[PREV_COL_STRIKE] = G_TYPE_BOOLEAN;
+    for (guint i = PREV_N_FIXED_COLS; i <  ncols; i++)
         model_col_types[i] = G_TYPE_STRING;
     auto store = gtk_list_store_newv (ncols, model_col_types);
     g_free (model_col_types);
 
-    /* Fill the data liststore with data from the file. */
+    /* Fill the data liststore with data from importer object. */
     for (auto parse_line : tx_imp->m_parsed_lines)
     {
+        /* Fill the state cells */
         GtkTreeIter iter;
         gtk_list_store_append (store, &iter);
+        preview_row_fill_state_cells (store, &iter,
+                std::get<1>(parse_line), std::get<4>(parse_line));
 
-        /* Row Color column */
-        gtk_list_store_set (store, &iter, PREV_COL_COLOR, nullptr, -1);
-
-        /* Add the optional error messages to the store */
-        auto err_msg = std::string();
-        if (!std::get<4>(parse_line))
-            err_msg = std::get<1>(parse_line);
-        gtk_list_store_set (store, &iter, PREV_COL_ERROR, err_msg.c_str(), -1);
-
+        /* Fill the data cells. */
         for (auto cell_str_it = std::get<0>(parse_line).cbegin(); cell_str_it != std::get<0>(parse_line).cend(); cell_str_it++)
         {
-            /* Set the value of the proper column in the list store. */
             uint pos = PREV_N_FIXED_COLS + cell_str_it - std::get<0>(parse_line).cbegin();
             gtk_list_store_set (store, &iter, pos, cell_str_it->c_str(), -1);
         }
     }
     gtk_tree_view_set_model (treeview, GTK_TREE_MODEL(store));
+    gtk_tree_view_set_tooltip_column (treeview, PREV_COL_ERROR);
 
     // Set up the file data treeview using the created model above
     auto column = gtk_tree_view_get_column (treeview, 0);
@@ -1441,7 +1447,10 @@ void CsvImpTransAssist::preview_refresh_table ()
         g_object_set (G_OBJECT(renderer), "family", "monospace", nullptr);
 
         /* Add a single column for the treeview. */
-        auto col = gtk_tree_view_column_new_with_attributes ("", renderer, "background", 0,
+        auto col = gtk_tree_view_column_new_with_attributes ("", renderer,
+                "foreground", PREV_COL_FCOLOR,
+                "background", PREV_COL_BCOLOR,
+                "strikethrough", PREV_COL_STRIKE,
                 "text", PREV_N_FIXED_COLS + i, nullptr);
         gtk_tree_view_append_column (treeview, col);
         /* Enable resizing of the columns. */
@@ -1459,9 +1468,12 @@ void CsvImpTransAssist::preview_refresh_table ()
     }
 
     /* Add a column for the error messages */
-    auto renderer = gtk_cell_renderer_text_new();
-    auto col = gtk_tree_view_column_new_with_attributes (_("Errors"), renderer,
-            "background", PREV_COL_COLOR, "text", PREV_COL_ERROR, nullptr);
+    auto renderer = gtk_cell_renderer_pixbuf_new();
+    g_object_set (G_OBJECT(renderer), "stock-size", GTK_ICON_SIZE_MENU, nullptr);
+    auto col = gtk_tree_view_column_new_with_attributes (nullptr, renderer,
+            "stock-id", PREV_COL_ERR_ICON, nullptr);
+    g_object_set (G_OBJECT(col), "sizing", GTK_TREE_VIEW_COLUMN_FIXED,
+            "fixed-width", 20, nullptr);
     gtk_tree_view_insert_column (treeview, col, 0);
     gtk_tree_view_column_set_resizable (col, true);
 
@@ -1471,10 +1483,6 @@ void CsvImpTransAssist::preview_refresh_table ()
 
     /* Make the things actually appear. */
     gtk_widget_show_all (GTK_WIDGET(treeview));
-
-    /* Update the row selection highlight */
-    preview_row_sel_update ();
-
 }
 
 /* Update the preview page based on the current state of the importer.

commit 9ecde3a05d457a718067a696aa58ca4fd9c5cd00
Author: Geert Janssens <geert at kobaltwit.be>
Date:   Mon Feb 6 11:05:39 2017 +0100

    Improve preview table handling
    
    - Properly enable/disable context menu for managing columns in fixed width data mode
    - Make this context menu work on the table body instead of the table headers
    - Both of the above is done a well for the double-click column creation behaviour
    - Migrate the column type setting widgets to the table header cells and remove
      the second (one-row) table, which was only used to allow type selection.
    - Use enums instead of magic numbers to access associated treeview models
    
    To fix: for some reason the column headers are no responding to clicks when
    the preview page is first opened. However the first time the preview table
    is regenerated (pretty much whenever an option is changed) they are. This
    should still be debugged.

diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index 965a95c..5e50f1a 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -99,15 +99,13 @@ public:
     void preview_update_encoding (const char* encoding);
     void preview_update_date_format ();
     void preview_update_currency_format ();
-    void preview_update_col_type (GtkCellRenderer* renderer,
-            GtkTreeIter* new_text_iter);
-    void preview_update_fw_columns (GtkWidget* button, GdkEventButton* event);
+    void preview_update_col_type (GtkComboBox* cbox);
+    bool preview_update_fw_columns (GtkTreeView* treeview, GdkEventButton* event);
 
     void preview_populate_settings_combo();
     void preview_handle_save_del_sensitivity (GtkComboBox* combo);
-    void preview_resize_treeview();
     void preview_row_sel_update ();
-    void preview_split_column (int col, int dx);
+    void preview_split_column (int col, int offset);
     void preview_refresh_table ();
     void preview_refresh ();
     void preview_validate_settings ();
@@ -122,7 +120,7 @@ public:
             gpointer userdata);
 private:
     /* member functions to manage the context menu for fixed with columns */
-    uint get_new_col_rel_pos (int col, int dx);
+    uint get_new_col_rel_pos (GtkTreeViewColumn *tcol, int dx);
     void fixed_context_menu (GdkEventButton *event, int col, int dx);
 
     GtkAssistant    *csv_imp_asst;
@@ -151,13 +149,12 @@ private:
     GtkComboBoxText *date_format_combo;             /**< The Combo Text widget for selecting the date format */
     GtkComboBoxText *currency_format_combo;         /**< The Combo Text widget for selecting the currency format */
     GtkTreeView     *treeview;                      /**< The treeview containing the data */
-    GtkTreeView     *ctreeview;                     /**< The treeview containing the column types */
     GtkLabel        *instructions_label;            /**< The instructions label */
     GtkImage        *instructions_image;            /**< The instructions image */
     bool             encoding_selected_called;      /**< Before encoding_selected is first called, this is false.
                                                        * error lines, instead of all the file data. */
-    int              fixed_context_col;             /**< The number of the column whose the user has clicked */
-    int              fixed_context_dx;              /**< The horizontal coordinate of the pixel in the header of the column
+    int              fixed_context_col;             /**< The number of the column the user has clicked */
+    int              fixed_context_offset;          /**< The offset (in characters) in the column
                                                        * the user has clicked */
 
     GtkWidget            *account_match_page;       /**< Assistant account matcher page widget */
@@ -350,22 +347,16 @@ static void csv_tximp_preview_currency_fmt_sel_cb (GtkComboBox* format_selector,
     info->preview_update_currency_format();
 }
 
-void csv_tximp_preview_treeview_resized_cb (GtkWidget* widget, GtkAllocation* allocation, CsvImpTransAssist* info)
+void csv_tximp_preview_col_type_changed_cb (GtkComboBox* cbox, CsvImpTransAssist* info)
 {
-    info->preview_resize_treeview();
+    info->preview_update_col_type (cbox);
 }
 
-void csv_tximp_preview_col_type_changed_cb (GtkCellRenderer* renderer, gchar* path,
-                                GtkTreeIter* new_text_iter, CsvImpTransAssist* info)
-{
-    info->preview_update_col_type (renderer, new_text_iter);
-}
-
-void
-csv_tximp_preview_header_clicked_cb (GtkWidget* button, GdkEventButton* event,
+gboolean
+csv_tximp_preview_treeview_clicked_cb (GtkTreeView* treeview, GdkEventButton* event,
                                         CsvImpTransAssist* info)
 {
-    info->preview_update_fw_columns(button, event);
+    return info->preview_update_fw_columns(treeview, event);
 }
 
 
@@ -554,9 +545,7 @@ CsvImpTransAssist::CsvImpTransAssist ()
 
         /* Load the data treeview and connect it to its resizing event handler. */
         treeview = (GtkTreeView*)GTK_WIDGET(gtk_builder_get_object (builder, "treeview"));
-
-        /* Load the column type treeview. */
-        ctreeview = (GtkTreeView*)GTK_WIDGET(gtk_builder_get_object (builder, "ctreeview"));
+        gtk_tree_view_set_headers_clickable (treeview, true);
 
         /* This is true only after encoding_selected is called, so we must
          * set it initially to false. */
@@ -961,9 +950,19 @@ void CsvImpTransAssist::preview_update_file_format ()
     try
     {
         if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(csv_button)))
+        {
             tx_imp->file_format (GncImpFileFormat::CSV);
+            g_signal_handlers_disconnect_by_func(G_OBJECT(treeview),
+                    (gpointer)csv_tximp_preview_treeview_clicked_cb, (gpointer)this);
+        }
         else
+        {
             tx_imp->file_format (GncImpFileFormat::FIXED_WIDTH);
+            /* Enable context menu for adding/removing columns. */
+            g_signal_connect (G_OBJECT(treeview), "button_press_event",
+                    G_CALLBACK(csv_tximp_preview_treeview_clicked_cb), (gpointer)this);
+
+        }
 
         tx_imp->tokenize (false);
         preview_refresh_table ();
@@ -1040,33 +1039,21 @@ CsvImpTransAssist::preview_update_currency_format ()
     preview_refresh_table ();
 }
 
-
-/** Event handler for the data treeview being resized. When the data
- * treeview is resized, the column types treeview's columns are also resized to
- * match.
- */
-void
-CsvImpTransAssist::preview_resize_treeview ()
+gboolean
+csv_imp_preview_queue_rebuild_table (CsvImpTransAssist *assist)
 {
-    /* Go through each column except for the last. (We don't want to set
-     * the width of the last column because the user won't be able to
-     * shrink the dialog back if it's expanded.) */
-    for (uint i = 0; i < tx_imp->column_types().size() - 1; i++)
-    {
-        /* Get the width. */
-        auto col_width = gtk_tree_view_column_get_width (gtk_tree_view_get_column (treeview, i));
-        /* Set the minimum width for a column so that drop down selector can be seen. */
-        if (col_width < MIN_COL_WIDTH)
-            col_width = MIN_COL_WIDTH;
-        auto pcol = gtk_tree_view_get_column (treeview, i);
-        gtk_tree_view_column_set_min_width (pcol, col_width);
-        /* Set ccol's width the same. */
-        auto ccol = gtk_tree_view_get_column (ctreeview, i);
-        gtk_tree_view_column_set_min_width (ccol, col_width);
-        gtk_tree_view_column_set_max_width (ccol, col_width);
-    }
+    assist->preview_refresh_table ();
+    return false;
 }
 
+/* Internally used enum to access the columns in the comboboxes
+ * the user can click to set a type for each column of the data
+ */
+enum PreviewHeaderComboCols { COL_TYPE_NAME, COL_TYPE_ID };
+/* Internally used enum to access the first two (fixed) columns
+ * in the model used to display the prased data.
+ */
+enum PreviewDataTableCols { PREV_COL_COLOR, PREV_COL_ERROR, PREV_N_FIXED_COLS };
 
 /** Event handler for the user selecting a new column type. When the
  * user selects a new column type, that column's text must be changed
@@ -1077,20 +1064,23 @@ CsvImpTransAssist::preview_resize_treeview ()
  * @param new_text The text the user selected
  * @param info The display of the data being imported
  */
-void CsvImpTransAssist::preview_update_col_type (GtkCellRenderer* renderer,
-                                GtkTreeIter* new_text_iter)
+void CsvImpTransAssist::preview_update_col_type (GtkComboBox* cbox)
 {
     /* Get the new text */
-    auto col_num = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT(renderer), "col-num"));
-    GtkTreeModel* model;
-    g_object_get (renderer, "model", &model, nullptr);
+    GtkTreeIter iter;
+    auto model = gtk_combo_box_get_model (cbox);
+    gtk_combo_box_get_active_iter (cbox, &iter);
     auto new_col_type = GncTransPropType::NONE;
-    gtk_tree_model_get (model, new_text_iter,
-            1, &new_col_type, // Invisible column in the combobox' model containing the column type
-            -1);
+    gtk_tree_model_get (model, &iter, COL_TYPE_ID, &new_col_type, -1);
 
+    auto col_num = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT(cbox), "col-num"));
     tx_imp->set_column_type (col_num, new_col_type);
-    preview_refresh_table ();
+
+    /* Delay rebuilding our data table to avoid critical warnings due to
+     * pending events still acting on them after this event is processed.
+     */
+    g_idle_add ((GSourceFunc)csv_imp_preview_queue_rebuild_table, this);
+
 }
 
 /*======================================================================*/
@@ -1156,9 +1146,9 @@ static GnumericPopupMenuElement const popup_elements[] =
     { nullptr, nullptr, 0, 0, 0 },
 };
 
-uint CsvImpTransAssist::get_new_col_rel_pos (int col, int dx)
+uint CsvImpTransAssist::get_new_col_rel_pos (GtkTreeViewColumn *tcol, int dx)
 {
-    auto renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT(gtk_tree_view_get_column (treeview, col)));
+    auto renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT(tcol));
     auto cell = GTK_CELL_RENDERER(renderers->data);
     g_list_free (renderers);
     PangoFontDescription *font_desc;
@@ -1181,26 +1171,24 @@ fixed_context_menu_handler (GnumericPopupMenuElement const *element,
         gpointer userdata)
 {
     auto info = (CsvImpTransAssist*)userdata;
-    auto col = info->fixed_context_col;
-    auto rel_pos = info->get_new_col_rel_pos (col, info->fixed_context_dx);
     auto fwtok = dynamic_cast<GncFwTokenizer*>(info->tx_imp->m_tokenizer.get());
 
     switch (element->index)
     {
     case CONTEXT_STF_IMPORT_MERGE_LEFT:
-        fwtok->col_delete (col - 1);
+        fwtok->col_delete (info->fixed_context_col - 1);
         break;
     case CONTEXT_STF_IMPORT_MERGE_RIGHT:
-        fwtok->col_delete (col);
+        fwtok->col_delete (info->fixed_context_col);
         break;
     case CONTEXT_STF_IMPORT_SPLIT:
-        fwtok->col_split (col, rel_pos);
+        fwtok->col_split (info->fixed_context_col, info->fixed_context_offset);
         break;
     case CONTEXT_STF_IMPORT_WIDEN:
-        fwtok->col_widen (col);
+        fwtok->col_widen (info->fixed_context_col);
         break;
     case CONTEXT_STF_IMPORT_NARROW:
-        fwtok->col_narrow (col);
+        fwtok->col_narrow (info->fixed_context_col);
         break;
     default:
         ; /* Nothing */
@@ -1221,31 +1209,24 @@ fixed_context_menu_handler (GnumericPopupMenuElement const *element,
 
 void
 CsvImpTransAssist::fixed_context_menu (GdkEventButton *event,
-                    int col, int dx)
+                    int col, int offset)
 {
     auto fwtok = dynamic_cast<GncFwTokenizer*>(tx_imp->m_tokenizer.get());
     fixed_context_col = col;
-    fixed_context_dx = dx;
-    uint rel_pos = get_new_col_rel_pos (col, dx);
+    fixed_context_offset = offset;
 
     int sensitivity_filter = 0;
     if (!fwtok->col_can_delete (col - 1))
         sensitivity_filter |= (1 << CONTEXT_STF_IMPORT_MERGE_LEFT);
     if (!fwtok->col_can_delete (col))
         sensitivity_filter |= (1 << CONTEXT_STF_IMPORT_MERGE_RIGHT);
-    if (!fwtok->col_can_split (col, rel_pos))
+    if (!fwtok->col_can_split (col, offset))
         sensitivity_filter |= (1 << CONTEXT_STF_IMPORT_SPLIT);
     if (!fwtok->col_can_widen (col))
         sensitivity_filter |= (1 << CONTEXT_STF_IMPORT_WIDEN);
     if (!fwtok->col_can_narrow (col))
         sensitivity_filter |= (1 << CONTEXT_STF_IMPORT_NARROW);
 
-    if (col >= 0)
-    {
-        auto column = gtk_tree_view_get_column (treeview, col);
-        gtk_widget_grab_focus (gtk_tree_view_column_get_widget(column));
-    }
-
     gnumeric_create_popup_menu (popup_elements, &fixed_context_menu_handler,
                                 this, 0,
                                 sensitivity_filter, event);
@@ -1253,13 +1234,11 @@ CsvImpTransAssist::fixed_context_menu (GdkEventButton *event,
 
 /*===================== End of Gnumeric Code ===========================*/
 /*======================================================================*/
-
 void
-CsvImpTransAssist::preview_split_column (int col, int dx)
+CsvImpTransAssist::preview_split_column (int col, int offset)
 {
-    auto rel_pos = get_new_col_rel_pos (col, dx);
     auto fwtok = dynamic_cast<GncFwTokenizer*>(tx_imp->m_tokenizer.get());
-    fwtok->col_split (col, rel_pos);
+    fwtok->col_split (col, offset);
     try
     {
         tx_imp->tokenize (false);
@@ -1269,7 +1248,7 @@ CsvImpTransAssist::preview_split_column (int col, int dx)
         gnc_error_dialog (nullptr, "%s", e.what());
         return;
     }
-    preview_refresh_table ();
+    preview_refresh_table();
 }
 
 
@@ -1279,27 +1258,49 @@ CsvImpTransAssist::preview_split_column (int col, int dx)
  * @param button The button at the top of a column of the treeview
  * @param event The event that happened (where the user clicked)
  * @param info The data being configured
+ * @returns true if further processing of this even should stop, false
+ *               if other event handlers can have a go at this as well
  */
-void
-CsvImpTransAssist::preview_update_fw_columns (GtkWidget* button, GdkEventButton* event)
+bool
+CsvImpTransAssist::preview_update_fw_columns (GtkTreeView* treeview, GdkEventButton* event)
 {
+    /* Nothing to do if this was not triggered on our treeview body */
+    if (event->window != gtk_tree_view_get_bin_window (treeview))
+        return false;
+
     /* Find the column that was clicked. */
-    auto col = GPOINTER_TO_UINT(g_object_get_data (G_OBJECT(button), "col-num"));
+    GtkTreeViewColumn *tcol = nullptr;
+    int cell_x = 0;
+    auto success = gtk_tree_view_get_path_at_pos (treeview,
+            (int)event->x, (int)event->y,
+            nullptr, &tcol, &cell_x, nullptr);
+    if (!success)
+        return false;
 
-    /* Don't let the user affect the last column if it has error messages. */
-    if (col == tx_imp->column_types().size())
-        return;
+    /* Stop if no column found in this treeview (-1) or
+     * if column is the error messages column (0) */
+    auto tcol_list = gtk_tree_view_get_columns(treeview);
+    auto tcol_num = g_list_index (tcol_list, tcol);
+    g_list_free (tcol_list);
+    if (tcol_num <= 0)
+        return false;
 
-    /*  calculate offset to compensate for the button indentation. */
-    GtkAllocation alloc;
-    gtk_widget_get_allocation (gtk_bin_get_child (GTK_BIN(button)), &alloc);
-    auto offset = alloc.x - alloc.x;
+    /* Data columns in the treeview are offset by one
+     * because the first column is the error column
+     */
+    auto dcol = tcol_num - 1;
+    auto offset = get_new_col_rel_pos (tcol, cell_x);
     if (event->type == GDK_2BUTTON_PRESS && event->button == 1)
+    {
         /* Double clicks can split columns. */
-        preview_split_column (col, (int)event->x - offset);
+        preview_split_column (dcol, offset);
+        return true; // Stop processing here !
+    }
     else if (event->type == GDK_BUTTON_PRESS && event->button == 3)
         /* Right clicking brings up a context menu. */
-        fixed_context_menu (event, col, (int)event->x - offset);
+        fixed_context_menu (event, dcol, offset);
+
+    return false;
 }
 
 
@@ -1322,11 +1323,43 @@ void CsvImpTransAssist::preview_row_sel_update ()
 
         bool valid = gtk_tree_model_iter_nth_child (GTK_TREE_MODEL(store), &iter, nullptr, i);
         if (valid)
-            gtk_list_store_set (store, &iter, 0, color, -1);
+            gtk_list_store_set (store, &iter, PREV_COL_COLOR, color, -1);
         i++;
     }
 }
 
+/* Helper function that creates a combo_box using a model
+ * with valid column types and selects the given column type
+ */
+static GtkWidget*
+cbox_factory (GtkTreeModel* model, GncTransPropType col_type)
+{
+    GtkTreeIter iter;
+    auto cbox = gtk_combo_box_new_with_model(model);
+
+    /* Set up a renderer for this combobox. */
+    auto renderer = gtk_cell_renderer_text_new();
+    gtk_cell_layout_pack_start (GTK_CELL_LAYOUT(cbox),
+            renderer, true);
+    gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT(cbox),
+            renderer, "text", COL_TYPE_NAME);
+
+    auto valid = gtk_tree_model_get_iter_first (model, &iter);
+    while (valid)
+    {
+        gint stored_col_type;
+        gtk_tree_model_get (model, &iter,
+                COL_TYPE_ID, &stored_col_type, -1);
+        if (stored_col_type == static_cast<int>(col_type))
+            break;
+        valid = gtk_tree_model_iter_next(model, &iter);
+    }
+    if (valid)
+        gtk_combo_box_set_active_iter (GTK_COMBO_BOX(cbox), &iter);
+
+    gtk_widget_show (cbox);
+    return cbox;
+}
 
 /* Loads the preview's data into its data treeview.
  *
@@ -1337,54 +1370,17 @@ void CsvImpTransAssist::preview_refresh_table ()
     preview_validate_settings ();
 
     /* ncols is the number of columns in the file data. */
-    auto column_types = tx_imp->column_types();
-    auto ncols = column_types.size();
-
-    // Set up the header liststore
-
-    /* ctstore will be the liststore for the header row, which displays the user's
-     * column type selection
-     * its related treeview is info->ctreeview
-     * ctstore is arranged so that every two
-     * columns form a pair of
-     * - the column type as a user visible (translated) string
-     * - the internal type for this column
-     * So store looks like:
-     * col_type_str 0, col_type, col_type_str 1, col_type 1, ..., col_type_str ncols, col_type ncols.
-     * And then a final column is added for the Error column (which doesn't require a col_type) */
-    auto headertypes = g_new (GType, 2 * ncols + 1);
-    for (guint i = 0; i < 2 * ncols; i += 2)
-    {
-        headertypes[i] = G_TYPE_STRING;
-        headertypes[i+1] = G_TYPE_INT;
-    }
-    headertypes[2 * ncols] = G_TYPE_STRING;
-    auto ctstore = gtk_list_store_newv (2 * ncols + 1, headertypes);
-    g_free (headertypes);
-
-    /* Set all the column types to what's in the parse data. */
-    GtkTreeIter iter;
-    gtk_list_store_append (ctstore, &iter);
-    for (guint i = 0; i < ncols; i++)
-    {
-        gtk_list_store_set (ctstore, &iter,
-                2 * i, _(gnc_csv_col_type_strs[column_types[i]]),
-                2 * i + 1, static_cast<int>(column_types[i]),
-                -1);
-    }
-    gtk_list_store_set (ctstore, &iter, 2 * ncols, _("Errors"), -1);
-    gtk_tree_view_set_model (ctreeview, GTK_TREE_MODEL(ctstore));
-
+    auto ncols = PREV_N_FIXED_COLS + tx_imp->column_types().size();
 
     // Set up file data liststore
 
     /* store is a liststore to hold the data from the file being imported.
        it contains only strings. */
-    auto bodytypes = g_new (GType, ncols + 2);
-    for (guint i = 0; i <  ncols + 2; i++)
-        bodytypes[i] = G_TYPE_STRING;
-    auto store = gtk_list_store_newv (ncols + 2, bodytypes);
-    g_free (bodytypes);
+    auto model_col_types = g_new (GType, ncols);
+    for (guint i = 0; i <  ncols; i++)
+        model_col_types[i] = G_TYPE_STRING;
+    auto store = gtk_list_store_newv (ncols, model_col_types);
+    g_free (model_col_types);
 
     /* Fill the data liststore with data from the file. */
     for (auto parse_line : tx_imp->m_parsed_lines)
@@ -1393,32 +1389,25 @@ void CsvImpTransAssist::preview_refresh_table ()
         gtk_list_store_append (store, &iter);
 
         /* Row Color column */
-        gtk_list_store_set (store, &iter, 0, nullptr, -1);
+        gtk_list_store_set (store, &iter, PREV_COL_COLOR, nullptr, -1);
+
+        /* Add the optional error messages to the store */
+        auto err_msg = std::string();
+        if (!std::get<4>(parse_line))
+            err_msg = std::get<1>(parse_line);
+        gtk_list_store_set (store, &iter, PREV_COL_ERROR, err_msg.c_str(), -1);
 
         for (auto cell_str_it = std::get<0>(parse_line).cbegin(); cell_str_it != std::get<0>(parse_line).cend(); cell_str_it++)
         {
             /* Set the value of the proper column in the list store. */
-            uint pos = cell_str_it - std::get<0>(parse_line).cbegin() + 1;
+            uint pos = PREV_N_FIXED_COLS + cell_str_it - std::get<0>(parse_line).cbegin();
             gtk_list_store_set (store, &iter, pos, cell_str_it->c_str(), -1);
         }
-        /* Add the optional error messages in the last column of the store */
-        auto err_msg = std::string();
-        if (!std::get<4>(parse_line))
-            err_msg = std::get<1>(parse_line);
-        gtk_list_store_set (store, &iter, ncols + 1, err_msg.c_str(), -1);
     }
     gtk_tree_view_set_model (treeview, GTK_TREE_MODEL(store));
 
-    // Set up the two header and file data treeviews using the liststores created above
-
-    /* Clear any columns from a previous invocation */
-    auto column = gtk_tree_view_get_column (ctreeview, 0);
-    while (column)
-    {
-        gtk_tree_view_remove_column (ctreeview, column);
-        column = gtk_tree_view_get_column (ctreeview, 0);
-    }
-    column = gtk_tree_view_get_column (treeview, 0);
+    // Set up the file data treeview using the created model above
+    auto column = gtk_tree_view_get_column (treeview, 0);
     while (column)
     {
         gtk_tree_view_remove_column (treeview, column);
@@ -1437,30 +1426,15 @@ void CsvImpTransAssist::preview_refresh_table ()
         {
             GtkTreeIter iter;
             gtk_list_store_append (combostore, &iter);
-            gtk_list_store_set (combostore, &iter, 0, _(col_type.second),
-                                                   1, static_cast<int>(col_type.first),
-                                                   -1);
+            gtk_list_store_set (combostore, &iter,
+                    COL_TYPE_NAME, _(col_type.second),
+                    COL_TYPE_ID, static_cast<int>(col_type.first), -1);
         }
     }
 
-    /* Insert columns into the data and column type treeviews. */
-    for (uint i = 0; i < ncols ; i++)
+    /* Insert columns into the data treeview. */
+    for (uint i = 0; i < tx_imp->column_types().size() ; i++)
     {
-        /* The header cells are combobox entries. They all use the same
-         * common model for the dropdown list while their text value
-         * comes from the header liststore (ctstore). */
-        auto crenderer = gtk_cell_renderer_combo_new();
-        /* Set the properties for the dropdown list */
-        g_object_set (G_OBJECT(crenderer), "model", combostore, "text-column", 0,
-                     "editable", TRUE, "has-entry", FALSE, nullptr);
-        g_object_set_data (G_OBJECT(crenderer),
-                           "col-num", GUINT_TO_POINTER(i));
-        g_signal_connect (G_OBJECT(crenderer), "changed",
-                         G_CALLBACK(csv_tximp_preview_col_type_changed_cb), (gpointer)this);
-        /* Insert the column */
-        gtk_tree_view_insert_column_with_attributes (ctreeview,
-                -1, "", crenderer, "text", 2 * i, nullptr);
-
         /* The file data treeview cells are simple text cells. */
         auto renderer = gtk_cell_renderer_text_new();
         /* We want a monospace font for the data in case of fixed-width data. */
@@ -1468,46 +1442,35 @@ void CsvImpTransAssist::preview_refresh_table ()
 
         /* Add a single column for the treeview. */
         auto col = gtk_tree_view_column_new_with_attributes ("", renderer, "background", 0,
-                                                             "text", i + 1, nullptr);
-        gtk_tree_view_insert_column (treeview, col, -1);
+                "text", PREV_N_FIXED_COLS + i, nullptr);
+        gtk_tree_view_append_column (treeview, col);
         /* Enable resizing of the columns. */
-        gtk_tree_view_column_set_resizable (col, TRUE);
-
-        /* We need to allow clicking on the file data's column headers for
-         * fixed-width column splitting and merging. */
-        g_object_set (G_OBJECT(col), "clickable", TRUE, nullptr);
-        auto button = gtk_tree_view_column_get_widget(col);
-        g_signal_connect (G_OBJECT(button), "button_press_event",
-                         G_CALLBACK(csv_tximp_preview_header_clicked_cb), (gpointer)this);
-        /* Store the column number in the button to know which one was clicked later on */
-        g_object_set_data (G_OBJECT(button), "col-num",GINT_TO_POINTER(i));
+        gtk_tree_view_column_set_resizable (col, true);
+        gtk_tree_view_column_set_clickable (col, true);
+
+        /* Add a combobox to select column types as column header. Each uses the same
+         * common model for the dropdown list. The selected value is taken
+         * from the column_types vector. */
+        auto cbox = cbox_factory (GTK_TREE_MODEL(combostore), tx_imp->column_types()[i]);
+        g_object_set_data (G_OBJECT(cbox), "col-num", GUINT_TO_POINTER(i));
+        g_signal_connect (G_OBJECT(cbox), "changed",
+                         G_CALLBACK(csv_tximp_preview_col_type_changed_cb), (gpointer)this);
+        gtk_tree_view_column_set_widget (col, cbox);
     }
 
     /* Add a column for the error messages */
-    auto crenderer = gtk_cell_renderer_text_new();
-    gtk_tree_view_insert_column_with_attributes (ctreeview,
-            0, "", crenderer, "text", 2 * ncols, nullptr);
-
     auto renderer = gtk_cell_renderer_text_new();
-    auto col = gtk_tree_view_column_new_with_attributes ("", renderer, "background", 0,
-                                                         "text", ncols + 1, nullptr);
+    auto col = gtk_tree_view_column_new_with_attributes (_("Errors"), renderer,
+            "background", PREV_COL_COLOR, "text", PREV_COL_ERROR, nullptr);
     gtk_tree_view_insert_column (treeview, col, 0);
-    /* Enable resizing of the columns. */
-    gtk_tree_view_column_set_resizable (col, TRUE);
-
-    /* Select the header row */
-    gtk_tree_model_get_iter_first (GTK_TREE_MODEL(ctstore), &iter);
-    auto selection = gtk_tree_view_get_selection (ctreeview);
-    gtk_tree_selection_select_iter (selection, &iter);
+    gtk_tree_view_column_set_resizable (col, true);
 
     /* Release our reference for the stores to allow proper memory management. */
     g_object_unref (store);
-    g_object_unref (ctstore);
     g_object_unref (combostore);
 
     /* Make the things actually appear. */
     gtk_widget_show_all (GTK_WIDGET(treeview));
-    gtk_widget_show_all (GTK_WIDGET(ctreeview));
 
     /* Update the row selection highlight */
     preview_row_sel_update ();
@@ -1783,9 +1746,6 @@ CsvImpTransAssist::assist_file_page_prepare ()
 void
 CsvImpTransAssist::assist_preview_page_prepare ()
 {
-    g_signal_connect (G_OBJECT(treeview), "size-allocate",
-                     G_CALLBACK(csv_tximp_preview_treeview_resized_cb), (gpointer)this);
-
     tx_imp->req_mapped_accts (false);
 
     /* Disable the Forward Assistant Button */

commit 88a482c5d36e8cdffe6514f911def90b71286ceb
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sat Jan 14 15:52:55 2017 +0100

    Use GncRational directly instead of gnc_numeric in gnc-trans-props

diff --git a/src/import-export/csv-imp/gnc-trans-props.cpp b/src/import-export/csv-imp/gnc-trans-props.cpp
index 02bd4cd..57fdf9f 100644
--- a/src/import-export/csv-imp/gnc-trans-props.cpp
+++ b/src/import-export/csv-imp/gnc-trans-props.cpp
@@ -200,13 +200,13 @@ time64 parse_date (const std::string &date_str, int format)
 }
 
 
-/** Convert str into a gnc_numeric using the user-specified (import) currency format.
+/** Convert str into a GncRational using the user-specified (import) currency format.
  * @param str The string to be parsed
  * @param currency_format The currency format to use.
- * @return a gnc_numeric
+ * @return a GncRational
  * @exception May throw std::invalid argument if string can't be parsed properly
  */
-gnc_numeric parse_amount (const std::string &str, int currency_format)
+GncRational parse_amount (const std::string &str, int currency_format)
 {
     /* If a cell is empty or just spaces return invalid amount */
     if(!boost::regex_search(str, boost::regex("[0-9]")))
@@ -237,7 +237,7 @@ gnc_numeric parse_amount (const std::string &str, int currency_format)
         break;
     }
 
-    return val;
+    return GncRational(val);
 }
 
 static char parse_reconciled (const std::string& reconcile)
@@ -632,25 +632,26 @@ std::string GncPreSplit::verify_essentials (void)
  * @param book The book where the split should be stored
  * @param amount The amount of the split
  */
-static void trans_add_split (Transaction* trans, Account* account, gnc_numeric amount,
+static void trans_add_split (Transaction* trans, Account* account, GncRational amount,
                             const boost::optional<std::string>& action,
                             const boost::optional<std::string>& memo,
                             const boost::optional<char>& rec_state,
                             const boost::optional<time64> rec_date,
-                            const boost::optional<gnc_numeric> price)
+                            const boost::optional<GncRational> price)
 {
     QofBook* book = xaccTransGetBook (trans);
     auto split = xaccMallocSplit (book);
     xaccSplitSetAccount (split, account);
     xaccSplitSetParent (split, trans);
-    xaccSplitSetAmount (split, amount);
+    xaccSplitSetAmount (split, static_cast<gnc_numeric>(amount));
     auto trans_curr = xaccTransGetCurrency(trans);
     auto acct_comm = xaccAccountGetCommodity(account);
     if (gnc_commodity_equiv(trans_curr, acct_comm))
-        xaccSplitSetValue (split, amount);
+        xaccSplitSetValue (split, static_cast<gnc_numeric>(amount));
     else if (price)
     {
-        gnc_numeric value = gnc_numeric_mul (amount, *price, GNC_DENOM_AUTO, GNC_HOW_RND_ROUND);
+        auto value = gnc_numeric_mul (static_cast<gnc_numeric>(amount),
+                static_cast<gnc_numeric>(*price), GNC_DENOM_AUTO, GNC_HOW_RND_ROUND);
         xaccSplitSetValue (split, value);
     }
     else
@@ -659,12 +660,7 @@ static void trans_add_split (Transaction* trans, Account* account, gnc_numeric a
         /* Import data didn't specify price, let's lookup the nearest in time */
         auto nprice = gnc_pricedb_lookup_nearest_in_time(gnc_pricedb_get_db(book),
                 acct_comm, trans_curr, tts);
-        if (!nprice)
-        {
-            PWARN("No price found, using a price of 1.");
-            xaccSplitSetValue (split, amount);
-        }
-        else
+        if (nprice)
         {
             /* Found a usable price. Let's check if the conversion direction is right */
             gnc_numeric rate = {0, 1};
@@ -673,9 +669,15 @@ static void trans_add_split (Transaction* trans, Account* account, gnc_numeric a
             else
                 rate = gnc_numeric_invert(gnc_price_get_value(nprice));
 
-            gnc_numeric value = gnc_numeric_mul (amount, rate, GNC_DENOM_AUTO, GNC_HOW_RND_ROUND);
+            auto value = gnc_numeric_mul (static_cast<gnc_numeric>(amount),
+                    rate, GNC_DENOM_AUTO, GNC_HOW_RND_ROUND);
             xaccSplitSetValue (split, value);
         }
+        else
+        {
+            PWARN("No price found, using a price of 1.");
+            xaccSplitSetValue (split, static_cast<gnc_numeric>(amount));
+        }
     }
 
     if (memo)
@@ -709,29 +711,23 @@ void GncPreSplit::create_split (Transaction* trans)
 
     Account *account = nullptr;
     Account *taccount = nullptr;
-    bool amount_set = false;
-    gnc_numeric deposit = { 0, 1 };
-    gnc_numeric withdrawal = { 0, 1 };
-    gnc_numeric amount = { 0, 1 };
+    auto deposit = GncRational (0, 1);
+    auto withdrawal = GncRational (0, 1);
+    auto amount = GncRational (0, 1);
 
     if (m_account)
         account = *m_account;
     if (m_taccount)
         taccount = *m_taccount;
     if (m_deposit)
-    {
         deposit = *m_deposit;
-        amount_set = true;
-    }
     if (m_withdrawal)
-    {
         withdrawal = *m_withdrawal;
-        amount_set = true;
-    }
-    if (amount_set)
-        amount = gnc_numeric_add (deposit, withdrawal,
-                xaccAccountGetCommoditySCU (account),
-                GNC_HOW_RND_ROUND_HALF_UP);
+
+    amount = GncRational(gnc_numeric_add (static_cast<gnc_numeric>(deposit),
+            static_cast<gnc_numeric>(withdrawal),
+            xaccAccountGetCommoditySCU (account),
+            GNC_HOW_RND_ROUND_HALF_UP));
 
     /* Add a split with the cumulative amount value. */
     trans_add_split (trans, account, amount, m_action, m_memo, m_rec_state, m_rec_date, m_price);
@@ -742,8 +738,8 @@ void GncPreSplit::create_split (Transaction* trans)
          * will be the negative of the the first split amount.
          */
         auto inv_price = m_price;
-        if (inv_price)
-            inv_price = gnc_numeric_invert(*inv_price);
+        if (m_price)
+            inv_price = m_price->inv();
         trans_add_split (trans, taccount, gnc_numeric_neg(amount), m_taction, m_tmemo, m_trec_state, m_trec_date, inv_price);
     }
 
diff --git a/src/import-export/csv-imp/gnc-trans-props.hpp b/src/import-export/csv-imp/gnc-trans-props.hpp
index 572e1ff..93058a1 100644
--- a/src/import-export/csv-imp/gnc-trans-props.hpp
+++ b/src/import-export/csv-imp/gnc-trans-props.hpp
@@ -41,6 +41,7 @@ extern "C" {
 #include <map>
 #include <memory>
 #include <boost/optional.hpp>
+#include <gnc-rational.hpp>
 
 /** Enumeration for column types. These are the different types of
  * columns that can exist in a CSV/Fixed-Width file. There should be
@@ -104,7 +105,7 @@ GncTransPropType sanitize_trans_prop (GncTransPropType prop, bool multi_split);
 
 time64 parse_date (const std::string &date_str, int format);
 gnc_commodity* parse_commodity (const std::string& comm_str);
-gnc_numeric parse_amount (const std::string &str, int currency_format);
+GncRational parse_amount (const std::string &str, int currency_format);
 
 struct GncPreTrans
 {
@@ -170,9 +171,9 @@ private:
     int m_currency_format;
     boost::optional<std::string> m_action;
     boost::optional<Account*> m_account;
-    boost::optional<gnc_numeric> m_deposit;
-    boost::optional<gnc_numeric> m_withdrawal;
-    boost::optional<gnc_numeric> m_price;
+    boost::optional<GncRational> m_deposit;
+    boost::optional<GncRational> m_withdrawal;
+    boost::optional<GncRational> m_price;
     boost::optional<std::string> m_memo;
     boost::optional<char> m_rec_state;
     boost::optional<time64> m_rec_date;

commit 92969b4e39220000acd70013e51f1ddfe733e6a8
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Thu Jan 12 09:41:25 2017 +0100

    Prevent the user from setting an invalid settings name
    
    The keyfile that stores the settings won't accept '[' and ']' as settingss names,
    so this commit will prevent the user from entering these characters. They are
    automatically replaced with the valid '(' and ')' characters.
    
    In addition any attempt to save settings with an invalid name via a code path
    bypassing the gui will be refused.

diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index d27ce93..965a95c 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -196,6 +196,8 @@ void csv_tximp_file_confirm_cb (GtkWidget *button, CsvImpTransAssist *info);
 void csv_tximp_preview_del_settings_cb (GtkWidget *button, CsvImpTransAssist *info);
 void csv_tximp_preview_save_settings_cb (GtkWidget *button, CsvImpTransAssist *info);
 void csv_tximp_preview_settings_sel_changed_cb (GtkComboBox *combo, CsvImpTransAssist *info);
+void csv_tximp_preview_settings_text_inserted_cb (GtkEditable *entry, gchar *new_text,
+        gint new_text_length, gint *position, CsvImpTransAssist *info);
 void csv_tximp_preview_settings_text_changed_cb (GtkEntry *entry, CsvImpTransAssist *info);
 void csv_tximp_preview_srow_cb (GtkSpinButton *spin, CsvImpTransAssist *info);
 void csv_tximp_preview_erow_cb (GtkSpinButton *spin, CsvImpTransAssist *info);
@@ -266,6 +268,27 @@ void csv_tximp_preview_settings_sel_changed_cb (GtkComboBox *combo, CsvImpTransA
 }
 
 void
+csv_tximp_preview_settings_text_inserted_cb (GtkEditable *entry, gchar *new_text,
+        gint new_text_length, gint *position, CsvImpTransAssist *info)
+{
+    if (!new_text)
+        return;
+
+    /* Prevent entering [], which are invalid characters in key files */
+    auto base_txt = std::string (new_text);
+    auto mod_txt = base_txt;
+    std::replace (mod_txt.begin(), mod_txt.end(), '[', '(');
+    std::replace (mod_txt.begin(), mod_txt.end(), ']', ')');
+    if (base_txt == mod_txt)
+        return;
+    g_signal_handlers_block_by_func (entry, (gpointer) csv_tximp_preview_settings_text_inserted_cb, info);
+    gtk_editable_insert_text (entry, mod_txt.c_str(), mod_txt.size() , position);
+    g_signal_handlers_unblock_by_func (entry, (gpointer) csv_tximp_preview_settings_text_inserted_cb, info);
+
+    g_signal_stop_emission_by_name (entry, "insert_text");
+}
+
+void
 csv_tximp_preview_settings_text_changed_cb (GtkEntry *entry, CsvImpTransAssist *info)
 {
     info->preview_settings_name(entry);
@@ -431,6 +454,8 @@ CsvImpTransAssist::CsvImpTransAssist ()
         auto emb_entry = gtk_bin_get_child (GTK_BIN (settings_combo));
         g_signal_connect (G_OBJECT(emb_entry), "changed",
                          G_CALLBACK(csv_tximp_preview_settings_text_changed_cb), this);
+        g_signal_connect (G_OBJECT(emb_entry), "insert-text",
+                         G_CALLBACK(csv_tximp_preview_settings_text_inserted_cb), this);
 
         // Add Save Settings button
         save_button = GTK_WIDGET(gtk_builder_get_object (builder, "save_settings"));
diff --git a/src/import-export/csv-imp/gnc-csv-trans-settings.cpp b/src/import-export/csv-imp/gnc-csv-trans-settings.cpp
index a8fb718..017c3a8 100644
--- a/src/import-export/csv-imp/gnc-csv-trans-settings.cpp
+++ b/src/import-export/csv-imp/gnc-csv-trans-settings.cpp
@@ -314,6 +314,12 @@ CsvTransSettings::save (void)
         return true;
     }
 
+    if ((m_name.find('[') != std::string::npos))
+    {
+        PWARN ("Name '%s' contains invalid characters '[]'. Refusing to save", m_name.c_str());
+        return true;
+    }
+
     auto keyfile = gnc_state_get_current ();
     auto group = csv_group_prefix + m_name;
 

commit e92c5ebad4919136afdbc710f77fdea079e8f3e1
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Fri Dec 30 16:52:56 2016 +0100

    Move column parsing earlier up the chain of events
    
    In this form not all data needs reparsing when a single column
    changes. The drawback is it makes the code slightly more complicated
    because accounts can only be verified after the account match page
    has completed, while most columns are set on the preview page.

diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index 7263b12..d27ce93 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -1309,12 +1309,7 @@ void CsvImpTransAssist::preview_row_sel_update ()
  */
 void CsvImpTransAssist::preview_refresh_table ()
 {
-    auto save_skip_errors = tx_imp->skip_err_lines();
-    tx_imp->update_skipped_lines (boost::none, boost::none,
-        boost::none, false);
     preview_validate_settings ();
-    tx_imp->update_skipped_lines (boost::none, boost::none,
-        boost::none, save_skip_errors);
 
     /* ncols is the number of columns in the file data. */
     auto column_types = tx_imp->column_types();
@@ -1766,6 +1761,8 @@ CsvImpTransAssist::assist_preview_page_prepare ()
     g_signal_connect (G_OBJECT(treeview), "size-allocate",
                      G_CALLBACK(csv_tximp_preview_treeview_resized_cb), (gpointer)this);
 
+    tx_imp->req_mapped_accts (false);
+
     /* Disable the Forward Assistant Button */
     gtk_assistant_set_page_complete (csv_imp_asst, preview_page, false);
 
@@ -1776,6 +1773,8 @@ CsvImpTransAssist::assist_preview_page_prepare ()
 void
 CsvImpTransAssist::assist_account_match_page_prepare ()
 {
+    tx_imp->req_mapped_accts(true);
+
     // Load the account strings into the store
     acct_match_set_accounts ();
 
@@ -1804,6 +1803,36 @@ CsvImpTransAssist::assist_doc_page_prepare ()
     /* Block going back */
     gtk_assistant_commit (csv_imp_asst);
 
+    /* At this stage in the assistant each account should be mapped so
+     * complete the split properties with this information. If this triggers
+     * an exception it indicates a logic error in the code.
+     */
+    try
+    {
+        auto col_types = tx_imp->column_types();
+        auto acct_col = std::find (col_types.begin(),
+                col_types.end(), GncTransPropType::ACCOUNT);
+        if (acct_col != col_types.end())
+            tx_imp->set_column_type (acct_col - col_types.begin(),
+                    GncTransPropType::ACCOUNT, true);
+        acct_col = std::find (col_types.begin(),
+                col_types.end(), GncTransPropType::TACCOUNT);
+        if (acct_col != col_types.end())
+            tx_imp->set_column_type (acct_col - col_types.begin(),
+                    GncTransPropType::TACCOUNT, true);
+    }
+    catch (const std::invalid_argument& err)
+    {
+        /* Oops! This shouldn't happen when using the import assistant !
+         * Inform the user and go back to the preview page.
+         */
+        gnc_error_dialog (GTK_WIDGET(csv_imp_asst),
+            _("An unexpected error has occurred while mapping accounts. Please report this as a bug.\n\n"
+              "Error message:\n%s"), err.what());
+        gtk_assistant_set_current_page (csv_imp_asst, 2);
+
+    }
+
     /* Before creating transactions, if this is a new book, let user specify
      * book options, since they affect how transactions are created */
     if (new_book)
@@ -1832,7 +1861,7 @@ CsvImpTransAssist::assist_match_page_prepare ()
          * Inform the user and go back to the preview page.
          */
         gnc_error_dialog (GTK_WIDGET(csv_imp_asst),
-            _("An unexpected error has occurred. Please report this as a bug.\n\n"
+            _("An unexpected error has occurred while creating transactions. Please report this as a bug.\n\n"
               "Error message:\n%s"), err.what());
         gtk_assistant_set_current_page (csv_imp_asst, 2);
     }
diff --git a/src/import-export/csv-imp/gnc-trans-props.cpp b/src/import-export/csv-imp/gnc-trans-props.cpp
index 85300ac..02bd4cd 100644
--- a/src/import-export/csv-imp/gnc-trans-props.cpp
+++ b/src/import-export/csv-imp/gnc-trans-props.cpp
@@ -161,13 +161,13 @@ time64 parse_date (const std::string &date_str, int format)
     boost::regex r(date_regex[format]);
     boost::smatch what;
     if(!boost::regex_search(date_str, what, r))
-        throw std::invalid_argument ("String doesn't appear to be formatted as a date.");  // regex didn't find a match
+        throw std::invalid_argument (_("Value can't be parsed into a date using the selected date format."));  // regex didn't find a match
 
     // Attention: different behavior from 2.6.x series !
     // If date format without year was selected, the match
     // should NOT have found a year.
     if ((format >= 3) && (what.length("YEAR") != 0))
-        throw std::invalid_argument ("String appears to contain a year while the selected format forbids this.");
+        throw std::invalid_argument (_("Value appears to contain a year while the selected format forbids this."));
 
     auto day = std::stoi (what.str("DAY"));
     auto month = std::stoi (what.str("MONTH"));
@@ -210,7 +210,7 @@ gnc_numeric parse_amount (const std::string &str, int currency_format)
 {
     /* If a cell is empty or just spaces return invalid amount */
     if(!boost::regex_search(str, boost::regex("[0-9]")))
-        throw std::invalid_argument ("String doesn't appear to contain a valid number.");
+        throw std::invalid_argument (_("Value doesn't appear to contain a valid number."));
 
     auto expr = boost::make_u32regex("[[:Sc:]]");
     std::string str_no_symbols = boost::u32regex_replace(str, expr, "");
@@ -223,17 +223,17 @@ gnc_numeric parse_amount (const std::string &str, int currency_format)
     case 0:
         /* Currency locale */
         if (!(xaccParseAmount (str_no_symbols.c_str(), TRUE, &val, &endptr)))
-            throw std::invalid_argument ("String can't be parsed into a number using the selected currency format.");
+            throw std::invalid_argument (_("Value can't be parsed into a number using the selected currency format."));
         break;
     case 1:
         /* Currency decimal period */
         if (!(xaccParseAmountExtended (str_no_symbols.c_str(), TRUE, '-', '.', ',', "\003\003", "$+", &val, &endptr)))
-            throw std::invalid_argument ("String can't be parsed into a number using the selected currency format.");
+            throw std::invalid_argument (_("Value can't be parsed into a number using the selected currency format."));
         break;
     case 2:
         /* Currency decimal comma */
         if (!(xaccParseAmountExtended (str_no_symbols.c_str(), TRUE, '-', ',', '.', "\003\003", "$+", &val, &endptr)))
-            throw std::invalid_argument ("String can't be parsed into a number using the selected currency format.");
+            throw std::invalid_argument (_("Value can't be parsed into a number using the selected currency format."));
         break;
     }
 
@@ -253,7 +253,7 @@ static char parse_reconciled (const std::string& reconcile)
     else if (g_strcmp0 (reconcile.c_str(), _("v")) == 0) // Voided will be handled at the transaction level
         return NREC;                                      // so return not reconciled here
     else
-        throw std::invalid_argument ("String can't be parsed into a valid reconcile state.");
+        throw std::invalid_argument (_("Value can't be parsed into a valid reconcile state."));
 }
 
 gnc_commodity* parse_commodity (const std::string& comm_str)
@@ -291,62 +291,84 @@ gnc_commodity* parse_commodity (const std::string& comm_str)
     }
 
     if (!comm)
-        throw std::invalid_argument ("String can't be parsed into a valid commodity.");
+        throw std::invalid_argument (_("Value can't be parsed into a valid commodity."));
     else
         return comm;
 }
 
 void GncPreTrans::set (GncTransPropType prop_type, const std::string& value)
 {
-    gnc_commodity *comm = nullptr;
-    switch (prop_type)
+    try
     {
-        case GncTransPropType::UNIQUE_ID:
-            m_differ = boost::none;
-            if (!value.empty())
-                m_differ = value;
-            break;
-
-        case GncTransPropType::DATE:
-            m_date = boost::none;
-            m_date = parse_date (value, m_date_format); // Throws if parsing fails
-            break;
-
-        case GncTransPropType::NUM:
-            m_num = boost::none;
-            if (!value.empty())
-                m_num = value;
-            break;
-
-        case GncTransPropType::DESCRIPTION:
-            m_desc = boost::none;
-            if (!value.empty())
-                m_desc = value;
-            break;
-
-        case GncTransPropType::NOTES:
-            m_notes = boost::none;
-            if (!value.empty())
-                m_notes = value;
-            break;
-
-        case GncTransPropType::COMMODITY:
-            m_commodity = boost::none;
-            comm = parse_commodity (value); // Throws if parsing fails
-            if (comm)
-                m_commodity = comm;
-            break;
-
-        case GncTransPropType::VOID_REASON:
-            m_void_reason = boost::none;
-            if (!value.empty())
-                m_void_reason = value;
-            break;
-
-        default:
-            /* Issue a warning for all other prop_types. */
-            PWARN ("%d is an invalid property for a transaction", static_cast<int>(prop_type));
-            break;
+        // Drop any existing error for the prop_type we're about to set
+        m_errors.erase(prop_type);
+
+        gnc_commodity *comm = nullptr;
+        switch (prop_type)
+        {
+            case GncTransPropType::UNIQUE_ID:
+                m_differ = boost::none;
+                if (!value.empty())
+                    m_differ = value;
+                break;
+
+            case GncTransPropType::DATE:
+                m_date = boost::none;
+                m_date = parse_date (value, m_date_format); // Throws if parsing fails
+                break;
+
+            case GncTransPropType::NUM:
+                m_num = boost::none;
+                if (!value.empty())
+                    m_num = value;
+                break;
+
+            case GncTransPropType::DESCRIPTION:
+                m_desc = boost::none;
+                if (!value.empty())
+                    m_desc = value;
+                break;
+
+            case GncTransPropType::NOTES:
+                m_notes = boost::none;
+                if (!value.empty())
+                    m_notes = value;
+                break;
+
+            case GncTransPropType::COMMODITY:
+                m_commodity = boost::none;
+                comm = parse_commodity (value); // Throws if parsing fails
+                if (comm)
+                    m_commodity = comm;
+                break;
+
+            case GncTransPropType::VOID_REASON:
+                m_void_reason = boost::none;
+                if (!value.empty())
+                    m_void_reason = value;
+                break;
+
+            default:
+                /* Issue a warning for all other prop_types. */
+                PWARN ("%d is an invalid property for a transaction", static_cast<int>(prop_type));
+                break;
+        }
+    }
+    catch (const std::invalid_argument& e)
+    {
+        auto err_str = std::string(_(gnc_csv_col_type_strs[prop_type])) +
+                       std::string(_(" could not be understood.\n")) +
+                       e.what();
+        m_errors.emplace(prop_type, err_str);
+        throw std::invalid_argument (err_str);
+    }
+    catch (const std::out_of_range& e)
+    {
+        auto err_str = std::string(_(gnc_csv_col_type_strs[prop_type])) +
+                       std::string(_(" could not be understood.\n")) +
+                       e.what();
+        m_errors.emplace(prop_type, err_str);
+        throw std::invalid_argument (err_str);
     }
 
 }
@@ -360,7 +382,8 @@ void GncPreTrans::reset (GncTransPropType prop_type)
     catch (...)
     {
         // Set with an empty string will effectively clear the property
-        // but also throw in many cases. For a reset this is fine, so catch it here.
+        // but can also set an error for the property. Clear that error here.
+        m_errors.erase(prop_type);
     }
 }
 
@@ -418,98 +441,151 @@ bool GncPreTrans::is_part_of (std::shared_ptr<GncPreTrans> parent)
             (!m_desc || m_desc == parent->m_desc) &&
             (!m_notes || m_notes == parent->m_notes) &&
             (!m_commodity || m_commodity == parent->m_commodity) &&
-            (!m_void_reason || m_void_reason == parent->m_void_reason);
+            (!m_void_reason || m_void_reason == parent->m_void_reason) &&
+            parent->m_errors.empty(); // A GncPreTrans with errors can never be a parent
 }
 
-void GncPreSplit::set (GncTransPropType prop_type, const std::string& value)
+/* Declare two translatable error strings here as they will be used in several places */
+const char *bad_acct = N_("Account value can't be mapped back to an account.");
+const char *bad_tacct = N_("Transfer account value can't be mapped back to an account.");
+
+static std::string gen_err_str (std::map<GncTransPropType, std::string>& errors,
+        bool check_accts_mapped = false)
 {
-    Account *acct = nullptr;
-    switch (prop_type)
+    auto full_error = std::string();
+    for (auto error : errors)
     {
-        case GncTransPropType::ACTION:
-            m_action = boost::none;
-            if (!value.empty())
-                m_action = value;
-            break;
-
-        case GncTransPropType::TACTION:
-            m_taction = boost::none;
-            if (!value.empty())
-                m_taction = value;
-            break;
-
-        case GncTransPropType::ACCOUNT:
-            m_account = boost::none;
-            acct = gnc_csv_account_map_search (value.c_str());
-            if (acct)
-                m_account = acct;
-            else
-                throw std::invalid_argument ("String can't be mapped back to an account.");
-            break;
-
-        case GncTransPropType::TACCOUNT:
-            m_taccount = boost::none;
-            acct = gnc_csv_account_map_search (value.c_str());
-            if (acct)
-                m_taccount = acct;
-            else
-                throw std::invalid_argument ("String can't be mapped back to an account.");
-            break;
-
-        case GncTransPropType::MEMO:
-            m_memo = boost::none;
-            if (!value.empty())
-                m_memo = value;
-            break;
-
-        case GncTransPropType::TMEMO:
-            m_tmemo = boost::none;
-            if (!value.empty())
-                m_tmemo = value;
-            break;
-
-        case GncTransPropType::DEPOSIT:
-            m_deposit = boost::none;
-            m_deposit = parse_amount (value, m_currency_format); // Will throw if parsing fails
-            break;
-        case GncTransPropType::WITHDRAWAL:
-            m_withdrawal = boost::none;
-            m_withdrawal = parse_amount (value, m_currency_format); // Will throw if parsing fails
-            break;
-
-        case GncTransPropType::PRICE:
-            m_price = boost::none;
-            m_price = parse_amount (value, m_currency_format); // Will throw if parsing fails
-            break;
-
-        case GncTransPropType::REC_STATE:
-            m_rec_state = boost::none;
-            m_rec_state = parse_reconciled (value); // Throws if parsing fails
-            break;
-
-        case GncTransPropType::TREC_STATE:
-            m_trec_state = boost::none;
-            m_trec_state = parse_reconciled (value); // Throws if parsing fails
-            break;
-
-        case GncTransPropType::REC_DATE:
-            m_rec_date = boost::none;
-            if (!value.empty())
-                m_rec_date = parse_date (value, m_date_format); // Throws if parsing fails
-            break;
-
-        case GncTransPropType::TREC_DATE:
-            m_trec_date = boost::none;
-            if (!value.empty())
-                m_trec_date = parse_date (value, m_date_format); // Throws if parsing fails
-            break;
-
-        default:
-            /* Issue a warning for all other prop_types. */
-            PWARN ("%d is an invalid property for a split", static_cast<int>(prop_type));
-            break;
+        auto err_str = error.second;
+        if (!check_accts_mapped &&
+                ((err_str.find (_(bad_acct)) != std::string::npos) ||
+                 (err_str.find (_(bad_tacct)) != std::string::npos)))
+            continue;
+        full_error += (full_error.empty() ? "" : "\n") + error.second;
     }
 
+    return full_error;
+}
+
+std::string GncPreTrans::errors ()
+{
+    return gen_err_str (m_errors);
+}
+
+void GncPreSplit::set (GncTransPropType prop_type, const std::string& value)
+{
+    try
+    {
+        // Drop any existing error for the prop_type we're about to set
+        m_errors.erase(prop_type);
+
+        Account *acct = nullptr;
+        switch (prop_type)
+        {
+            case GncTransPropType::ACTION:
+                m_action = boost::none;
+                if (!value.empty())
+                    m_action = value;
+                break;
+
+            case GncTransPropType::TACTION:
+                m_taction = boost::none;
+                if (!value.empty())
+                    m_taction = value;
+                break;
+
+            case GncTransPropType::ACCOUNT:
+                m_account = boost::none;
+                if (value.empty())
+                    throw std::invalid_argument (_("Account value can't be empty."));
+                acct = gnc_csv_account_map_search (value.c_str());
+                if (acct)
+                    m_account = acct;
+                else
+                    throw std::invalid_argument (_(bad_acct));
+                break;
+
+            case GncTransPropType::TACCOUNT:
+                m_taccount = boost::none;
+                if (value.empty())
+                    throw std::invalid_argument (_("Transfer account value can't be empty."));
+
+                acct = gnc_csv_account_map_search (value.c_str());
+                if (acct)
+                    m_taccount = acct;
+                else
+                    throw std::invalid_argument (_(bad_tacct));
+                break;
+
+            case GncTransPropType::MEMO:
+                m_memo = boost::none;
+                if (!value.empty())
+                    m_memo = value;
+                break;
+
+            case GncTransPropType::TMEMO:
+                m_tmemo = boost::none;
+                if (!value.empty())
+                    m_tmemo = value;
+                break;
+
+            case GncTransPropType::DEPOSIT:
+                m_deposit = boost::none;
+                m_deposit = parse_amount (value, m_currency_format); // Will throw if parsing fails
+                break;
+            case GncTransPropType::WITHDRAWAL:
+                m_withdrawal = boost::none;
+                m_withdrawal = parse_amount (value, m_currency_format); // Will throw if parsing fails
+                break;
+
+            case GncTransPropType::PRICE:
+                m_price = boost::none;
+                m_price = parse_amount (value, m_currency_format); // Will throw if parsing fails
+                break;
+
+            case GncTransPropType::REC_STATE:
+                m_rec_state = boost::none;
+                m_rec_state = parse_reconciled (value); // Throws if parsing fails
+                break;
+
+            case GncTransPropType::TREC_STATE:
+                m_trec_state = boost::none;
+                m_trec_state = parse_reconciled (value); // Throws if parsing fails
+                break;
+
+            case GncTransPropType::REC_DATE:
+                m_rec_date = boost::none;
+                if (!value.empty())
+                    m_rec_date = parse_date (value, m_date_format); // Throws if parsing fails
+                break;
+
+            case GncTransPropType::TREC_DATE:
+                m_trec_date = boost::none;
+                if (!value.empty())
+                    m_trec_date = parse_date (value, m_date_format); // Throws if parsing fails
+                break;
+
+            default:
+                /* Issue a warning for all other prop_types. */
+                PWARN ("%d is an invalid property for a split", static_cast<int>(prop_type));
+                break;
+        }
+    }
+    catch (const std::invalid_argument& e)
+    {
+        auto err_str = std::string(_(gnc_csv_col_type_strs[prop_type])) +
+                       std::string(_(" could not be understood.\n")) +
+                       e.what();
+        m_errors.emplace(prop_type, err_str);
+        throw std::invalid_argument (err_str);
+    }
+    catch (const std::out_of_range& e)
+    {
+        auto err_str = std::string(_(gnc_csv_col_type_strs[prop_type])) +
+                       std::string(_(" could not be understood.\n")) +
+                       e.what();
+        m_errors.emplace(prop_type, err_str);
+        throw std::invalid_argument (err_str);
+    }
 }
 
 void GncPreSplit::reset (GncTransPropType prop_type)
@@ -521,7 +597,8 @@ void GncPreSplit::reset (GncTransPropType prop_type)
     catch (...)
     {
         // Set with an empty string will effectively clear the property
-        // but also throw in many cases. For a reset this is fine, so catch it here.
+        // but can also set an error for the property. Clear that error here.
+        m_errors.erase(prop_type);
     }
 }
 
@@ -672,3 +749,8 @@ void GncPreSplit::create_split (Transaction* trans)
 
     created = true;
 }
+
+std::string GncPreSplit::errors (bool check_accts_mapped)
+{
+    return gen_err_str (m_errors, check_accts_mapped);
+}
diff --git a/src/import-export/csv-imp/gnc-trans-props.hpp b/src/import-export/csv-imp/gnc-trans-props.hpp
index 8cb71f5..572e1ff 100644
--- a/src/import-export/csv-imp/gnc-trans-props.hpp
+++ b/src/import-export/csv-imp/gnc-trans-props.hpp
@@ -112,6 +112,7 @@ public:
     GncPreTrans(int date_format) : m_date_format{date_format} {};
 
     void set (GncTransPropType prop_type, const std::string& value);
+    void set_date_format (int date_format) { m_date_format = date_format ;}
     void reset (GncTransPropType prop_type);
     std::string verify_essentials (void);
     Transaction *create_trans (QofBook* book, gnc_commodity* currency);
@@ -132,6 +133,7 @@ public:
      */
     bool is_part_of (std::shared_ptr<GncPreTrans> parent);
     boost::optional<std::string> get_void_reason() { return m_void_reason; }
+    std::string errors();
 
 private:
     int m_date_format;
@@ -143,6 +145,8 @@ private:
     boost::optional<gnc_commodity*> m_commodity;
     boost::optional<std::string> m_void_reason;
     bool created = false;
+
+    std::map<GncTransPropType, std::string> m_errors;
 };
 
 struct GncPreSplit
@@ -152,11 +156,14 @@ public:
         m_currency_format{currency_format}{};
     void set (GncTransPropType prop_type, const std::string& value);
     void reset (GncTransPropType prop_type);
+    void set_date_format (int date_format) { m_date_format = date_format ;}
+    void set_currency_format (int currency_format) { m_currency_format = currency_format; }
     std::string verify_essentials (void);
     void create_split(Transaction* trans);
 
     Account* get_account () { if (m_account) return *m_account; else return nullptr; }
     void set_account (Account* acct) { if (acct) m_account = acct; else m_account = boost::none; }
+    std::string errors(bool check_accts_mapped);
 
 private:
     int m_date_format;
@@ -175,6 +182,8 @@ private:
     boost::optional<char> m_trec_state;
     boost::optional<time64> m_trec_date;
     bool created = false;
+
+    std::map<GncTransPropType, std::string> m_errors;
 };
 
 #endif
diff --git a/src/import-export/csv-imp/gnc-tx-import.cpp b/src/import-export/csv-imp/gnc-tx-import.cpp
index 665f5bd..04acc07 100644
--- a/src/import-export/csv-imp/gnc-tx-import.cpp
+++ b/src/import-export/csv-imp/gnc-tx-import.cpp
@@ -67,6 +67,7 @@ GncTxImport::GncTxImport(GncImpFileFormat format)
      * gnc_csv_parse_data_free is called before all of the data is
      * initialized, only the data that needs to be freed is freed. */
     m_skip_errors = false;
+    m_req_mapped_accts = true;
     file_format(m_settings.m_file_format = format);
 }
 
@@ -115,18 +116,30 @@ GncImpFileFormat GncTxImport::file_format()
 /** Toggles the multi-split state of the importer and will subsequently
  *  sanitize the column_types list. All types that don't make sense
  *  in the new state are reset to type GncTransPropType::NONE.
+ *  Additionally the interpretation of the columns with transaction
+ *  properties changes when changing multi-split mode. So this function
+ *  will force a reparsing of the transaction properties (if there are
+ *  any) by resetting the first column with a transaction property
+ *  it encounters.
  * @param multi_split_val Boolean value with desired state (multi-split
  * vs two-split).
  */
 void GncTxImport::multi_split (bool multi_split)
 {
+    auto trans_prop_seen = false;
     m_settings.m_multi_split = multi_split;
-    for (auto col_it = m_settings.m_column_types.begin(); col_it != m_settings.m_column_types.end();
-            col_it++)
+    for (uint i = 0; i < m_settings.m_column_types.size(); i++)
     {
-        auto san_prop = sanitize_trans_prop (*col_it, m_settings.m_multi_split);
-        if (san_prop != *col_it)
-            *col_it = san_prop;
+        auto old_prop = m_settings.m_column_types[i];
+        auto is_trans_prop = ((old_prop > GncTransPropType::NONE)
+                && (old_prop <= GncTransPropType::TRANS_PROPS));
+        auto san_prop = sanitize_trans_prop (old_prop, m_settings.m_multi_split);
+        if (san_prop != old_prop)
+            set_column_type (i, san_prop);
+        else if (is_trans_prop && !trans_prop_seen)
+            set_column_type (i, old_prop, true);
+        trans_prop_seen |= is_trans_prop;
+
     }
     if (m_settings.m_multi_split)
         m_settings.m_base_account = nullptr;
@@ -158,18 +171,52 @@ void GncTxImport::base_account (Account* base_account)
         auto col_type = std::find (m_settings.m_column_types.begin(),
                 m_settings.m_column_types.end(), GncTransPropType::ACCOUNT);
         if (col_type != m_settings.m_column_types.end())
-            *col_type = GncTransPropType::NONE;
+            set_column_type(col_type -m_settings.m_column_types.begin(),
+                            GncTransPropType::NONE);
+
+        /* Set default account for each line's split properties */
+        for (auto line : m_parsed_lines)
+            std::get<3>(line)->set_account (m_settings.m_base_account);
+
+
     }
 }
 
 Account *GncTxImport::base_account () { return m_settings.m_base_account; }
 
+void GncTxImport::reset_formatted_column (std::vector<GncTransPropType>& col_types)
+{
+    for (auto col_type: col_types)
+    {
+        auto col = std::find (m_settings.m_column_types.begin(),
+                m_settings.m_column_types.end(), col_type);
+        if (col != m_settings.m_column_types.end())
+            set_column_type (col - m_settings.m_column_types.begin(), col_type, true);
+    }
+}
+
 void GncTxImport::currency_format (int currency_format)
-    { m_settings.m_currency_format = currency_format; }
+{
+    m_settings.m_currency_format = currency_format;
+
+    /* Reparse all currency related columns */
+    std::vector<GncTransPropType> commodities = { GncTransPropType::DEPOSIT,
+            GncTransPropType::WITHDRAWAL,
+            GncTransPropType::PRICE};
+    reset_formatted_column (commodities);
+}
 int GncTxImport::currency_format () { return m_settings.m_currency_format; }
 
 void GncTxImport::date_format (int date_format)
-    { m_settings.m_date_format = date_format; }
+{
+    m_settings.m_date_format = date_format;
+
+    /* Reparse all date related columns */
+    std::vector<GncTransPropType> dates = { GncTransPropType::DATE,
+            GncTransPropType::REC_DATE,
+            GncTransPropType::TREC_DATE};
+    reset_formatted_column (dates);
+}
 int GncTxImport::date_format () { return m_settings.m_date_format; }
 
 /** Converts raw file data using a new encoding. This function must be
@@ -182,7 +229,15 @@ void GncTxImport::encoding (const std::string& encoding)
 
     // TODO investigate if we can catch conversion errors and report them
     if (m_tokenizer)
+    {
         m_tokenizer->encoding(encoding); // May throw
+        try
+        {
+            tokenize(false);
+        }
+        catch (...)
+        { };
+    }
 
     m_settings.m_encoding = encoding;
 }
@@ -286,7 +341,7 @@ void GncTxImport::load_file (const std::string& filename)
  * function.
  * Notes: - this function must be called with guessColTypes set to true once
  *          before calling it with guessColTypes set to false.
- *        - if guessColTypes is TRUE, all the column types will be set
+ *        - if guessColTypes is true, all the column types will be set
  *          GncTransPropType::NONE right now as real guessing isn't implemented yet
  * @param guessColTypes true to guess what the types of columns are based on the cell contents
  * @exception std::range_error if tokenizing failed
@@ -302,7 +357,9 @@ void GncTxImport::tokenize (bool guessColTypes)
     for (auto tokenized_line : m_tokenizer->get_tokens())
     {
         m_parsed_lines.push_back (std::make_tuple (tokenized_line, std::string(),
-                nullptr, nullptr, false));
+                std::make_shared<GncPreTrans>(date_format()),
+                std::make_shared<GncPreSplit>(date_format(), currency_format()),
+                false));
         auto length = tokenized_line.size();
         if (length > max_cols)
             max_cols = length;
@@ -317,6 +374,15 @@ void GncTxImport::tokenize (bool guessColTypes)
 
     m_settings.m_column_types.resize(max_cols, GncTransPropType::NONE);
 
+    /* Force reinterpretation of already set columns and/or base_account */
+    for (uint i = 0; i < m_settings.m_column_types.size(); i++)
+        set_column_type (i, m_settings.m_column_types[i], true);
+    if (m_settings.m_base_account)
+    {
+        for (auto line : m_parsed_lines)
+            std::get<3>(line)->set_account (m_settings.m_base_account);
+    }
+
     if (guessColTypes)
     {
         /* Guess column_types based
@@ -346,117 +412,6 @@ std::string ErrorList::str()
     return m_error.substr(0, m_error.size() - 1);
 }
 
-void GncTxImport::verify_data(ErrorList& error_msg)
-{
-    auto have_date_errors = false;
-    auto have_amount_errors = false;
-    for (uint i = 0; i < m_parsed_lines.size(); i++)
-    {
-        auto line_err = ErrorList();
-        auto line_data = std::get<0>(m_parsed_lines[i]);
-
-        /* Attempt to parse date column values */
-        auto date_col_it = std::find(m_settings.m_column_types.begin(),
-            m_settings.m_column_types.end(), GncTransPropType::DATE);
-        if (date_col_it != m_settings.m_column_types.end())
-        try
-        {
-            auto date_col = date_col_it -m_settings.m_column_types.begin();
-            auto date_str = line_data[date_col];
-            if (!m_settings.m_multi_split || !date_str.empty())
-                parse_date (date_str, date_format());
-        }
-        catch (...)
-        {
-            if (!std::get<4>(m_parsed_lines[i])) // Skipped lines don't trigger a global error
-                have_date_errors = true;
-            line_err.add_error(_("Date could not be understood"));
-        }
-
-        /* Attempt to parse reconcile date column values */
-        date_col_it = std::find(m_settings.m_column_types.begin(),
-            m_settings.m_column_types.end(), GncTransPropType::REC_DATE);
-        if (date_col_it != m_settings.m_column_types.end())
-        try
-        {
-            auto date_col = date_col_it -m_settings.m_column_types.begin();
-            auto date_str = line_data[date_col];
-            if (!date_str.empty())
-                parse_date (date_str, date_format());
-        }
-        catch (...)
-        {
-            if (!std::get<4>(m_parsed_lines[i])) // Skipped lines don't trigger a global error
-                have_date_errors = true;
-            line_err.add_error(_("Reconcile date could not be understood"));
-        }
-
-        /* Attempt to parse transfer reconcile date column values */
-        date_col_it = std::find(m_settings.m_column_types.begin(),
-            m_settings.m_column_types.end(), GncTransPropType::TREC_DATE);
-        if (date_col_it != m_settings.m_column_types.end())
-        try
-        {
-            auto date_col = date_col_it -m_settings.m_column_types.begin();
-            auto date_str = line_data[date_col];
-            if (!date_str.empty())
-                parse_date (date_str, date_format());
-        }
-        catch (...)
-        {
-            if (!std::get<4>(m_parsed_lines[i])) // Skipped lines don't trigger a global error
-                have_date_errors = true;
-            line_err.add_error(_("Transfer reconcile date could not be understood"));
-        }
-
-        /* Attempt to parse deposit column values */
-        auto num_col_it = std::find(m_settings.m_column_types.begin(),
-            m_settings.m_column_types.end(), GncTransPropType::DEPOSIT);
-        if (num_col_it != m_settings.m_column_types.end())
-        try
-        {
-            auto num_col = num_col_it -m_settings.m_column_types.begin();
-            auto num_str = line_data[num_col];
-            if (!m_settings.m_multi_split || !num_str.empty())
-                parse_amount (num_str, currency_format());
-        }
-        catch (...)
-        {
-            if (!std::get<4>(m_parsed_lines[i])) // Skipped lines don't trigger a global error
-                have_amount_errors = true;
-            line_err.add_error(_("Deposit amount could not be understood"));
-        }
-
-        /* Attempt to parse withdrawal column values */
-        num_col_it = std::find(m_settings.m_column_types.begin(),
-            m_settings.m_column_types.end(), GncTransPropType::WITHDRAWAL);
-        if (num_col_it != m_settings.m_column_types.end())
-        try
-        {
-            auto num_col = num_col_it -m_settings.m_column_types.begin();
-            auto num_str = line_data[num_col];
-            if (!m_settings.m_multi_split || !num_str.empty())
-                parse_amount (num_str, currency_format());
-        }
-        catch (...)
-        {
-            if (!std::get<4>(m_parsed_lines[i])) // Skipped lines don't trigger a global error
-                have_amount_errors = true;
-            line_err.add_error(_("Withdrawal amount could not be understood"));
-        }
-
-        if (!line_err.empty())
-            std::get<1>(m_parsed_lines[i]) = line_err.str();
-        else
-            std::get<1>(m_parsed_lines[i]).clear();
-    }
-
-    if (have_date_errors)
-        error_msg.add_error( _("Not all dates could be parsed. Please verify your chosen date format or adjust the lines to skip."));
-    if (have_amount_errors)
-        error_msg.add_error( _("Not all amounts could be parsed. Please verify your chosen currency format or adjust the lines to skip."));
-}
-
 
 /* Test for the required minimum number of columns selected and
  * the selection is consistent.
@@ -504,8 +459,11 @@ void GncTxImport::verify_column_selections (ErrorList& error_msg)
 }
 
 
-/* Test for the required minimum number of columns selected and
- * a valid date format.
+/* Check whether the chosen settings can successfully parse
+ * the import data. This will check:
+ * - there's at least one line selected for import
+ * - the minimum number of columns is selected
+ * - the values in the selected columns can be parsed meaningfully.
  * @return An empty string if all checks passed or the reason
  *         verification failed otherwise.
  */
@@ -530,7 +488,22 @@ std::string GncTxImport::verify ()
     }
 
     verify_column_selections (error_msg);
-    verify_data (error_msg);
+
+    update_skipped_lines (boost::none, boost::none, boost::none, boost::none);
+
+    auto have_line_errors = false;
+    for (auto line : m_parsed_lines)
+    {
+        if (!std::get<4>(line) && !std::get<1>(line).empty())
+        {
+            have_line_errors = true;
+            break;
+        }
+    }
+
+    if (have_line_errors)
+        error_msg.add_error( _("Not all fields could be parsed. Please correct the issues reported for each line or adjust the lines to skip."));
+
     return error_msg.str();
 }
 
@@ -629,70 +602,15 @@ void GncTxImport::create_transaction (std::vector<parse_line_t>::iterator& parse
 {
     StrVec line;
     std::string error_message;
-    auto trans_props = std::make_shared<GncPreTrans>(date_format());
-    auto split_props = std::make_shared<GncPreSplit>(date_format(), currency_format());
-    std::tie(line, error_message, std::ignore, std::ignore, std::ignore) = *parsed_line;
-    error_message.clear();
-
-    /* Convert all tokens in this line into transaction/split properties. */
-    auto col_types_it = m_settings.m_column_types.cbegin();
-    auto line_it = line.cbegin();
-    for (col_types_it, line_it;
-            col_types_it != m_settings.m_column_types.cend() &&
-            line_it != line.cend();
-            ++col_types_it, ++line_it)
-    {
-        try
-        {
-            if (*col_types_it == GncTransPropType::NONE)
-                continue; /* We do nothing with "None"-type columns. */
-            else if  (*col_types_it <= GncTransPropType::TRANS_PROPS)
-            {
-                if (m_settings.m_multi_split && line_it->empty())
-                    continue; // In multi-split mode, transaction properties can be empty
-                trans_props->set(*col_types_it, *line_it);
-            }
-            else
-                split_props->set(*col_types_it, *line_it);
-        }
-        catch (const std::exception& e)
-        {
-            if (!error_message.empty())
-                error_message += "\n";
-            error_message += _(gnc_csv_col_type_strs[*col_types_it]);
-            error_message += _(" column could not be understood.");
-            PINFO("User warning: %s", error_message.c_str());
-        }
-    }
-    std::get<2>(*parsed_line) = trans_props;
-
-    /* For multi-split input data, we need to check whether this line is part of a transaction that
-     * has already be started by a previous line. */
-    if (m_settings.m_multi_split)
-    {
-        if (trans_props->is_part_of(m_parent))
-        {
-            /* This line is part of an already started transaction
-             * continue with that one instead to make sure the split from this line
-             * gets added to the proper transaction */
-            std::get<2>(*parsed_line) = m_parent;
+    std::shared_ptr<GncPreTrans> trans_props = nullptr;
+    std::shared_ptr<GncPreSplit> split_props = nullptr;
+    bool skip_line = false;
+    std::tie(line, error_message, trans_props, split_props, skip_line) = *parsed_line;
 
-            /* Check if the parent line is ready for conversion. If not,
-             * this child line can't be converted either.
-             */
-            if (!m_parent->verify_essentials().empty())
-                error_message = _("First line of this transaction has errors.");
-        }
-        else
-        {
-            /* This line starts a new transaction, set it as parent for
-             * subsequent lines. */
-            m_parent = trans_props;
-        }
-    }
+    if (skip_line)
+        return;
 
-    if (!error_message.empty())
-        throw std::invalid_argument (error_message);
+    error_message.clear();
 
     // Add an ACCOUNT property with the default account if no account column was set by the user
     auto line_acct = split_props->get_account();
@@ -710,7 +628,6 @@ void GncTxImport::create_transaction (std::vector<parse_line_t>::iterator& parse
             throw std::invalid_argument(error_message);
         }
     }
-    std::get<3>(*parsed_line) = split_props;
 
     /* If column parsing was successful, convert trans properties into a draft transaction. */
     try
@@ -734,17 +651,17 @@ void GncTxImport::create_transaction (std::vector<parse_line_t>::iterator& parse
 
 
 /** Creates a list of transactions from parsed data. The parsed data
- * will first be validated. If any errors are found this function will
- * throw an error unless skip_errors was set.
+ * will first be validated. If any errors are found in lines that are marked
+ * for processing (ie not marked to skip) this function will
+ * throw an error.
  * @param skip_errors true skip over lines with errors
- * @exception throws std::invalid_argument if data validation fails and
- *            skip_errors wasn't set.
+ * @exception throws std::invalid_argument if data validation or processing fails.
  */
 void GncTxImport::create_transactions ()
 {
     /* Start with verifying the current data. */
     auto verify_result = verify();
-    if (!verify_result.empty() && !m_skip_errors)
+    if (!verify_result.empty())
         throw std::invalid_argument (verify_result);
 
     /* Drop all existing draft transactions */
@@ -761,15 +678,8 @@ void GncTxImport::create_transactions ()
         if ((std::get<4>(*parsed_lines_it)))
             continue;
 
-        try
-        {
-            create_transaction (parsed_lines_it);
-        }
-        catch (const std::invalid_argument& e)
-        {
-            std::get<1>(*parsed_lines_it) = e.what();
-            continue;
-        }
+        /* Should not throw anymore, otherwise verify needs revision */
+        create_transaction (parsed_lines_it);
     }
 }
 
@@ -782,20 +692,157 @@ GncTxImport::check_for_column_type (GncTransPropType type)
                         != m_settings.m_column_types.end());
 }
 
+/* A helper function intended to be called only from set_column_type */
+void GncTxImport::update_pre_trans_props (uint row, uint col, GncTransPropType prop_type)
+{
+    if ((prop_type == GncTransPropType::NONE) || (prop_type > GncTransPropType::TRANS_PROPS))
+        return; /* Only deal with transaction related properties. */
+
+    auto trans_props = std::make_shared<GncPreTrans> (*(std::get<2>(m_parsed_lines[row])).get());
+    auto value = std::string();
+
+    if (col < std::get<0>(m_parsed_lines[row]).size())
+        value = std::get<0>(m_parsed_lines[row]).at(col);
+
+    if (value.empty())
+        trans_props->reset (prop_type);
+    else
+    {
+        try
+        {
+            trans_props->set(prop_type, value);
+        }
+        catch (const std::exception& e)
+        {
+            /* Do nothing, just prevent the exception from escalating up
+             * However log the error if it happens on a row that's not skipped
+             */
+            if (!std::get<4>(m_parsed_lines[row]))
+                PINFO("User warning: %s", e.what());
+        }
+    }
+
+    /* Store the result */
+    std::get<2>(m_parsed_lines[row]) = trans_props;
+
+    /* For multi-split input data, we need to check whether this line is part of
+     * a transaction that has already been started by a previous line. */
+    if (m_settings.m_multi_split)
+    {
+        if (trans_props->is_part_of(m_parent))
+        {
+            /* This line is part of an already started transaction
+             * continue with that one instead to make sure the split from this line
+             * gets added to the proper transaction */
+            std::get<2>(m_parsed_lines[row]) = m_parent;
+        }
+        else
+        {
+            /* This line starts a new transaction, set it as parent for
+             * subsequent lines. */
+            m_parent = trans_props;
+        }
+    }
+}
+
+/* A helper function intended to be called only from set_column_type */
+void GncTxImport::update_pre_split_props (uint row, uint col, GncTransPropType prop_type)
+{
+    if ((prop_type > GncTransPropType::SPLIT_PROPS) || (prop_type <= GncTransPropType::TRANS_PROPS))
+        return; /* Only deal with split related properties. */
+
+    auto split_props = std::get<3>(m_parsed_lines[row]);
+    auto value = std::string();
+
+    if (col < std::get<0>(m_parsed_lines[row]).size())
+        value = std::get<0>(m_parsed_lines[row]).at(col);
+
+    if (value.empty())
+        split_props->reset (prop_type);
+    else
+    {
+        try
+        {
+            split_props->set(prop_type, value);
+        }
+        catch (const std::exception& e)
+        {
+            /* Do nothing, just prevent the exception from escalating up
+             * However log the error if it happens on a row that's not skipped
+             */
+            if (!std::get<4>(m_parsed_lines[row]))
+                PINFO("User warning: %s", e.what());
+        }
+    }
+}
+
+
 void
-GncTxImport::set_column_type (uint position, GncTransPropType type)
+GncTxImport::set_column_type (uint position, GncTransPropType type, bool force)
 {
     if (position >= m_settings.m_column_types.size())
         return;
 
+    auto old_type = m_settings.m_column_types[position];
+    if ((type == old_type) && !force)
+        return; /* Nothing to do */
+
     // Column types should be unique, so remove any previous occurrence of the new type
     std::replace(m_settings.m_column_types.begin(), m_settings.m_column_types.end(),
             type, GncTransPropType::NONE);
+
     m_settings.m_column_types.at (position) = type;
 
     // If the user has set an Account column, we can't have a base account set
     if (type == GncTransPropType::ACCOUNT)
         base_account (nullptr);
+
+    /* Update the preparsed data */
+    m_parent = nullptr;
+    for (auto parsed_lines_it = m_parsed_lines.begin();
+            parsed_lines_it != m_parsed_lines.end();
+            ++parsed_lines_it)
+    {
+        /* Reset date and currency formats for each trans/split props object
+         * to ensure column updates use the most recent one
+         */
+        std::get<2>(*parsed_lines_it)->set_date_format (m_settings.m_date_format);
+        std::get<3>(*parsed_lines_it)->set_date_format (m_settings.m_date_format);
+        std::get<3>(*parsed_lines_it)->set_currency_format (m_settings.m_currency_format);
+
+        uint row = parsed_lines_it - m_parsed_lines.begin();
+
+        /* If the column type actually changed, first reset the property
+         * represented by the old column type
+         */
+        if (old_type != type)
+        {
+            auto old_col = std::get<0>(*parsed_lines_it).size(); // Deliberately out of bounds to trigger a reset!
+            if ((old_type > GncTransPropType::NONE)
+                    && (old_type <= GncTransPropType::TRANS_PROPS))
+                update_pre_trans_props (row, old_col, old_type);
+            else if ((old_type > GncTransPropType::TRANS_PROPS)
+                    && (old_type <= GncTransPropType::SPLIT_PROPS))
+                update_pre_split_props (row, old_col, old_type);
+        }
+
+        /* Then set the property represented by the new column type */
+        if ((type > GncTransPropType::NONE)
+                && (type <= GncTransPropType::TRANS_PROPS))
+            update_pre_trans_props (row, position, type);
+        else if ((type > GncTransPropType::TRANS_PROPS)
+                && (type <= GncTransPropType::SPLIT_PROPS))
+            update_pre_split_props (row, position, type);
+
+        /* Report errors if there are any */
+        auto trans_errors = std::get<2>(*parsed_lines_it)->errors();
+        auto split_errors = std::get<3>(*parsed_lines_it)->errors(m_req_mapped_accts);
+        std::get<1>(*parsed_lines_it) =
+                trans_errors +
+                (trans_errors.empty() && split_errors.empty() ? std::string() : "\n") +
+                split_errors;
+
+    }
 }
 
 std::vector<GncTransPropType> GncTxImport::column_types ()
diff --git a/src/import-export/csv-imp/gnc-tx-import.hpp b/src/import-export/csv-imp/gnc-tx-import.hpp
index 09ef501..4072cc3 100644
--- a/src/import-export/csv-imp/gnc-tx-import.hpp
+++ b/src/import-export/csv-imp/gnc-tx-import.hpp
@@ -123,6 +123,8 @@ public:
     bool skip_alt_lines ();
     bool skip_err_lines ();
 
+    void req_mapped_accts (bool val) {m_req_mapped_accts = val; }
+
     void separators (std::string separators);
     std::string separators ();
 
@@ -144,7 +146,7 @@ public:
      */
     void create_transactions ();
     bool check_for_column_type (GncTransPropType type);
-    void set_column_type (uint position, GncTransPropType type);
+    void set_column_type (uint position, GncTransPropType type, bool force = false);
     std::vector<GncTransPropType> column_types ();
 
     std::set<std::string> accounts ();
@@ -164,19 +166,29 @@ private:
     void create_transaction (std::vector<parse_line_t>::iterator& parsed_line);
 
     void verify_column_selections (ErrorList& error_msg);
-    void verify_data(ErrorList& error_msg);
+
+    /* Internal helper function to force reparsing of columns subject to format changes */
+    void reset_formatted_column (std::vector<GncTransPropType>& col_types);
 
     /* Internal helper function that does the actual conversion from property lists
      * to real (possibly unbalanced) transaction with splits.
      */
     std::shared_ptr<DraftTransaction> trans_properties_to_trans (std::vector<parse_line_t>::iterator& parsed_line);
 
+    /* Two internal helper functions that should only be called from within
+     * set_column_type for consistency (otherwise error messages may not be (re)set)
+     */
+    void update_pre_trans_props (uint row, uint col, GncTransPropType prop_type);
+    void update_pre_split_props (uint row, uint col, GncTransPropType prop_type);
+
     struct CsvTranSettings;
     CsvTransSettings m_settings;
     bool m_skip_errors;
+    bool m_req_mapped_accts;
 
     /* The parameters below are only used while creating
-     * transactions. They keep state information during the conversion.
+     * transactions. They keep state information while processing multi-split
+     * transactions.
      */
     std::shared_ptr<GncPreTrans> m_parent = nullptr;
     std::shared_ptr<DraftTransaction> m_current_draft = nullptr;

commit 1660276e295fb9f7487bfd5aebcb9e4942ad3c01
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Wed Dec 28 09:53:15 2016 +0100

    Copyright updates

diff --git a/src/import-export/csv-imp/gnc-csv-trans-settings.cpp b/src/import-export/csv-imp/gnc-csv-trans-settings.cpp
index 12104cf..a8fb718 100644
--- a/src/import-export/csv-imp/gnc-csv-trans-settings.cpp
+++ b/src/import-export/csv-imp/gnc-csv-trans-settings.cpp
@@ -23,6 +23,7 @@
 /** @file gnc-csv-trans-settings.c
     @brief CSV Import Settings
     @author Copyright (c) 2014 Robert Fewell
+    @author Copyright (c) 2016 Geert Janssens
 */
 
 #include "gnc-csv-trans-settings.hpp"
diff --git a/src/import-export/csv-imp/gnc-csv-trans-settings.hpp b/src/import-export/csv-imp/gnc-csv-trans-settings.hpp
index f137113..6837aef 100644
--- a/src/import-export/csv-imp/gnc-csv-trans-settings.hpp
+++ b/src/import-export/csv-imp/gnc-csv-trans-settings.hpp
@@ -23,6 +23,7 @@
 /** @file gnc-csv-trans-settings.h
     @brief CSV Import Settings
     @author Copyright (c) 2014 Robert Fewell
+    @author Copyright (c) 2016 Geert Janssens
 */
 #ifndef GNC_CSV_TRANS_SETTINGS_H
 #define GNC_CSV_TRANS_SETTINGS_H

commit 5ba4764a2d18546d5d3bf3977dfd17f91b4a8f99
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Wed Dec 28 09:40:06 2016 +0100

    Add a reset member function to GncPreTrans and GncPreSplit
    
    These will be used in future commits

diff --git a/src/import-export/csv-imp/gnc-trans-props.cpp b/src/import-export/csv-imp/gnc-trans-props.cpp
index 4f29cc7..85300ac 100644
--- a/src/import-export/csv-imp/gnc-trans-props.cpp
+++ b/src/import-export/csv-imp/gnc-trans-props.cpp
@@ -296,51 +296,51 @@ gnc_commodity* parse_commodity (const std::string& comm_str)
         return comm;
 }
 
-void GncPreTrans::set_property (GncTransPropType prop_type, const std::string& value)
+void GncPreTrans::set (GncTransPropType prop_type, const std::string& value)
 {
+    gnc_commodity *comm = nullptr;
     switch (prop_type)
     {
         case GncTransPropType::UNIQUE_ID:
+            m_differ = boost::none;
             if (!value.empty())
                 m_differ = value;
-            else
-                m_differ = boost::none;
             break;
 
         case GncTransPropType::DATE:
+            m_date = boost::none;
             m_date = parse_date (value, m_date_format); // Throws if parsing fails
             break;
 
         case GncTransPropType::NUM:
+            m_num = boost::none;
             if (!value.empty())
                 m_num = value;
-            else
-                m_num = boost::none;
             break;
 
         case GncTransPropType::DESCRIPTION:
+            m_desc = boost::none;
             if (!value.empty())
                 m_desc = value;
-            else
-                m_desc = boost::none;
             break;
 
         case GncTransPropType::NOTES:
+            m_notes = boost::none;
             if (!value.empty())
                 m_notes = value;
-            else
-                m_notes = boost::none;
             break;
 
         case GncTransPropType::COMMODITY:
-            m_commodity = parse_commodity (value); // Throws if parsing fails
+            m_commodity = boost::none;
+            comm = parse_commodity (value); // Throws if parsing fails
+            if (comm)
+                m_commodity = comm;
             break;
 
         case GncTransPropType::VOID_REASON:
+            m_void_reason = boost::none;
             if (!value.empty())
                 m_void_reason = value;
-            else
-                m_void_reason = boost::none;
             break;
 
         default:
@@ -351,6 +351,19 @@ void GncPreTrans::set_property (GncTransPropType prop_type, const std::string& v
 
 }
 
+void GncPreTrans::reset (GncTransPropType prop_type)
+{
+    try
+    {
+        set (prop_type, std::string());
+    }
+    catch (...)
+    {
+        // Set with an empty string will effectively clear the property
+        // but also throw in many cases. For a reset this is fine, so catch it here.
+    }
+}
+
 std::string GncPreTrans::verify_essentials (void)
 {
     /* Make sure this transaction has the minimum required set of properties defined */
@@ -408,26 +421,25 @@ bool GncPreTrans::is_part_of (std::shared_ptr<GncPreTrans> parent)
             (!m_void_reason || m_void_reason == parent->m_void_reason);
 }
 
-void GncPreSplit::set_property (GncTransPropType prop_type, const std::string& value)
+void GncPreSplit::set (GncTransPropType prop_type, const std::string& value)
 {
     Account *acct = nullptr;
     switch (prop_type)
     {
         case GncTransPropType::ACTION:
+            m_action = boost::none;
             if (!value.empty())
                 m_action = value;
-            else
-                m_action = boost::none;
             break;
 
         case GncTransPropType::TACTION:
+            m_taction = boost::none;
             if (!value.empty())
                 m_taction = value;
-            else
-                m_taction = boost::none;
             break;
 
         case GncTransPropType::ACCOUNT:
+            m_account = boost::none;
             acct = gnc_csv_account_map_search (value.c_str());
             if (acct)
                 m_account = acct;
@@ -436,6 +448,7 @@ void GncPreSplit::set_property (GncTransPropType prop_type, const std::string& v
             break;
 
         case GncTransPropType::TACCOUNT:
+            m_taccount = boost::none;
             acct = gnc_csv_account_map_search (value.c_str());
             if (acct)
                 m_taccount = acct;
@@ -444,45 +457,51 @@ void GncPreSplit::set_property (GncTransPropType prop_type, const std::string& v
             break;
 
         case GncTransPropType::MEMO:
+            m_memo = boost::none;
             if (!value.empty())
                 m_memo = value;
-            else
-                m_memo = boost::none;
             break;
 
         case GncTransPropType::TMEMO:
+            m_tmemo = boost::none;
             if (!value.empty())
                 m_tmemo = value;
-            else
-                m_tmemo = boost::none;
             break;
 
         case GncTransPropType::DEPOSIT:
+            m_deposit = boost::none;
             m_deposit = parse_amount (value, m_currency_format); // Will throw if parsing fails
             break;
         case GncTransPropType::WITHDRAWAL:
+            m_withdrawal = boost::none;
             m_withdrawal = parse_amount (value, m_currency_format); // Will throw if parsing fails
             break;
 
         case GncTransPropType::PRICE:
+            m_price = boost::none;
             m_price = parse_amount (value, m_currency_format); // Will throw if parsing fails
             break;
 
         case GncTransPropType::REC_STATE:
+            m_rec_state = boost::none;
             m_rec_state = parse_reconciled (value); // Throws if parsing fails
             break;
 
         case GncTransPropType::TREC_STATE:
+            m_trec_state = boost::none;
             m_trec_state = parse_reconciled (value); // Throws if parsing fails
             break;
 
         case GncTransPropType::REC_DATE:
+            m_rec_date = boost::none;
             if (!value.empty())
                 m_rec_date = parse_date (value, m_date_format); // Throws if parsing fails
             break;
 
         case GncTransPropType::TREC_DATE:
-            m_trec_date = parse_date (value, m_date_format); // Throws if parsing fails
+            m_trec_date = boost::none;
+            if (!value.empty())
+                m_trec_date = parse_date (value, m_date_format); // Throws if parsing fails
             break;
 
         default:
@@ -493,6 +512,19 @@ void GncPreSplit::set_property (GncTransPropType prop_type, const std::string& v
 
 }
 
+void GncPreSplit::reset (GncTransPropType prop_type)
+{
+    try
+    {
+        set (prop_type, std::string());
+    }
+    catch (...)
+    {
+        // Set with an empty string will effectively clear the property
+        // but also throw in many cases. For a reset this is fine, so catch it here.
+    }
+}
+
 std::string GncPreSplit::verify_essentials (void)
 {
     auto err_msg = std::string();
diff --git a/src/import-export/csv-imp/gnc-trans-props.hpp b/src/import-export/csv-imp/gnc-trans-props.hpp
index 9a7e2c9..8cb71f5 100644
--- a/src/import-export/csv-imp/gnc-trans-props.hpp
+++ b/src/import-export/csv-imp/gnc-trans-props.hpp
@@ -111,7 +111,8 @@ struct GncPreTrans
 public:
     GncPreTrans(int date_format) : m_date_format{date_format} {};
 
-    void set_property (GncTransPropType prop_type, const std::string& value);
+    void set (GncTransPropType prop_type, const std::string& value);
+    void reset (GncTransPropType prop_type);
     std::string verify_essentials (void);
     Transaction *create_trans (QofBook* book, gnc_commodity* currency);
 
@@ -149,7 +150,8 @@ struct GncPreSplit
 public:
     GncPreSplit (int date_format, int currency_format) : m_date_format{date_format},
         m_currency_format{currency_format}{};
-    void set_property (GncTransPropType prop_type, const std::string& value);
+    void set (GncTransPropType prop_type, const std::string& value);
+    void reset (GncTransPropType prop_type);
     std::string verify_essentials (void);
     void create_split(Transaction* trans);
 
diff --git a/src/import-export/csv-imp/gnc-tx-import.cpp b/src/import-export/csv-imp/gnc-tx-import.cpp
index 260df26..665f5bd 100644
--- a/src/import-export/csv-imp/gnc-tx-import.cpp
+++ b/src/import-export/csv-imp/gnc-tx-import.cpp
@@ -650,10 +650,10 @@ void GncTxImport::create_transaction (std::vector<parse_line_t>::iterator& parse
             {
                 if (m_settings.m_multi_split && line_it->empty())
                     continue; // In multi-split mode, transaction properties can be empty
-                trans_props->set_property(*col_types_it, *line_it);
+                trans_props->set(*col_types_it, *line_it);
             }
             else
-                split_props->set_property(*col_types_it, *line_it);
+                split_props->set(*col_types_it, *line_it);
         }
         catch (const std::exception& e)
         {

commit 5b446cd9a1d9ab971955cc20b449141869c9a826
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sat Dec 24 17:36:31 2016 +0100

    Make sure all lines are checked for errors, regardless of their skipped status
    
    Which errors get actually reported the user remains filtered based on the
    line skipping options the user has set.

diff --git a/src/import-export/csv-imp/gnc-tx-import.cpp b/src/import-export/csv-imp/gnc-tx-import.cpp
index e6c70cb..260df26 100644
--- a/src/import-export/csv-imp/gnc-tx-import.cpp
+++ b/src/import-export/csv-imp/gnc-tx-import.cpp
@@ -352,103 +352,103 @@ void GncTxImport::verify_data(ErrorList& error_msg)
     auto have_amount_errors = false;
     for (uint i = 0; i < m_parsed_lines.size(); i++)
     {
-        if (std::get<4>(m_parsed_lines[i])) // Ignore skipped lines
-            continue;
-        else
+        auto line_err = ErrorList();
+        auto line_data = std::get<0>(m_parsed_lines[i]);
+
+        /* Attempt to parse date column values */
+        auto date_col_it = std::find(m_settings.m_column_types.begin(),
+            m_settings.m_column_types.end(), GncTransPropType::DATE);
+        if (date_col_it != m_settings.m_column_types.end())
+        try
         {
-            auto line_err = ErrorList();
-            auto line_data = std::get<0>(m_parsed_lines[i]);
-
-            /* Attempt to parse date column values */
-            auto date_col_it = std::find(m_settings.m_column_types.begin(),
-                m_settings.m_column_types.end(), GncTransPropType::DATE);
-            if (date_col_it != m_settings.m_column_types.end())
-            try
-            {
-                auto date_col = date_col_it -m_settings.m_column_types.begin();
-                auto date_str = line_data[date_col];
-                if (!m_settings.m_multi_split || !date_str.empty())
-                    parse_date (date_str, date_format());
-            }
-            catch (...)
-            {
+            auto date_col = date_col_it -m_settings.m_column_types.begin();
+            auto date_str = line_data[date_col];
+            if (!m_settings.m_multi_split || !date_str.empty())
+                parse_date (date_str, date_format());
+        }
+        catch (...)
+        {
+            if (!std::get<4>(m_parsed_lines[i])) // Skipped lines don't trigger a global error
                 have_date_errors = true;
-                line_err.add_error(_("Date could not be understood"));
-            }
+            line_err.add_error(_("Date could not be understood"));
+        }
 
-            /* Attempt to parse reconcile date column values */
-            date_col_it = std::find(m_settings.m_column_types.begin(),
-                m_settings.m_column_types.end(), GncTransPropType::REC_DATE);
-            if (date_col_it != m_settings.m_column_types.end())
-            try
-            {
-                auto date_col = date_col_it -m_settings.m_column_types.begin();
-                auto date_str = line_data[date_col];
-                if (!date_str.empty())
-                    parse_date (date_str, date_format());
-            }
-            catch (...)
-            {
+        /* Attempt to parse reconcile date column values */
+        date_col_it = std::find(m_settings.m_column_types.begin(),
+            m_settings.m_column_types.end(), GncTransPropType::REC_DATE);
+        if (date_col_it != m_settings.m_column_types.end())
+        try
+        {
+            auto date_col = date_col_it -m_settings.m_column_types.begin();
+            auto date_str = line_data[date_col];
+            if (!date_str.empty())
+                parse_date (date_str, date_format());
+        }
+        catch (...)
+        {
+            if (!std::get<4>(m_parsed_lines[i])) // Skipped lines don't trigger a global error
                 have_date_errors = true;
-                line_err.add_error(_("Reconcile date could not be understood"));
-            }
+            line_err.add_error(_("Reconcile date could not be understood"));
+        }
 
-            /* Attempt to parse transfer reconcile date column values */
-            date_col_it = std::find(m_settings.m_column_types.begin(),
-                m_settings.m_column_types.end(), GncTransPropType::REC_DATE);
-            if (date_col_it != m_settings.m_column_types.end())
-            try
-            {
-                auto date_col = date_col_it -m_settings.m_column_types.begin();
-                auto date_str = line_data[date_col];
-                if (!date_str.empty())
-                    parse_date (date_str, date_format());
-            }
-            catch (...)
-            {
+        /* Attempt to parse transfer reconcile date column values */
+        date_col_it = std::find(m_settings.m_column_types.begin(),
+            m_settings.m_column_types.end(), GncTransPropType::TREC_DATE);
+        if (date_col_it != m_settings.m_column_types.end())
+        try
+        {
+            auto date_col = date_col_it -m_settings.m_column_types.begin();
+            auto date_str = line_data[date_col];
+            if (!date_str.empty())
+                parse_date (date_str, date_format());
+        }
+        catch (...)
+        {
+            if (!std::get<4>(m_parsed_lines[i])) // Skipped lines don't trigger a global error
                 have_date_errors = true;
-                line_err.add_error(_("Transfer reconcile date could not be understood"));
-            }
+            line_err.add_error(_("Transfer reconcile date could not be understood"));
+        }
 
-            /* Attempt to parse deposit column values */
-            auto num_col_it = std::find(m_settings.m_column_types.begin(),
-                m_settings.m_column_types.end(), GncTransPropType::DEPOSIT);
-            if (num_col_it != m_settings.m_column_types.end())
-            try
-            {
-                auto num_col = num_col_it -m_settings.m_column_types.begin();
-                auto num_str = line_data[num_col];
-                if (!m_settings.m_multi_split || !num_str.empty())
-                    parse_amount (num_str, currency_format());
-            }
-            catch (...)
-            {
+        /* Attempt to parse deposit column values */
+        auto num_col_it = std::find(m_settings.m_column_types.begin(),
+            m_settings.m_column_types.end(), GncTransPropType::DEPOSIT);
+        if (num_col_it != m_settings.m_column_types.end())
+        try
+        {
+            auto num_col = num_col_it -m_settings.m_column_types.begin();
+            auto num_str = line_data[num_col];
+            if (!m_settings.m_multi_split || !num_str.empty())
+                parse_amount (num_str, currency_format());
+        }
+        catch (...)
+        {
+            if (!std::get<4>(m_parsed_lines[i])) // Skipped lines don't trigger a global error
                 have_amount_errors = true;
-                line_err.add_error(_("Deposit amount could not be understood"));
-            }
+            line_err.add_error(_("Deposit amount could not be understood"));
+        }
 
-            /* Attempt to parse withdrawal column values */
-            num_col_it = std::find(m_settings.m_column_types.begin(),
-                m_settings.m_column_types.end(), GncTransPropType::WITHDRAWAL);
-            if (num_col_it != m_settings.m_column_types.end())
-            try
-            {
-                auto num_col = num_col_it -m_settings.m_column_types.begin();
-                auto num_str = line_data[num_col];
-                if (!m_settings.m_multi_split || !num_str.empty())
-                    parse_amount (num_str, currency_format());
-            }
-            catch (...)
-            {
+        /* Attempt to parse withdrawal column values */
+        num_col_it = std::find(m_settings.m_column_types.begin(),
+            m_settings.m_column_types.end(), GncTransPropType::WITHDRAWAL);
+        if (num_col_it != m_settings.m_column_types.end())
+        try
+        {
+            auto num_col = num_col_it -m_settings.m_column_types.begin();
+            auto num_str = line_data[num_col];
+            if (!m_settings.m_multi_split || !num_str.empty())
+                parse_amount (num_str, currency_format());
+        }
+        catch (...)
+        {
+            if (!std::get<4>(m_parsed_lines[i])) // Skipped lines don't trigger a global error
                 have_amount_errors = true;
-                line_err.add_error(_("Withdrawal amount could not be understood"));
-            }
-
-            if (!line_err.empty())
-                std::get<1>(m_parsed_lines[i]) = line_err.str();
-            else
-                std::get<1>(m_parsed_lines[i]).clear();
+            line_err.add_error(_("Withdrawal amount could not be understood"));
         }
+
+        if (!line_err.empty())
+            std::get<1>(m_parsed_lines[i]) = line_err.str();
+        else
+            std::get<1>(m_parsed_lines[i]).clear();
     }
 
     if (have_date_errors)

commit f8470ffa4c8877c2b7a7939b24d56685c83c2be7
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sat Dec 24 17:34:43 2016 +0100

    Isolate the gtk callbacks from the assistant class' member variables.
    
    This required some refactoring of a number of callback funtions.

diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index a5fc07e..7263b12 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -70,32 +70,66 @@ extern "C"
 /* This static indicates the debugging module that this .o belongs to.  */
 static QofLogModule log_module = GNC_MOD_ASSISTANT;
 
-struct  CsvImpTransAssist
+class  CsvImpTransAssist
 {
+public:
     CsvImpTransAssist ();
 
+    void assist_prepare_cb (GtkWidget *page);
     void assist_file_page_prepare ();
     void assist_preview_page_prepare ();
     void assist_account_match_page_prepare ();
     void assist_doc_page_prepare ();
     void assist_match_page_prepare ();
     void assist_summary_page_prepare ();
+    void assist_finish (bool canceled);
+    void assist_compmgr_close ();
+
+    void file_confirm_cb ();
+
+    void preview_settings_delete ();
+    void preview_settings_save ();
+    void preview_settings_name (GtkEntry* entry);
+    void preview_settings_load ();
+    void preview_update_skipped_rows ();
+    void preview_multi_split (bool multi);
+    void preview_update_separators (GtkWidget* widget);
+    void preview_update_file_format ();
+    void preview_update_account ();
+    void preview_update_encoding (const char* encoding);
+    void preview_update_date_format ();
+    void preview_update_currency_format ();
+    void preview_update_col_type (GtkCellRenderer* renderer,
+            GtkTreeIter* new_text_iter);
+    void preview_update_fw_columns (GtkWidget* button, GdkEventButton* event);
 
     void preview_populate_settings_combo();
     void preview_handle_save_del_sensitivity (GtkComboBox* combo);
+    void preview_resize_treeview();
     void preview_row_sel_update ();
     void preview_split_column (int col, int dx);
     void preview_refresh_table ();
     void preview_refresh ();
     void preview_validate_settings ();
+
+    void acct_match_via_button ();
+    bool acct_match_via_view_dblclick (GdkEventButton *event);
     void acct_match_select(GtkTreeModel *model, GtkTreeIter* iter);
     void acct_match_set_accounts ();
 
+    friend gboolean
+    fixed_context_menu_handler (GnumericPopupMenuElement const *element,
+            gpointer userdata);
+private:
+    /* member functions to manage the context menu for fixed with columns */
+    uint get_new_col_rel_pos (int col, int dx);
+    void fixed_context_menu (GdkEventButton *event, int col, int dx);
+
     GtkAssistant    *csv_imp_asst;
 
     GtkWidget       *file_page;                     /**< Assistant file page widget */
     GtkWidget       *file_chooser;                  /**< The widget for the file chooser */
-    std::string      file_name;                     /**< The import file name */
+    std::string      m_file_name;                     /**< The import file name */
 
     GtkWidget       *preview_page;                  /**< Assistant preview page widget */
     GtkComboBox     *settings_combo;                /**< The Settings Combo */
@@ -106,6 +140,7 @@ struct  CsvImpTransAssist
     GtkSpinButton   *start_row_spin;                /**< The widget for the start row spinner */
     GtkSpinButton   *end_row_spin;                  /**< The widget for the end row spinner */
     GtkWidget       *skip_alt_rows_button;          /**< The widget for Skip alternate rows from start row */
+    GtkWidget       *skip_errors_button;            /**< The widget for Skip error rows*/
     GtkWidget       *csv_button;                    /**< The widget for the CSV button */
     GtkWidget       *fixed_button;                  /**< The widget for the Fixed Width button */
     GtkWidget       *multi_split_cbutton;           /**< The widget for Multi-split */
@@ -121,7 +156,6 @@ struct  CsvImpTransAssist
     GtkImage        *instructions_image;            /**< The instructions image */
     bool             encoding_selected_called;      /**< Before encoding_selected is first called, this is false.
                                                        * error lines, instead of all the file data. */
-    bool             skip_errors;                   /**< This is false until the user checks the skip errors. */
     int              fixed_context_col;             /**< The number of the column whose the user has clicked */
     int              fixed_context_dx;              /**< The horizontal coordinate of the pixel in the header of the column
                                                        * the user has clicked */
@@ -147,25 +181,27 @@ struct  CsvImpTransAssist
 };
 
 
-/*************************************************************************/
+/*******************************************************
+ * Assistant call back functions
+ *******************************************************/
 
 extern "C"
 {
 void csv_tximp_assist_prepare_cb (GtkAssistant  *assistant, GtkWidget *page, CsvImpTransAssist* info);
-void csv_tximp_assist_finish_cb (GtkAssistant *gtkassistant, CsvImpTransAssist* info);
+void csv_tximp_assist_destroy_cb (GtkWidget *object, CsvImpTransAssist* info);
 void csv_tximp_assist_cancel_cb (GtkAssistant *gtkassistant, CsvImpTransAssist* info);
 void csv_tximp_assist_close_cb (GtkAssistant *gtkassistant, CsvImpTransAssist* info);
-void csv_tximp_assist_destroy_cb (GtkWidget *object, CsvImpTransAssist* info);
+void csv_tximp_assist_finish_cb (GtkAssistant *gtkassistant, CsvImpTransAssist* info);
 void csv_tximp_file_confirm_cb (GtkWidget *button, CsvImpTransAssist *info);
-void csv_tximp_preview_srow_cb (GtkSpinButton *spin, CsvImpTransAssist *info);
-void csv_tximp_preview_erow_cb (GtkSpinButton *spin, CsvImpTransAssist *info);
-void csv_tximp_preview_skiperrors_cb (GtkToggleButton *checkbox, CsvImpTransAssist *info);
-void csv_tximp_preview_skiprows_cb (GtkToggleButton *checkbox, CsvImpTransAssist *info);
-void csv_tximp_preview_multisplit_cb (GtkToggleButton *checkbox, CsvImpTransAssist *info);
 void csv_tximp_preview_del_settings_cb (GtkWidget *button, CsvImpTransAssist *info);
 void csv_tximp_preview_save_settings_cb (GtkWidget *button, CsvImpTransAssist *info);
 void csv_tximp_preview_settings_sel_changed_cb (GtkComboBox *combo, CsvImpTransAssist *info);
 void csv_tximp_preview_settings_text_changed_cb (GtkEntry *entry, CsvImpTransAssist *info);
+void csv_tximp_preview_srow_cb (GtkSpinButton *spin, CsvImpTransAssist *info);
+void csv_tximp_preview_erow_cb (GtkSpinButton *spin, CsvImpTransAssist *info);
+void csv_tximp_preview_skiprows_cb (GtkToggleButton *checkbox, CsvImpTransAssist *info);
+void csv_tximp_preview_skiperrors_cb (GtkToggleButton *checkbox, CsvImpTransAssist *info);
+void csv_tximp_preview_multisplit_cb (GtkToggleButton *checkbox, CsvImpTransAssist *info);
 void csv_tximp_preview_sep_button_cb (GtkWidget* widget, CsvImpTransAssist* info);
 void csv_tximp_preview_sep_fixed_sel_cb (GtkToggleButton* csv_button, CsvImpTransAssist* info);
 void csv_tximp_preview_acct_sel_cb (GtkWidget* widget, CsvImpTransAssist* info);
@@ -175,8 +211,365 @@ void csv_tximp_acct_match_button_clicked_cb (GtkWidget *widget, CsvImpTransAssis
 bool csv_tximp_acct_match_view_clicked_cb (GtkWidget *widget, GdkEventButton *event, CsvImpTransAssist* info);
 }
 
-/*************************************************************************/
+void
+csv_tximp_assist_prepare_cb (GtkAssistant *assistant, GtkWidget *page,
+        CsvImpTransAssist* info)
+{
+    info->assist_prepare_cb(page);
+}
+
+void
+csv_tximp_assist_destroy_cb (GtkWidget *object, CsvImpTransAssist* info)
+{
+    gnc_unregister_gui_component_by_data (ASSISTANT_CSV_IMPORT_TRANS_CM_CLASS, info);
+    delete info;
+}
+
+void
+csv_tximp_assist_cancel_cb (GtkAssistant *assistant, CsvImpTransAssist* info)
+{
+    info->assist_finish (true);
+    gnc_close_gui_component_by_data (ASSISTANT_CSV_IMPORT_TRANS_CM_CLASS, info);
+}
+
+void
+csv_tximp_assist_close_cb (GtkAssistant *assistant, CsvImpTransAssist* info)
+{
+    gnc_close_gui_component_by_data (ASSISTANT_CSV_IMPORT_TRANS_CM_CLASS, info);
+}
+
+void
+csv_tximp_assist_finish_cb (GtkAssistant *assistant, CsvImpTransAssist* info)
+{
+    info->assist_finish (false);
+}
+
+
+void csv_tximp_file_confirm_cb (GtkWidget *button, CsvImpTransAssist *info)
+{
+    info->file_confirm_cb();
+}
+
+void csv_tximp_preview_del_settings_cb (GtkWidget *button, CsvImpTransAssist *info)
+{
+    info->preview_settings_delete();
+}
+
+void csv_tximp_preview_save_settings_cb (GtkWidget *button, CsvImpTransAssist *info)
+{
+    info->preview_settings_save();
+}
+
+void csv_tximp_preview_settings_sel_changed_cb (GtkComboBox *combo, CsvImpTransAssist *info)
+{
+    info->preview_settings_load();
+}
+
+void
+csv_tximp_preview_settings_text_changed_cb (GtkEntry *entry, CsvImpTransAssist *info)
+{
+    info->preview_settings_name(entry);
+}
+
+void csv_tximp_preview_srow_cb (GtkSpinButton *spin, CsvImpTransAssist *info)
+{
+    info->preview_update_skipped_rows();
+}
+
+void csv_tximp_preview_erow_cb (GtkSpinButton *spin, CsvImpTransAssist *info)
+{
+    info->preview_update_skipped_rows();
+}
+
+void csv_tximp_preview_skiprows_cb (GtkToggleButton *checkbox, CsvImpTransAssist *info)
+{
+    info->preview_update_skipped_rows();
+}
+
+void csv_tximp_preview_skiperrors_cb (GtkToggleButton *checkbox, CsvImpTransAssist *info)
+{
+    info->preview_update_skipped_rows();
+}
+
+void csv_tximp_preview_multisplit_cb (GtkToggleButton *checkbox, CsvImpTransAssist *info)
+{
+    info->preview_multi_split (gtk_toggle_button_get_active (checkbox));
+}
+
+void csv_tximp_preview_sep_button_cb (GtkWidget* widget, CsvImpTransAssist* info)
+{
+    info->preview_update_separators(widget);
+}
+
+void csv_tximp_preview_sep_fixed_sel_cb (GtkToggleButton* csv_button, CsvImpTransAssist* info)
+{
+    info->preview_update_file_format();
+}
+
+void csv_tximp_preview_acct_sel_cb (GtkWidget* widget, CsvImpTransAssist* info)
+{
+    info->preview_update_account();
+}
+
+void csv_tximp_preview_enc_sel_cb (GOCharmapSel* selector, const char* encoding,
+                              CsvImpTransAssist* info)
+{
+    info->preview_update_encoding(encoding);
+}
+
+static void csv_tximp_preview_date_fmt_sel_cb (GtkComboBox* format_selector, CsvImpTransAssist* info)
+{
+    info->preview_update_date_format();
+}
+
+static void csv_tximp_preview_currency_fmt_sel_cb (GtkComboBox* format_selector, CsvImpTransAssist* info)
+{
+    info->preview_update_currency_format();
+}
+
+void csv_tximp_preview_treeview_resized_cb (GtkWidget* widget, GtkAllocation* allocation, CsvImpTransAssist* info)
+{
+    info->preview_resize_treeview();
+}
+
+void csv_tximp_preview_col_type_changed_cb (GtkCellRenderer* renderer, gchar* path,
+                                GtkTreeIter* new_text_iter, CsvImpTransAssist* info)
+{
+    info->preview_update_col_type (renderer, new_text_iter);
+}
+
+void
+csv_tximp_preview_header_clicked_cb (GtkWidget* button, GdkEventButton* event,
+                                        CsvImpTransAssist* info)
+{
+    info->preview_update_fw_columns(button, event);
+}
+
+
+void csv_tximp_acct_match_button_clicked_cb (GtkWidget *widget, CsvImpTransAssist* info)
+{
+    info->acct_match_via_button();
+}
+
+bool csv_tximp_acct_match_view_clicked_cb (GtkWidget *widget, GdkEventButton *event, CsvImpTransAssist* info)
+{
+    return info->acct_match_via_view_dblclick(event);
+}
+
+
+/*******************************************************
+ * Assistant Constructor
+ *******************************************************/
+CsvImpTransAssist::CsvImpTransAssist ()
+{
+    auto builder = gtk_builder_new();
+    gnc_builder_add_from_file  (builder , "assistant-csv-trans-import.glade", "start_row_adj");
+    gnc_builder_add_from_file  (builder , "assistant-csv-trans-import.glade", "end_row_adj");
+    gnc_builder_add_from_file  (builder , "assistant-csv-trans-import.glade", "account_match_store");
+    gnc_builder_add_from_file  (builder , "assistant-csv-trans-import.glade", "CSV Transaction Assistant");
+    csv_imp_asst = GTK_ASSISTANT(gtk_builder_get_object (builder, "CSV Transaction Assistant"));
+
+    /* Enable buttons on all page. */
+    gtk_assistant_set_page_complete (csv_imp_asst,
+                                     GTK_WIDGET(gtk_builder_get_object (builder, "start_page")),
+                                     true);
+    gtk_assistant_set_page_complete (csv_imp_asst,
+                                     GTK_WIDGET(gtk_builder_get_object (builder, "file_page")),
+                                     false);
+    gtk_assistant_set_page_complete (csv_imp_asst,
+                                     GTK_WIDGET(gtk_builder_get_object (builder, "preview_page")),
+                                     false);
+    gtk_assistant_set_page_complete (csv_imp_asst,
+                                     GTK_WIDGET(gtk_builder_get_object (builder, "account_match_page")),
+                                     false);
+    gtk_assistant_set_page_complete (csv_imp_asst,
+                                     GTK_WIDGET(gtk_builder_get_object (builder, "doc_page")),
+                                     true);
+    gtk_assistant_set_page_complete (csv_imp_asst,
+                                     GTK_WIDGET(gtk_builder_get_object (builder, "match_page")),
+                                     true);
+    gtk_assistant_set_page_complete (csv_imp_asst,
+                                     GTK_WIDGET(gtk_builder_get_object (builder, "summary_page")),
+                                     true);
+
+    /* File chooser Page */
+    file_page = GTK_WIDGET(gtk_builder_get_object (builder, "file_page"));
+    file_chooser = gtk_file_chooser_widget_new (GTK_FILE_CHOOSER_ACTION_OPEN);
+    g_signal_connect (G_OBJECT(file_chooser), "file-activated",
+                      G_CALLBACK(csv_tximp_file_confirm_cb), this);
+    auto button = gtk_button_new_from_stock (GTK_STOCK_OK);
+    gtk_widget_set_size_request (button, 100, -1);
+    gtk_widget_show (button);
+    auto h_box = gtk_hbox_new (TRUE, 0);
+    gtk_box_pack_start (GTK_BOX(h_box), button, FALSE, FALSE, 0);
+    gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER(file_chooser), h_box);
+    g_signal_connect (G_OBJECT(button), "clicked",
+                      G_CALLBACK(csv_tximp_file_confirm_cb), this);
+
+    auto box = GTK_WIDGET(gtk_builder_get_object (builder, "file_page"));
+    gtk_box_pack_start (GTK_BOX(box), file_chooser, TRUE, TRUE, 6);
+    gtk_widget_show (file_chooser);
+
+    /* Preview Settings Page */
+    {
+        preview_page = GTK_WIDGET(gtk_builder_get_object (builder, "preview_page"));
+
+        // Add Settings combo
+        auto settings_store = gtk_list_store_new (2, G_TYPE_POINTER, G_TYPE_STRING);
+        settings_combo = GTK_COMBO_BOX(gtk_combo_box_new_with_model_and_entry (GTK_TREE_MODEL(settings_store)));
+        gtk_combo_box_set_entry_text_column (GTK_COMBO_BOX(settings_combo), SET_NAME);
+        gtk_combo_box_set_active (GTK_COMBO_BOX(settings_combo), 0);
+
+        combo_hbox = GTK_WIDGET(gtk_builder_get_object (builder, "combo_hbox"));
+        gtk_box_pack_start (GTK_BOX(combo_hbox), GTK_WIDGET(settings_combo), true, true, 6);
+        gtk_widget_show (GTK_WIDGET(settings_combo));
+
+        g_signal_connect (G_OBJECT(settings_combo), "changed",
+                         G_CALLBACK(csv_tximp_preview_settings_sel_changed_cb), this);
+
+        // Additionally connect to the changed signal of the embedded GtkEntry
+        auto emb_entry = gtk_bin_get_child (GTK_BIN (settings_combo));
+        g_signal_connect (G_OBJECT(emb_entry), "changed",
+                         G_CALLBACK(csv_tximp_preview_settings_text_changed_cb), this);
+
+        // Add Save Settings button
+        save_button = GTK_WIDGET(gtk_builder_get_object (builder, "save_settings"));
+
+        // Add Delete Settings button
+        del_button = GTK_WIDGET(gtk_builder_get_object (builder, "delete_settings"));
+
+        /* The table containing the separator configuration widgets */
+        start_row_spin = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "start_row"));
+        end_row_spin = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "end_row"));
+        skip_alt_rows_button = GTK_WIDGET(gtk_builder_get_object (builder, "skip_rows"));
+        skip_errors_button = GTK_WIDGET(gtk_builder_get_object (builder, "skip_errors_button"));
+        multi_split_cbutton = GTK_WIDGET(gtk_builder_get_object (builder, "multi_split_button"));
+
+        /* Load the separator buttons from the glade builder file into the
+         * sep_buttons array. */
+        const char* sep_button_names[] = {
+                "space_cbutton",
+                "tab_cbutton",
+                "comma_cbutton",
+                "colon_cbutton",
+                "semicolon_cbutton",
+                "hyphen_cbutton"
+            };
+        for (int i = 0; i < SEP_NUM_OF_TYPES; i++)
+            sep_button[i]
+                = (GtkCheckButton*)GTK_WIDGET(gtk_builder_get_object (builder, sep_button_names[i]));
+
+        /* Load and connect the custom separator checkbutton in the same way
+         * as the other separator buttons. */
+        custom_cbutton
+            = (GtkCheckButton*)GTK_WIDGET(gtk_builder_get_object (builder, "custom_cbutton"));
+
+        /* Load the entry for the custom separator entry. Connect it to the
+         * sep_button_clicked event handler as well. */
+        custom_entry = (GtkEntry*)GTK_WIDGET(gtk_builder_get_object (builder, "custom_entry"));
+
+        /* Add account selection widget */
+        acct_selector = gnc_account_sel_new();
+        auto account_hbox = GTK_WIDGET(gtk_builder_get_object (builder, "account_hbox"));
+        gtk_box_pack_start (GTK_BOX(account_hbox), acct_selector, TRUE, TRUE, 6);
+        gtk_widget_show (acct_selector);
+
+        g_signal_connect(G_OBJECT(acct_selector), "account_sel_changed",
+                         G_CALLBACK(csv_tximp_preview_acct_sel_cb), this);
+
+
+        /* Create the encoding selector widget and add it to the assistant */
+        encselector = GO_CHARMAP_SEL(go_charmap_sel_new(GO_CHARMAP_SEL_TO_UTF8));
+        /* Connect the selector to the encoding_selected event handler. */
+        g_signal_connect (G_OBJECT(encselector), "charmap_changed",
+                         G_CALLBACK(csv_tximp_preview_enc_sel_cb), this);
+
+        auto encoding_container = GTK_CONTAINER(gtk_builder_get_object (builder, "encoding_container"));
+        gtk_container_add (encoding_container, GTK_WIDGET(encselector));
+        gtk_widget_show_all (GTK_WIDGET(encoding_container));
+
+        /* The instructions label and image */
+        instructions_label = GTK_LABEL(gtk_builder_get_object (builder, "instructions_label"));
+        instructions_image = GTK_IMAGE(gtk_builder_get_object (builder, "instructions_image"));
 
+        /* Add in the date format combo box and hook it up to an event handler. */
+        date_format_combo = GTK_COMBO_BOX_TEXT(gtk_combo_box_text_new());
+        for (int i = 0; i < num_date_formats; i++)
+        {
+            gtk_combo_box_text_append_text (date_format_combo, _(date_format_user[i]));
+        }
+        gtk_combo_box_set_active (GTK_COMBO_BOX(date_format_combo), 0);
+        g_signal_connect (G_OBJECT(date_format_combo), "changed",
+                         G_CALLBACK(csv_tximp_preview_date_fmt_sel_cb), this);
+
+        /* Add it to the assistant. */
+        auto date_format_container = GTK_CONTAINER(gtk_builder_get_object (builder, "date_format_container"));
+        gtk_container_add (date_format_container, GTK_WIDGET(date_format_combo));
+        gtk_widget_show_all (GTK_WIDGET(date_format_container));
+
+        /* Add in the currency format combo box and hook it up to an event handler. */
+        currency_format_combo = GTK_COMBO_BOX_TEXT(gtk_combo_box_text_new());
+        for (int i = 0; i < num_currency_formats; i++)
+        {
+            gtk_combo_box_text_append_text (currency_format_combo, _(currency_format_user[i]));
+        }
+        /* Default will the locale */
+        gtk_combo_box_set_active (GTK_COMBO_BOX(currency_format_combo), 0);
+        g_signal_connect (G_OBJECT(currency_format_combo), "changed",
+                         G_CALLBACK(csv_tximp_preview_currency_fmt_sel_cb), this);
+
+        /* Add it to the assistant. */
+        auto currency_format_container = GTK_CONTAINER(gtk_builder_get_object (builder, "currency_format_container"));
+        gtk_container_add (currency_format_container, GTK_WIDGET(currency_format_combo));
+        gtk_widget_show_all (GTK_WIDGET(currency_format_container));
+
+        /* Connect the CSV/Fixed-Width radio button event handler. */
+        csv_button = GTK_WIDGET(gtk_builder_get_object (builder, "csv_button"));
+        fixed_button = GTK_WIDGET(gtk_builder_get_object (builder, "fixed_button"));
+
+        /* Load the data treeview and connect it to its resizing event handler. */
+        treeview = (GtkTreeView*)GTK_WIDGET(gtk_builder_get_object (builder, "treeview"));
+
+        /* Load the column type treeview. */
+        ctreeview = (GtkTreeView*)GTK_WIDGET(gtk_builder_get_object (builder, "ctreeview"));
+
+        /* This is true only after encoding_selected is called, so we must
+         * set it initially to false. */
+        encoding_selected_called = false;
+    }
+
+    /* Account Match Page */
+    account_match_page  = GTK_WIDGET(gtk_builder_get_object (builder, "account_match_page"));
+    account_match_view  = GTK_WIDGET(gtk_builder_get_object (builder, "account_match_view"));
+    account_match_label = GTK_WIDGET(gtk_builder_get_object (builder, "account_match_label"));
+    account_match_btn = GTK_WIDGET(gtk_builder_get_object (builder, "account_match_change"));
+
+    /* Doc Page */
+    doc_page = GTK_WIDGET(gtk_builder_get_object (builder, "doc_page"));
+
+    /* Matcher page */
+    match_page  = GTK_WIDGET(gtk_builder_get_object (builder, "match_page"));
+    match_label = GTK_WIDGET(gtk_builder_get_object (builder, "match_label"));
+
+    /* Create the generic transaction importer GUI. */
+    gnc_csv_importer_gui = gnc_gen_trans_assist_new (match_page, nullptr, false, 42);
+
+    /* Summary Page */
+    summary_page  = GTK_WIDGET(gtk_builder_get_object (builder, "summary_page"));
+    summary_label = GTK_WIDGET(gtk_builder_get_object (builder, "summary_label"));
+
+    gnc_restore_window_size (GNC_PREFS_GROUP, GTK_WINDOW(csv_imp_asst));
+
+    gtk_builder_connect_signals (builder, this);
+    g_object_unref (G_OBJECT(builder));
+
+    gtk_widget_show_all (GTK_WIDGET(csv_imp_asst));
+    gnc_window_adjust_for_screen (GTK_WINDOW(csv_imp_asst));
+
+    /* In order to trigger a book options display on the creation of a new book,
+     * we need to detect when we are dealing with a new book. */
+    new_book = gnc_is_new_book();
+}
 
 /**************************************************
  * Code related to the file chooser page
@@ -187,21 +580,21 @@ bool csv_tximp_acct_match_view_clicked_cb (GtkWidget *widget, GdkEventButton *ev
  * call back for ok button in file chooser widget
  */
 void
-csv_tximp_file_confirm_cb (GtkWidget *button, CsvImpTransAssist *info)
+CsvImpTransAssist::file_confirm_cb ()
 {
-    gtk_assistant_set_page_complete (info->csv_imp_asst, info->account_match_page, false);
+    gtk_assistant_set_page_complete (csv_imp_asst, account_match_page, false);
 
-    auto file_name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(info->file_chooser));
+    auto file_name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_chooser));
     if (!file_name)
         return;
 
     auto filepath = gnc_uri_get_path (file_name);
     auto starting_dir = g_path_get_dirname (filepath);
 
-    info->file_name = file_name;
+    m_file_name = file_name;
     gnc_set_default_directory (GNC_PREFS_GROUP, starting_dir);
 
-    DEBUG("file_name selected is %s", info->file_name.c_str());
+    DEBUG("file_name selected is %s", m_file_name.c_str());
     DEBUG("starting directory is %s", starting_dir);
 
     g_free (filepath);
@@ -209,36 +602,36 @@ csv_tximp_file_confirm_cb (GtkWidget *button, CsvImpTransAssist *info)
     g_free (starting_dir);
 
     /* Load the file into parse_data. */
-    info->tx_imp = std::unique_ptr<GncTxImport>(new GncTxImport);
+    tx_imp = std::unique_ptr<GncTxImport>(new GncTxImport);
     /* Assume data is CSV. User can later override to Fixed Width if needed */
     try
     {
-        info->tx_imp->file_format (GncImpFileFormat::CSV);
-        info->tx_imp->load_file (info->file_name);
-        info->tx_imp->tokenize (true);
+        tx_imp->file_format (GncImpFileFormat::CSV);
+        tx_imp->load_file (m_file_name);
+        tx_imp->tokenize (true);
     }
     catch (std::ifstream::failure& e)
     {
         /* File loading failed ... */
-        gnc_error_dialog (GTK_WIDGET(info->csv_imp_asst), "%s", e.what());
+        gnc_error_dialog (GTK_WIDGET(csv_imp_asst), "%s", e.what());
         return;
     }
     catch (std::range_error &e)
     {
         /* Parsing failed ... */
-        gnc_error_dialog (GTK_WIDGET(info->csv_imp_asst), "%s", e.what());
+        gnc_error_dialog (GTK_WIDGET(csv_imp_asst), "%s", e.what());
         return;
     }
 
-    info->preview_refresh ();
+    preview_refresh ();
 
     /* Get settings store and populate */
-    info->preview_populate_settings_combo();
-    gtk_combo_box_set_active (info->settings_combo, 0);
+    preview_populate_settings_combo();
+    gtk_combo_box_set_active (settings_combo, 0);
 
-    gtk_assistant_set_page_complete (info->csv_imp_asst, info->account_match_page, true);
-    auto num = gtk_assistant_get_current_page (info->csv_imp_asst);
-    gtk_assistant_set_current_page (info->csv_imp_asst, num + 1);
+    gtk_assistant_set_page_complete (csv_imp_asst, account_match_page, true);
+    auto num = gtk_assistant_get_current_page (csv_imp_asst);
+    gtk_assistant_set_current_page (csv_imp_asst, num + 1);
 }
 
 
@@ -304,90 +697,86 @@ void CsvImpTransAssist::preview_handle_save_del_sensitivity (GtkComboBox* combo)
 
 }
 
+void
+CsvImpTransAssist::preview_settings_name (GtkEntry* entry)
+{
+    auto text = gtk_entry_get_text (entry);
+    if (text)
+        tx_imp->settings_name(text);
+
+    auto combo = gtk_widget_get_parent (GTK_WIDGET(entry));
+    preview_handle_save_del_sensitivity (GTK_COMBO_BOX(combo));
+}
+
 
 /* Use selected preset to configure the import. Triggered when
  * a preset is selected in the settings combo.
  */
 void
-csv_tximp_preview_settings_sel_changed_cb (GtkComboBox *combo, CsvImpTransAssist *info)
+CsvImpTransAssist::preview_settings_load ()
 {
     // Get the Active Selection
     GtkTreeIter iter;
-    if (!gtk_combo_box_get_active_iter (combo, &iter))
+    if (!gtk_combo_box_get_active_iter (settings_combo, &iter))
         return;
 
     CsvTransSettings *preset = nullptr;
-    auto model = gtk_combo_box_get_model (combo);
+    auto model = gtk_combo_box_get_model (settings_combo);
     gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1);
 
     if (!preset)
         return;
 
-    info->tx_imp->settings (*preset);
+    tx_imp->settings (*preset);
     if (preset->m_load_error)
-        gnc_error_dialog (GTK_WIDGET(info->csv_imp_asst),
+        gnc_error_dialog (GTK_WIDGET(csv_imp_asst),
             "%s", _("There were problems reading some saved settings, continuing to load.\n"
                     "Please review and save again."));
 
-    info->preview_refresh ();
-    info->preview_handle_save_del_sensitivity (combo);
-}
-
-
-/* Callback triggered while the user is editing text the settings combo.
- */
-void
-csv_tximp_preview_settings_text_changed_cb (GtkEntry *entry, CsvImpTransAssist *info)
-{
-    auto text = gtk_entry_get_text (entry);
-    if (text)
-        info->tx_imp->settings_name(text);
-
-    auto combo = gtk_widget_get_parent (GTK_WIDGET(entry));
-    info->preview_handle_save_del_sensitivity (GTK_COMBO_BOX(combo));
+    preview_refresh ();
+    preview_handle_save_del_sensitivity (settings_combo);
 }
 
-
 /* Callback to delete a settings entry
  */
 void
-csv_tximp_preview_del_settings_cb (GtkWidget *button, CsvImpTransAssist *info)
+CsvImpTransAssist::preview_settings_delete ()
 {
     // Get the Active Selection
     GtkTreeIter iter;
-    if (!gtk_combo_box_get_active_iter (info->settings_combo, &iter))
+    if (!gtk_combo_box_get_active_iter (settings_combo, &iter))
         return;
 
     CsvTransSettings *preset = nullptr;
-    auto model = gtk_combo_box_get_model (info->settings_combo);
+    auto model = gtk_combo_box_get_model (settings_combo);
     gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1);
 
-    auto response = gnc_ok_cancel_dialog (GTK_WIDGET(info->csv_imp_asst),
+    auto response = gnc_ok_cancel_dialog (GTK_WIDGET(csv_imp_asst),
                                 GTK_RESPONSE_CANCEL,
                                 "%s", _("Delete the Import Settings."));
     if (response == GTK_RESPONSE_OK)
     {
         preset->remove();
-        info->preview_populate_settings_combo();
-        gtk_combo_box_set_active (info->settings_combo, 0); // Default
-        info->preview_refresh (); // Reset the widgets
+        preview_populate_settings_combo();
+        gtk_combo_box_set_active (settings_combo, 0); // Default
+        preview_refresh (); // Reset the widgets
     }
 }
 
 /* Callback to save the current settings to the gnucash state file.
  */
 void
-csv_tximp_preview_save_settings_cb (GtkWidget *button, CsvImpTransAssist *info)
+CsvImpTransAssist::preview_settings_save ()
 {
     auto title = _("Save the Import Settings.");
-    auto new_name = info->tx_imp->settings_name();
+    auto new_name = tx_imp->settings_name();
 
     /* Check if the entry text matches an already existing preset */
     GtkTreeIter iter;
-    if (!gtk_combo_box_get_active_iter (info->settings_combo, &iter))
+    if (!gtk_combo_box_get_active_iter (settings_combo, &iter))
     {
 
-        auto model = gtk_combo_box_get_model (info->settings_combo);
+        auto model = gtk_combo_box_get_model (settings_combo);
         bool valid = gtk_tree_model_get_iter_first (model, &iter);
         while (valid)
         {
@@ -397,7 +786,7 @@ csv_tximp_preview_save_settings_cb (GtkWidget *button, CsvImpTransAssist *info)
 
             if (preset && (preset->m_name == std::string(new_name)))
             {
-                auto response = gnc_ok_cancel_dialog (GTK_WIDGET(info->csv_imp_asst),
+                auto response = gnc_ok_cancel_dialog (GTK_WIDGET(csv_imp_asst),
                         GTK_RESPONSE_OK,
                         "%s", _("Setting name already exists, over write?"));
                 if (response != GTK_RESPONSE_OK)
@@ -410,14 +799,14 @@ csv_tximp_preview_save_settings_cb (GtkWidget *button, CsvImpTransAssist *info)
     }
 
     /* All checks passed, let's save this preset */
-    if (!info->tx_imp->save_settings())
+    if (!tx_imp->save_settings())
     {
-        gnc_info_dialog (GTK_WIDGET(info->csv_imp_asst),
+        gnc_info_dialog (GTK_WIDGET(csv_imp_asst),
             "%s", _("The settings have been saved."));
 
         // Update the settings store
-        info->preview_populate_settings_combo();
-        auto model = gtk_combo_box_get_model (info->settings_combo);
+        preview_populate_settings_combo();
+        auto model = gtk_combo_box_get_model (settings_combo);
 
         // Get the first entry in model
         GtkTreeIter   iter;
@@ -429,7 +818,7 @@ csv_tximp_preview_save_settings_cb (GtkWidget *button, CsvImpTransAssist *info)
             gtk_tree_model_get (model, &iter, SET_NAME, &name, -1);
 
             if (g_strcmp0 (name, new_name.c_str()) == 0) // Set Active, the one Saved.
-                gtk_combo_box_set_active_iter (info->settings_combo, &iter);
+                gtk_combo_box_set_active_iter (settings_combo, &iter);
 
             g_free (name);
 
@@ -437,79 +826,36 @@ csv_tximp_preview_save_settings_cb (GtkWidget *button, CsvImpTransAssist *info)
         }
     }
     else
-        gnc_error_dialog (GTK_WIDGET(info->csv_imp_asst),
+        gnc_error_dialog (GTK_WIDGET(csv_imp_asst),
             "%s", _("There was a problem saving the settings, please try again."));
-}/* Callback triggered when user adjusts skip start lines
- */
-void csv_tximp_preview_srow_cb (GtkSpinButton *spin, CsvImpTransAssist *info)
-{
-
-    /* Get number of lines to skip at the beginning */
-    info->tx_imp->skip_start_lines (gtk_spin_button_get_value_as_int (spin));
-
-    /* And adjust maximum number of lines that can be skipped at the end accordingly */
-    auto adj = gtk_spin_button_get_adjustment (info->end_row_spin);
-    gtk_adjustment_set_upper (adj, info->tx_imp->m_parsed_lines.size()
-            - info->tx_imp->skip_start_lines());
-
-    info->preview_refresh_table ();
 }
 
-
-/* Callback triggered when user adjusts skip end lines
+/* Callback triggered when user adjusts skip start lines
  */
-void csv_tximp_preview_erow_cb (GtkSpinButton *spin, CsvImpTransAssist *info)
+void CsvImpTransAssist::preview_update_skipped_rows ()
 {
-    /* Get number of lines to skip at the end */
-    info->tx_imp->skip_end_lines (gtk_spin_button_get_value_as_int (spin));
-
-    /* And adjust maximum number of lines that can be skipped at the beginning accordingly */
-    auto adj = gtk_spin_button_get_adjustment (info->start_row_spin);
-    gtk_adjustment_set_upper (adj, info->tx_imp->m_parsed_lines.size()
-            - info->tx_imp->skip_end_lines());
+    /* Update skip rows in the parser */
+    tx_imp->update_skipped_lines (gtk_spin_button_get_value_as_int (start_row_spin),
+        gtk_spin_button_get_value_as_int (end_row_spin),
+        gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(skip_alt_rows_button)),
+        gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(skip_errors_button)));
 
-    info->preview_refresh_table ();
-}
-
-
-/* Callback triggered when user clicks the skip errors button
- */
-void csv_tximp_preview_skiperrors_cb (GtkToggleButton *checkbox, CsvImpTransAssist *info)
-{
-    info->tx_imp->skip_errors(gtk_toggle_button_get_active (checkbox));
-    info->preview_refresh_table ();
-}
-
-
-/* Callback triggered when user clicks the multi split button
- */
-void csv_tximp_preview_multisplit_cb (GtkToggleButton *checkbox, CsvImpTransAssist *info)
-{
-    info->tx_imp->multi_split (gtk_toggle_button_get_active (checkbox));
-    info->preview_refresh_table ();
-}
+    /* And adjust maximum number of lines that can be skipped at each end accordingly */
+    auto adj = gtk_spin_button_get_adjustment (end_row_spin);
+    gtk_adjustment_set_upper (adj, tx_imp->m_parsed_lines.size()
+            - tx_imp->skip_start_lines() -1);
 
+    adj = gtk_spin_button_get_adjustment (start_row_spin);
+    gtk_adjustment_set_upper (adj, tx_imp->m_parsed_lines.size()
+            - tx_imp->skip_end_lines() - 1);
 
-/* Callback triggered when user clicks the skip alternating lines button
- */
-void csv_tximp_preview_skiprows_cb (GtkToggleButton *checkbox, CsvImpTransAssist *info)
-{
-    info->tx_imp->skip_alt_lines (gtk_toggle_button_get_active (checkbox));
-    info->preview_refresh_table ();
+    preview_refresh_table ();
 }
 
-
-/** Returns the cell renderer from a column in the preview's treeview.
- * @param info The display of the data being imported
- * @param col The number of the column whose cell renderer is being retrieved
- * @return The cell renderer of column number col
- */
-static GtkCellRenderer* gnc_csv_preview_get_cell_renderer (CsvImpTransAssist* info, int col)
+void CsvImpTransAssist::preview_multi_split (bool multi)
 {
-    auto renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT(gtk_tree_view_get_column (info->treeview, col)));
-    auto cell = GTK_CELL_RENDERER(renderers->data);
-    g_list_free (renderers);
-    return cell;
+    tx_imp->multi_split(multi);
+    preview_refresh ();
 }
 
 
@@ -520,12 +866,12 @@ static GtkCellRenderer* gnc_csv_preview_get_cell_renderer (CsvImpTransAssist* in
  * @param widget The widget that was changed
  * @param info The data that is being configured
  */
-void csv_tximp_preview_sep_button_cb (GtkWidget* widget, CsvImpTransAssist* info)
+void CsvImpTransAssist::preview_update_separators (GtkWidget* widget)
 {
 
     /* Only manipulate separator characters if the currently open file is
      * csv separated. */
-    if (info->tx_imp->file_format() != GncImpFileFormat::CSV)
+    if (tx_imp->file_format() != GncImpFileFormat::CSV)
         return;
 
     /* Add the corresponding characters to checked_separators for each
@@ -534,41 +880,41 @@ void csv_tximp_preview_sep_button_cb (GtkWidget* widget, CsvImpTransAssist* info
     const auto stock_sep_chars = std::string (" \t,:;-");
     for (int i = 0; i < SEP_NUM_OF_TYPES; i++)
     {
-        if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(info->sep_button[i])))
+        if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(sep_button[i])))
             checked_separators += stock_sep_chars[i];
     }
 
     /* Add the custom separator if the user checked its button. */
-    if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(info->custom_cbutton)))
+    if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(custom_cbutton)))
     {
-        auto custom_sep = gtk_entry_get_text (info->custom_entry);
+        auto custom_sep = gtk_entry_get_text (custom_entry);
         if (custom_sep[0] != '\0') /* Don't add a blank separator (bad things will happen!). */
             checked_separators += custom_sep;
     }
 
     /* Set the parse options using the checked_separators list. */
-    info->tx_imp->separators (checked_separators);
+    tx_imp->separators (checked_separators);
 
     /* Parse the data using the new options. We don't want to reguess
      * the column types because we want to leave the user's
      * configurations intact. */
     try
     {
-        info->tx_imp->tokenize (false);
-        info->preview_refresh_table ();
+        tx_imp->tokenize (false);
+        preview_refresh_table ();
     }
     catch (std::range_error &e)
     {
         /* Warn the user there was a problem and try to undo what caused
          * the error. (This will cause a reparsing and ideally a usable
          * configuration.) */
-        gnc_error_dialog (nullptr, "Error in parsing");
+        gnc_error_dialog (GTK_WIDGET(csv_imp_asst), "Error in parsing");
         /* If we're here because the user changed the file format, we should just wait for the user
          * to update the configuration */
         if (!widget)
             return;
         /* If the user changed the custom separator, erase that custom separator. */
-        if (widget == GTK_WIDGET(info->custom_entry))
+        if (widget == GTK_WIDGET(custom_entry))
             gtk_entry_set_text (GTK_ENTRY(widget), "");
         /* If the user checked a checkbutton, toggle that checkbutton back. */
         else
@@ -578,34 +924,24 @@ void csv_tximp_preview_sep_button_cb (GtkWidget* widget, CsvImpTransAssist* info
     }
 }
 
-/* Callback triggered when user selects an account in the account selection
- */
-void csv_tximp_preview_acct_sel_cb (GtkWidget* widget, CsvImpTransAssist* info)
-{
-
-    auto acct = gnc_account_sel_get_account( GNC_ACCOUNT_SEL(widget) );
-    info->tx_imp->base_account(acct);
-    info->preview_refresh_table ();
-}
-
 
 /** Event handler for clicking one of the format type radio
  * buttons. This occurs if the format (Fixed-Width or CSV) is changed.
  * @param csv_button The "Separated" radio button
  * @param info The display of the data being imported
  */
-void csv_tximp_preview_sep_fixed_sel_cb (GtkToggleButton* csv_button, CsvImpTransAssist* info)
+void CsvImpTransAssist::preview_update_file_format ()
 {
     /* Set the parsing type correctly. */
     try
     {
-        if (gtk_toggle_button_get_active (csv_button))
-            info->tx_imp->file_format (GncImpFileFormat::CSV);
+        if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(csv_button)))
+            tx_imp->file_format (GncImpFileFormat::CSV);
         else
-            info->tx_imp->file_format (GncImpFileFormat::FIXED_WIDTH);
+            tx_imp->file_format (GncImpFileFormat::FIXED_WIDTH);
 
-        info->tx_imp->tokenize (false);
-        info->preview_refresh_table ();
+        tx_imp->tokenize (false);
+        preview_refresh_table ();
     }
     catch (std::range_error &e)
     {
@@ -621,94 +957,86 @@ void csv_tximp_preview_sep_fixed_sel_cb (GtkToggleButton* csv_button, CsvImpTran
 }
 
 
+void CsvImpTransAssist::preview_update_account ()
+{;
+    auto acct = gnc_account_sel_get_account( GNC_ACCOUNT_SEL(acct_selector) );
+    tx_imp->base_account(acct);
+    preview_refresh_table ();
+}
+
+
 /** Event handler for a new encoding. This is called when the user
  * selects a new encoding; the data is reparsed and shown to the
  * user.
  * @param selector The widget the user uses to select a new encoding
  * @param encoding The encoding that the user selected
- * @param info The display of the data being imported
  */
-void csv_tximp_preview_enc_sel_cb (GOCharmapSel* selector, const char* encoding,
-                              CsvImpTransAssist* info)
+void
+CsvImpTransAssist::preview_update_encoding (const char* encoding)
 {
     /* This gets called twice every time a new encoding is selected. The
      * second call actually passes the correct data; thus, we only do
      * something the second time this is called. */
 
     /* If this is the second time the function is called ... */
-    if (info->encoding_selected_called)
+    if (encoding_selected_called)
     {
-        std::string previous_encoding = info->tx_imp->m_tokenizer->encoding();
+        std::string previous_encoding = tx_imp->m_tokenizer->encoding();
         /* Try converting the new encoding and reparsing. */
         try
         {
-            info->tx_imp->encoding (encoding);
-            info->tx_imp->tokenize (false);
-
-            info->preview_refresh_table ();
-
-            info->encoding_selected_called = false;
+            tx_imp->encoding (encoding);
+            preview_refresh_table ();
         }
         catch (...)
         {
             /* If it fails, change back to the old encoding. */
             gnc_error_dialog (nullptr, "%s", _("Invalid encoding selected"));
-            info->encoding_selected_called = false;
-            go_charmap_sel_set_encoding (selector, previous_encoding.c_str());
+            go_charmap_sel_set_encoding (encselector, previous_encoding.c_str());
         }
     }
-    else /* If this is the first call of the function ... */
-    {
-        info->encoding_selected_called = true; /* ... set the flag and wait for the next call. */
-    }
+
+    encoding_selected_called = !encoding_selected_called;
 }
 
 
-/** Event handler for selecting a new date format.
- * @param format_selector The combo box for selecting date formats
- * @param info The display of the data being imported
- */
-static void csv_tximp_preview_date_fmt_sel_cb (GtkComboBox* format_selector, CsvImpTransAssist* info)
+void
+CsvImpTransAssist::preview_update_date_format ()
 {
-    info->tx_imp->date_format (gtk_combo_box_get_active (format_selector));
-    info->preview_refresh_table ();
+    tx_imp->date_format (gtk_combo_box_get_active (GTK_COMBO_BOX(date_format_combo)));
+    preview_refresh_table ();
 }
 
 
-/** Event handler for selecting a new currency format.
- * @param currency_selector The combo box for selecting currency formats
- * @param info The display of the data being imported
- */
-static void csv_tximp_preview_currency_fmt_sel_cb (GtkComboBox* currency_selector, CsvImpTransAssist* info)
+void
+CsvImpTransAssist::preview_update_currency_format ()
 {
-    info->tx_imp->currency_format (gtk_combo_box_get_active (currency_selector));
-    info->preview_refresh_table ();
+    tx_imp->currency_format (gtk_combo_box_get_active (GTK_COMBO_BOX(currency_format_combo)));
+    preview_refresh_table ();
 }
 
 
 /** Event handler for the data treeview being resized. When the data
  * treeview is resized, the column types treeview's columns are also resized to
  * match.
- * @param widget The data treeview
- * @param allocation The size of the data treeview
- * @param info The display of the data being imported
  */
-static void csv_tximp_preview_treeview_resized_cb (GtkWidget* widget, GtkAllocation* allocation, CsvImpTransAssist* info)
+void
+CsvImpTransAssist::preview_resize_treeview ()
 {
     /* Go through each column except for the last. (We don't want to set
      * the width of the last column because the user won't be able to
      * shrink the dialog back if it's expanded.) */
-    for (uint i = 0; i < info->tx_imp->column_types().size() - 1; i++)
+    for (uint i = 0; i < tx_imp->column_types().size() - 1; i++)
     {
         /* Get the width. */
-        auto col_width = gtk_tree_view_column_get_width (gtk_tree_view_get_column (info->treeview, i));
+        auto col_width = gtk_tree_view_column_get_width (gtk_tree_view_get_column (treeview, i));
         /* Set the minimum width for a column so that drop down selector can be seen. */
         if (col_width < MIN_COL_WIDTH)
             col_width = MIN_COL_WIDTH;
-        auto pcol = gtk_tree_view_get_column (info->treeview, i);
+        auto pcol = gtk_tree_view_get_column (treeview, i);
         gtk_tree_view_column_set_min_width (pcol, col_width);
         /* Set ccol's width the same. */
-        auto ccol = gtk_tree_view_get_column (info->ctreeview, i);
+        auto ccol = gtk_tree_view_get_column (ctreeview, i);
         gtk_tree_view_column_set_min_width (ccol, col_width);
         gtk_tree_view_column_set_max_width (ccol, col_width);
     }
@@ -724,8 +1052,8 @@ static void csv_tximp_preview_treeview_resized_cb (GtkWidget* widget, GtkAllocat
  * @param new_text The text the user selected
  * @param info The display of the data being imported
  */
-static void csv_tximp_preview_col_type_changed_cb (GtkCellRenderer* renderer, gchar* path,
-                                GtkTreeIter* new_text_iter, CsvImpTransAssist* info)
+void CsvImpTransAssist::preview_update_col_type (GtkCellRenderer* renderer,
+                                GtkTreeIter* new_text_iter)
 {
     /* Get the new text */
     auto col_num = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT(renderer), "col-num"));
@@ -736,8 +1064,8 @@ static void csv_tximp_preview_col_type_changed_cb (GtkCellRenderer* renderer, gc
             1, &new_col_type, // Invisible column in the combobox' model containing the column type
             -1);
 
-    info->tx_imp->set_column_type (col_num, new_col_type);
-    info->preview_refresh_table ();
+    tx_imp->set_column_type (col_num, new_col_type);
+    preview_refresh_table ();
 }
 
 /*======================================================================*/
@@ -803,13 +1131,15 @@ static GnumericPopupMenuElement const popup_elements[] =
     { nullptr, nullptr, 0, 0, 0 },
 };
 
-static uint get_new_col_rel_pos (CsvImpTransAssist* info, int col, int dx)
+uint CsvImpTransAssist::get_new_col_rel_pos (int col, int dx)
 {
-    auto cell = gnc_csv_preview_get_cell_renderer (info, col);
+    auto renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT(gtk_tree_view_get_column (treeview, col)));
+    auto cell = GTK_CELL_RENDERER(renderers->data);
+    g_list_free (renderers);
     PangoFontDescription *font_desc;
     g_object_get (G_OBJECT(cell), "font_desc", &font_desc, nullptr);
 
-    PangoLayout *layout = gtk_widget_create_pango_layout (GTK_WIDGET(info->treeview), "x");
+    PangoLayout *layout = gtk_widget_create_pango_layout (GTK_WIDGET(treeview), "x");
     pango_layout_set_font_description (layout, font_desc);
     int width;
     pango_layout_get_pixel_size (layout, &width, nullptr);
@@ -821,13 +1151,13 @@ static uint get_new_col_rel_pos (CsvImpTransAssist* info, int col, int dx)
     return charindex;
 }
 
-static gboolean
+gboolean
 fixed_context_menu_handler (GnumericPopupMenuElement const *element,
         gpointer userdata)
 {
     auto info = (CsvImpTransAssist*)userdata;
     auto col = info->fixed_context_col;
-    auto rel_pos = get_new_col_rel_pos (info, col, info->fixed_context_dx);
+    auto rel_pos = info->get_new_col_rel_pos (col, info->fixed_context_dx);
     auto fwtok = dynamic_cast<GncFwTokenizer*>(info->tx_imp->m_tokenizer.get());
 
     switch (element->index)
@@ -863,24 +1193,15 @@ fixed_context_menu_handler (GnumericPopupMenuElement const *element,
     info->preview_refresh_table ();
     return true;
 }
-static void
-select_column (CsvImpTransAssist* info, int col)
-{
-    if (col < 0)
-        return;
-
-    auto column = gtk_tree_view_get_column (info->treeview, col);
-    gtk_widget_grab_focus (gtk_tree_view_column_get_widget(column));
-}
 
-static void
-fixed_context_menu (CsvImpTransAssist* info, GdkEventButton *event,
+void
+CsvImpTransAssist::fixed_context_menu (GdkEventButton *event,
                     int col, int dx)
 {
-    auto fwtok = dynamic_cast<GncFwTokenizer*>(info->tx_imp->m_tokenizer.get());
-    info->fixed_context_col = col;
-    info->fixed_context_dx = dx;
-    uint rel_pos = get_new_col_rel_pos (info, col, dx);
+    auto fwtok = dynamic_cast<GncFwTokenizer*>(tx_imp->m_tokenizer.get());
+    fixed_context_col = col;
+    fixed_context_dx = dx;
+    uint rel_pos = get_new_col_rel_pos (col, dx);
 
     int sensitivity_filter = 0;
     if (!fwtok->col_can_delete (col - 1))
@@ -894,9 +1215,14 @@ fixed_context_menu (CsvImpTransAssist* info, GdkEventButton *event,
     if (!fwtok->col_can_narrow (col))
         sensitivity_filter |= (1 << CONTEXT_STF_IMPORT_NARROW);
 
-    select_column (info, col);
+    if (col >= 0)
+    {
+        auto column = gtk_tree_view_get_column (treeview, col);
+        gtk_widget_grab_focus (gtk_tree_view_column_get_widget(column));
+    }
+
     gnumeric_create_popup_menu (popup_elements, &fixed_context_menu_handler,
-                                info, 0,
+                                this, 0,
                                 sensitivity_filter, event);
 }
 
@@ -906,7 +1232,7 @@ fixed_context_menu (CsvImpTransAssist* info, GdkEventButton *event,
 void
 CsvImpTransAssist::preview_split_column (int col, int dx)
 {
-    auto rel_pos = get_new_col_rel_pos (this, col, dx);
+    auto rel_pos = get_new_col_rel_pos (col, dx);
     auto fwtok = dynamic_cast<GncFwTokenizer*>(tx_imp->m_tokenizer.get());
     fwtok->col_split (col, rel_pos);
     try
@@ -929,15 +1255,14 @@ CsvImpTransAssist::preview_split_column (int col, int dx)
  * @param event The event that happened (where the user clicked)
  * @param info The data being configured
  */
-static void
-csv_tximp_preview_header_clicked_cb (GtkWidget* button, GdkEventButton* event,
-                                        CsvImpTransAssist* info)
+void
+CsvImpTransAssist::preview_update_fw_columns (GtkWidget* button, GdkEventButton* event)
 {
     /* Find the column that was clicked. */
     auto col = GPOINTER_TO_UINT(g_object_get_data (G_OBJECT(button), "col-num"));
 
     /* Don't let the user affect the last column if it has error messages. */
-    if (col == info->tx_imp->column_types().size())
+    if (col == tx_imp->column_types().size())
         return;
 
     /*  calculate offset to compensate for the button indentation. */
@@ -946,10 +1271,10 @@ csv_tximp_preview_header_clicked_cb (GtkWidget* button, GdkEventButton* event,
     auto offset = alloc.x - alloc.x;
     if (event->type == GDK_2BUTTON_PRESS && event->button == 1)
         /* Double clicks can split columns. */
-        info->preview_split_column (col, (int)event->x - offset);
+        preview_split_column (col, (int)event->x - offset);
     else if (event->type == GDK_BUTTON_PRESS && event->button == 3)
         /* Right clicking brings up a context menu. */
-        fixed_context_menu (info, event, col, (int)event->x - offset);
+        fixed_context_menu (event, col, (int)event->x - offset);
 }
 
 
@@ -984,7 +1309,12 @@ void CsvImpTransAssist::preview_row_sel_update ()
  */
 void CsvImpTransAssist::preview_refresh_table ()
 {
+    auto save_skip_errors = tx_imp->skip_err_lines();
+    tx_imp->update_skipped_lines (boost::none, boost::none,
+        boost::none, false);
     preview_validate_settings ();
+    tx_imp->update_skipped_lines (boost::none, boost::none,
+        boost::none, save_skip_errors);
 
     /* ncols is the number of columns in the file data. */
     auto column_types = tx_imp->column_types();
@@ -1205,6 +1535,7 @@ CsvImpTransAssist::preview_refresh ()
 
     gnc_account_sel_set_account(GNC_ACCOUNT_SEL(acct_selector),
             tx_imp->base_account() , false);
+    gtk_widget_set_sensitive (acct_selector, !tx_imp->multi_split());
 
     // Handle separator checkboxes and custom field, only relevant if the file format is csv
     if (tx_imp->file_format() == GncImpFileFormat::CSV)
@@ -1368,39 +1699,39 @@ CsvImpTransAssist::acct_match_select(GtkTreeModel *model, GtkTreeIter* iter)
 }
 
 void
-csv_tximp_acct_match_button_clicked_cb (GtkWidget *widget, CsvImpTransAssist* info)
+CsvImpTransAssist::acct_match_via_button ()
 {
-    auto model = gtk_tree_view_get_model (GTK_TREE_VIEW(info->account_match_view));
-    auto selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(info->account_match_view));
+    auto model = gtk_tree_view_get_model (GTK_TREE_VIEW(account_match_view));
+    auto selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(account_match_view));
 
     GtkTreeIter iter;
     if (gtk_tree_selection_get_selected (selection, &model, &iter))
-        info->acct_match_select (model, &iter);
+        acct_match_select (model, &iter);
 }
 
 
 /* This is the callback for the mouse click */
 bool
-csv_tximp_acct_match_view_clicked_cb (GtkWidget *widget, GdkEventButton *event, CsvImpTransAssist* info)
+CsvImpTransAssist::acct_match_via_view_dblclick (GdkEventButton *event)
 {
     /* This is for a double click */
     if (event->button == 1 && event->type == GDK_2BUTTON_PRESS)
     {
-        auto window = gtk_tree_view_get_bin_window (GTK_TREE_VIEW (info->account_match_view));
+        auto window = gtk_tree_view_get_bin_window (GTK_TREE_VIEW (account_match_view));
         if (event->window != window)
             return false;
 
         /* Get tree path for row that was clicked, true if row exists */
         GtkTreePath *path;
-        if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (info->account_match_view), (gint) event->x, (gint) event->y,
+        if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (account_match_view), (gint) event->x, (gint) event->y,
                                              &path, nullptr, nullptr, nullptr))
         {
             DEBUG("event->x is %d and event->y is %d", (gint)event->x, (gint)event->y);
 
-            auto model = gtk_tree_view_get_model (GTK_TREE_VIEW(info->account_match_view));
+            auto model = gtk_tree_view_get_model (GTK_TREE_VIEW(account_match_view));
             GtkTreeIter iter;
             if (gtk_tree_model_get_iter (model, &iter, path))
-                info->acct_match_select (model, &iter);
+                acct_match_select (model, &iter);
             gtk_tree_path_free (path);
         }
         return true;
@@ -1514,27 +1845,21 @@ CsvImpTransAssist::assist_match_page_prepare ()
     text += "</b></span>";
     gtk_label_set_markup (GTK_LABEL(match_label), text.c_str());
 
-    if (!gnc_csv_importer_gui)
+    /* Add the help button for the matcher */
+    help_button = gtk_button_new_with_mnemonic (_("_Help"));
+    gtk_assistant_add_action_widget (csv_imp_asst, help_button);
+    g_signal_connect (help_button, "clicked",
+                     G_CALLBACK(on_matcher_help_clicked), gnc_csv_importer_gui);
+    gtk_widget_show (GTK_WIDGET(help_button));
+
+    /* Copy all of the transactions to the importer GUI. */
+    for (auto trans_it : tx_imp->m_transactions)
     {
-        /* Create the generic transaction importer GUI. */
-        gnc_csv_importer_gui = gnc_gen_trans_assist_new (match_page, nullptr, false, 42);
-
-        /* Add the help button for the matcher */
-        help_button = gtk_button_new_with_mnemonic (_("_Help"));
-        gtk_assistant_add_action_widget (csv_imp_asst, help_button);
-        g_signal_connect (help_button, "clicked",
-                         G_CALLBACK(on_matcher_help_clicked), gnc_csv_importer_gui);
-        gtk_widget_show (GTK_WIDGET(help_button));
-
-        /* Copy all of the transactions to the importer GUI. */
-        for (auto trans_it : tx_imp->m_transactions)
+        auto draft_trans = trans_it.second;
+        if (draft_trans->trans)
         {
-            auto draft_trans = trans_it.second;
-            if (draft_trans->trans)
-            {
-                gnc_gen_trans_list_add_trans (gnc_csv_importer_gui, draft_trans->trans);
-                draft_trans->trans = nullptr;
-            }
+            gnc_gen_trans_list_add_trans (gnc_csv_importer_gui, draft_trans->trans);
+            draft_trans->trans = nullptr;
         }
     }
 }
@@ -1548,282 +1873,56 @@ CsvImpTransAssist::assist_summary_page_prepare ()
     gtk_assistant_remove_action_widget (csv_imp_asst, cancel_button);
 
     auto text = std::string("<span size=\"medium\"><b>");
-    text += _("The transactions were imported from the file '") + file_name + "'.";
+    text += _("The transactions were imported from the file '") + m_file_name + "'.";
     text += "</b></span>";
     gtk_label_set_markup (GTK_LABEL(summary_label), text.c_str());
 }
 
 
 void
-csv_tximp_assist_prepare_cb (GtkAssistant *assistant, GtkWidget *page,
-        CsvImpTransAssist* info)
+CsvImpTransAssist::assist_prepare_cb (GtkWidget *page)
 {
-    if (page == info->file_page)
-        info->assist_file_page_prepare ();
-    else if (page == info->preview_page)
-        info->assist_preview_page_prepare ();
-    else if (page == info->account_match_page)
-        info->assist_account_match_page_prepare ();
-    else if (page == info->doc_page)
-        info->assist_doc_page_prepare ();
-    else if (page == info->match_page)
-        info->assist_match_page_prepare ();
-    else if (page == info->summary_page)
-        info->assist_summary_page_prepare ();
+    if (page == file_page)
+        assist_file_page_prepare ();
+    else if (page == preview_page)
+        assist_preview_page_prepare ();
+    else if (page == account_match_page)
+        assist_account_match_page_prepare ();
+    else if (page == doc_page)
+        assist_doc_page_prepare ();
+    else if (page == match_page)
+        assist_match_page_prepare ();
+    else if (page == summary_page)
+        assist_summary_page_prepare ();
 }
 
 
-/*******************************************************
- * Assistant call back functions
- *******************************************************/
 void
-csv_tximp_assist_destroy_cb (GtkWidget *object, CsvImpTransAssist* info)
+CsvImpTransAssist::assist_finish (bool canceled)
 {
-    gnc_unregister_gui_component_by_data (ASSISTANT_CSV_IMPORT_TRANS_CM_CLASS, info);
-    delete info;
+    /* Start the import */
+    if (canceled || tx_imp->m_transactions.empty())
+        gnc_gen_trans_list_delete (gnc_csv_importer_gui);
+    else
+        gnc_gen_trans_assist_start (gnc_csv_importer_gui);
 }
 
-void
-csv_tximp_assist_cancel_cb (GtkAssistant *assistant, CsvImpTransAssist* info)
-{
-    if (info->gnc_csv_importer_gui)
-        gnc_gen_trans_list_delete (info->gnc_csv_importer_gui);
-
-    gnc_close_gui_component_by_data (ASSISTANT_CSV_IMPORT_TRANS_CM_CLASS, info);
-}
 
 void
-csv_tximp_assist_close_cb (GtkAssistant *assistant, CsvImpTransAssist* info)
+CsvImpTransAssist::assist_compmgr_close ()
 {
-    gnc_close_gui_component_by_data (ASSISTANT_CSV_IMPORT_TRANS_CM_CLASS, info);
+    gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(csv_imp_asst));
+    gtk_widget_destroy (GTK_WIDGET(csv_imp_asst));
 }
 
-void
-csv_tximp_assist_finish_cb (GtkAssistant *assistant, CsvImpTransAssist* info)
-{
-    /* Start the import */
-    if (!info->tx_imp->m_transactions.empty())
-        gnc_gen_trans_assist_start (info->gnc_csv_importer_gui);
-    else
-        gnc_gen_trans_list_delete (info->gnc_csv_importer_gui);
-}
 
 static void
 csv_tximp_close_handler (gpointer user_data)
 {
     auto info = (CsvImpTransAssist*)user_data;
-
-    if (info->gnc_csv_importer_gui)
-        info->gnc_csv_importer_gui = nullptr;
-
-    gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(info->csv_imp_asst));
-    gtk_widget_destroy (GTK_WIDGET(info->csv_imp_asst));
-}
-
-/*******************************************************
- * Assistant Constructor
- *******************************************************/
-CsvImpTransAssist::CsvImpTransAssist ()
-{
-    auto builder = gtk_builder_new();
-    gnc_builder_add_from_file  (builder , "assistant-csv-trans-import.glade", "start_row_adj");
-    gnc_builder_add_from_file  (builder , "assistant-csv-trans-import.glade", "end_row_adj");
-    gnc_builder_add_from_file  (builder , "assistant-csv-trans-import.glade", "account_match_store");
-    gnc_builder_add_from_file  (builder , "assistant-csv-trans-import.glade", "CSV Transaction Assistant");
-    csv_imp_asst = GTK_ASSISTANT(gtk_builder_get_object (builder, "CSV Transaction Assistant"));
-
-    /* Enable buttons on all page. */
-    gtk_assistant_set_page_complete (csv_imp_asst,
-                                     GTK_WIDGET(gtk_builder_get_object (builder, "start_page")),
-                                     true);
-    gtk_assistant_set_page_complete (csv_imp_asst,
-                                     GTK_WIDGET(gtk_builder_get_object (builder, "file_page")),
-                                     false);
-    gtk_assistant_set_page_complete (csv_imp_asst,
-                                     GTK_WIDGET(gtk_builder_get_object (builder, "preview_page")),
-                                     false);
-    gtk_assistant_set_page_complete (csv_imp_asst,
-                                     GTK_WIDGET(gtk_builder_get_object (builder, "account_match_page")),
-                                     false);
-    gtk_assistant_set_page_complete (csv_imp_asst,
-                                     GTK_WIDGET(gtk_builder_get_object (builder, "doc_page")),
-                                     true);
-    gtk_assistant_set_page_complete (csv_imp_asst,
-                                     GTK_WIDGET(gtk_builder_get_object (builder, "match_page")),
-                                     true);
-    gtk_assistant_set_page_complete (csv_imp_asst,
-                                     GTK_WIDGET(gtk_builder_get_object (builder, "summary_page")),
-                                     true);
-
-    /* File chooser Page */
-    file_page = GTK_WIDGET(gtk_builder_get_object (builder, "file_page"));
-    file_chooser = gtk_file_chooser_widget_new (GTK_FILE_CHOOSER_ACTION_OPEN);
-    g_signal_connect (G_OBJECT(file_chooser), "file-activated",
-                      G_CALLBACK(csv_tximp_file_confirm_cb), this);
-    auto button = gtk_button_new_from_stock (GTK_STOCK_OK);
-    gtk_widget_set_size_request (button, 100, -1);
-    gtk_widget_show (button);
-    auto h_box = gtk_hbox_new (TRUE, 0);
-    gtk_box_pack_start (GTK_BOX(h_box), button, FALSE, FALSE, 0);
-    gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER(file_chooser), h_box);
-    g_signal_connect (G_OBJECT(button), "clicked",
-                      G_CALLBACK(csv_tximp_file_confirm_cb), this);
-
-    auto box = GTK_WIDGET(gtk_builder_get_object (builder, "file_page"));
-    gtk_box_pack_start (GTK_BOX(box), file_chooser, TRUE, TRUE, 6);
-    gtk_widget_show (file_chooser);
-
-    /* Preview Settings Page */
-    {
-        preview_page = GTK_WIDGET(gtk_builder_get_object (builder, "preview_page"));
-
-        // Add Settings combo
-        auto settings_store = gtk_list_store_new (2, G_TYPE_POINTER, G_TYPE_STRING);
-        settings_combo = GTK_COMBO_BOX(gtk_combo_box_new_with_model_and_entry (GTK_TREE_MODEL(settings_store)));
-        gtk_combo_box_set_entry_text_column (GTK_COMBO_BOX(settings_combo), SET_NAME);
-        gtk_combo_box_set_active (GTK_COMBO_BOX(settings_combo), 0);
-
-        combo_hbox = GTK_WIDGET(gtk_builder_get_object (builder, "combo_hbox"));
-        gtk_box_pack_start (GTK_BOX(combo_hbox), GTK_WIDGET(settings_combo), true, true, 6);
-        gtk_widget_show (GTK_WIDGET(settings_combo));
-
-        g_signal_connect (G_OBJECT(settings_combo), "changed",
-                         G_CALLBACK(csv_tximp_preview_settings_sel_changed_cb), this);
-
-        // Additionally connect to the changed signal of the embedded GtkEntry
-        auto emb_entry = gtk_bin_get_child (GTK_BIN (settings_combo));
-        g_signal_connect (G_OBJECT(emb_entry), "changed",
-                         G_CALLBACK(csv_tximp_preview_settings_text_changed_cb), this);
-
-        // Add Save Settings button
-        save_button = GTK_WIDGET(gtk_builder_get_object (builder, "save_settings"));
-
-        // Add Delete Settings button
-        del_button = GTK_WIDGET(gtk_builder_get_object (builder, "delete_settings"));
-
-        /* The table containing the separator configuration widgets */
-        start_row_spin = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "start_row"));
-        end_row_spin = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "end_row"));
-        skip_alt_rows_button = GTK_WIDGET(gtk_builder_get_object (builder, "skip_rows"));
-        multi_split_cbutton = GTK_WIDGET(gtk_builder_get_object (builder, "multi_split_button"));
-
-        /* Load the separator buttons from the glade builder file into the
-         * sep_buttons array. */
-        const char* sep_button_names[] = {
-                "space_cbutton",
-                "tab_cbutton",
-                "comma_cbutton",
-                "colon_cbutton",
-                "semicolon_cbutton",
-                "hyphen_cbutton"
-            };
-        for (int i = 0; i < SEP_NUM_OF_TYPES; i++)
-            sep_button[i]
-                = (GtkCheckButton*)GTK_WIDGET(gtk_builder_get_object (builder, sep_button_names[i]));
-
-        /* Load and connect the custom separator checkbutton in the same way
-         * as the other separator buttons. */
-        custom_cbutton
-            = (GtkCheckButton*)GTK_WIDGET(gtk_builder_get_object (builder, "custom_cbutton"));
-
-        /* Load the entry for the custom separator entry. Connect it to the
-         * sep_button_clicked event handler as well. */
-        custom_entry = (GtkEntry*)GTK_WIDGET(gtk_builder_get_object (builder, "custom_entry"));
-
-        /* Add account selection widget */
-        acct_selector = gnc_account_sel_new();
-        auto account_hbox = GTK_WIDGET(gtk_builder_get_object (builder, "account_hbox"));
-        gtk_box_pack_start (GTK_BOX(account_hbox), acct_selector, TRUE, TRUE, 6);
-        gtk_widget_show (acct_selector);
-
-        g_signal_connect(G_OBJECT(acct_selector), "account_sel_changed",
-                         G_CALLBACK(csv_tximp_preview_acct_sel_cb), this);
-
-
-        /* Create the encoding selector widget and add it to the assistant */
-        encselector = GO_CHARMAP_SEL(go_charmap_sel_new(GO_CHARMAP_SEL_TO_UTF8));
-        /* Connect the selector to the encoding_selected event handler. */
-        g_signal_connect (G_OBJECT(encselector), "charmap_changed",
-                         G_CALLBACK(csv_tximp_preview_enc_sel_cb), this);
-
-        auto encoding_container = GTK_CONTAINER(gtk_builder_get_object (builder, "encoding_container"));
-        gtk_container_add (encoding_container, GTK_WIDGET(encselector));
-        gtk_widget_show_all (GTK_WIDGET(encoding_container));
-
-        /* The instructions label and image */
-        instructions_label = GTK_LABEL(gtk_builder_get_object (builder, "instructions_label"));
-        instructions_image = GTK_IMAGE(gtk_builder_get_object (builder, "instructions_image"));
-
-        /* Add in the date format combo box and hook it up to an event handler. */
-        date_format_combo = GTK_COMBO_BOX_TEXT(gtk_combo_box_text_new());
-        for (int i = 0; i < num_date_formats; i++)
-        {
-            gtk_combo_box_text_append_text (date_format_combo, _(date_format_user[i]));
-        }
-        gtk_combo_box_set_active (GTK_COMBO_BOX(date_format_combo), 0);
-        g_signal_connect (G_OBJECT(date_format_combo), "changed",
-                         G_CALLBACK(csv_tximp_preview_date_fmt_sel_cb), this);
-
-        /* Add it to the assistant. */
-        auto date_format_container = GTK_CONTAINER(gtk_builder_get_object (builder, "date_format_container"));
-        gtk_container_add (date_format_container, GTK_WIDGET(date_format_combo));
-        gtk_widget_show_all (GTK_WIDGET(date_format_container));
-
-        /* Add in the currency format combo box and hook it up to an event handler. */
-        currency_format_combo = GTK_COMBO_BOX_TEXT(gtk_combo_box_text_new());
-        for (int i = 0; i < num_currency_formats; i++)
-        {
-            gtk_combo_box_text_append_text (currency_format_combo, _(currency_format_user[i]));
-        }
-        /* Default will the locale */
-        gtk_combo_box_set_active (GTK_COMBO_BOX(currency_format_combo), 0);
-        g_signal_connect (G_OBJECT(currency_format_combo), "changed",
-                         G_CALLBACK(csv_tximp_preview_currency_fmt_sel_cb), this);
-
-        /* Add it to the assistant. */
-        auto currency_format_container = GTK_CONTAINER(gtk_builder_get_object (builder, "currency_format_container"));
-        gtk_container_add (currency_format_container, GTK_WIDGET(currency_format_combo));
-        gtk_widget_show_all (GTK_WIDGET(currency_format_container));
-
-        /* Connect the CSV/Fixed-Width radio button event handler. */
-        csv_button = GTK_WIDGET(gtk_builder_get_object (builder, "csv_button"));
-        fixed_button = GTK_WIDGET(gtk_builder_get_object (builder, "fixed_button"));
-
-        /* Load the data treeview and connect it to its resizing event handler. */
-        treeview = (GtkTreeView*)GTK_WIDGET(gtk_builder_get_object (builder, "treeview"));
-
-        /* Load the column type treeview. */
-        ctreeview = (GtkTreeView*)GTK_WIDGET(gtk_builder_get_object (builder, "ctreeview"));
-
-        /* This is true only after encoding_selected is called, so we must
-         * set it initially to false. */
-        encoding_selected_called = false;
-    }
-
-    /* Account Match Page */
-    account_match_page  = GTK_WIDGET(gtk_builder_get_object (builder, "account_match_page"));
-    account_match_view  = GTK_WIDGET(gtk_builder_get_object (builder, "account_match_view"));
-    account_match_label = GTK_WIDGET(gtk_builder_get_object (builder, "account_match_label"));
-    account_match_btn = GTK_WIDGET(gtk_builder_get_object (builder, "account_match_change"));
-
-    /* Doc Page */
-    doc_page = GTK_WIDGET(gtk_builder_get_object (builder, "doc_page"));
-
-    /* Matcher page */
-    match_page  = GTK_WIDGET(gtk_builder_get_object (builder, "match_page"));
-    match_label = GTK_WIDGET(gtk_builder_get_object (builder, "match_label"));
-
-    /* Summary Page */
-    summary_page  = GTK_WIDGET(gtk_builder_get_object (builder, "summary_page"));
-    summary_label = GTK_WIDGET(gtk_builder_get_object (builder, "summary_label"));
-
-    gnc_restore_window_size (GNC_PREFS_GROUP, GTK_WINDOW(csv_imp_asst));
-
-    gtk_builder_connect_signals (builder, this);
-    g_object_unref (G_OBJECT(builder));
+    info->assist_compmgr_close();
 }
 
-
 /********************************************************************\
  * gnc_file_csv_trans_import                                        *
  * opens up a assistant to import accounts.                         *
@@ -1835,16 +1934,7 @@ void
 gnc_file_csv_trans_import(void)
 {
     auto info = new CsvImpTransAssist;
-
-    /* In order to trigger a book options display on the creation of a new book,
-     * we need to detect when we are dealing with a new book. */
-    info->new_book = gnc_is_new_book();
-
     gnc_register_gui_component (ASSISTANT_CSV_IMPORT_TRANS_CM_CLASS,
                                 nullptr, csv_tximp_close_handler,
                                 info);
-
-    gtk_widget_show_all (GTK_WIDGET(info->csv_imp_asst));
-
-    gnc_window_adjust_for_screen (GTK_WINDOW(info->csv_imp_asst));
 }
diff --git a/src/import-export/csv-imp/gnc-tx-import.cpp b/src/import-export/csv-imp/gnc-tx-import.cpp
index 9418438..e6c70cb 100644
--- a/src/import-export/csv-imp/gnc-tx-import.cpp
+++ b/src/import-export/csv-imp/gnc-tx-import.cpp
@@ -189,8 +189,18 @@ void GncTxImport::encoding (const std::string& encoding)
 
 std::string GncTxImport::encoding () { return m_settings.m_encoding; }
 
-void GncTxImport::update_skipped_lines()
+void GncTxImport::update_skipped_lines(boost::optional<uint> start, boost::optional<uint> end,
+        boost::optional<bool> alt, boost::optional<bool> errors)
 {
+    if (start)
+        m_settings.m_skip_start_lines = *start;
+    if (end)
+        m_settings.m_skip_end_lines = *end;
+    if (alt)
+        m_settings.m_skip_alt_lines = *alt;
+    if (errors)
+        m_skip_errors = *errors;
+
     for (uint i = 0; i < m_parsed_lines.size(); i++)
     {
         std::get<4>(m_parsed_lines[i]) =
@@ -202,37 +212,10 @@ void GncTxImport::update_skipped_lines()
     }
 }
 
-void GncTxImport::skip_start_lines (uint num)
-{
-    m_settings.m_skip_start_lines = num;
-    update_skipped_lines();
-}
-
 uint GncTxImport::skip_start_lines () { return m_settings.m_skip_start_lines; }
-
-void GncTxImport::skip_end_lines (uint num)
-{
-    m_settings.m_skip_end_lines = num;
-    update_skipped_lines();
-}
-
 uint GncTxImport::skip_end_lines () { return m_settings.m_skip_end_lines; }
-
-void GncTxImport::skip_alt_lines (bool skip)
-{
-    m_settings.m_skip_alt_lines = skip;
-    update_skipped_lines();
-}
-
 bool GncTxImport::skip_alt_lines () { return m_settings.m_skip_alt_lines; }
-
-void GncTxImport::skip_errors (bool skip)
-{
-    m_skip_errors = skip;
-    update_skipped_lines();
-}
-
-bool GncTxImport::skip_errors () { return m_skip_errors; }
+bool GncTxImport::skip_err_lines () { return m_skip_errors; }
 
 void GncTxImport::separators (std::string separators)
 {
diff --git a/src/import-export/csv-imp/gnc-tx-import.hpp b/src/import-export/csv-imp/gnc-tx-import.hpp
index db5bfb3..09ef501 100644
--- a/src/import-export/csv-imp/gnc-tx-import.hpp
+++ b/src/import-export/csv-imp/gnc-tx-import.hpp
@@ -44,6 +44,7 @@ extern "C" {
 #include "gnc-tokenizer.hpp"
 #include "gnc-trans-props.hpp"
 #include "gnc-csv-trans-settings.hpp"
+#include <boost/optional.hpp>
 
 
 /** This struct stores a possibly incomplete transaction
@@ -115,17 +116,12 @@ public:
     void encoding (const std::string& encoding);
     std::string encoding ();
 
-    void skip_start_lines (uint num);
+    void update_skipped_lines (boost::optional<uint> start, boost::optional<uint> end,
+                               boost::optional<bool> alt, boost::optional<bool> errors);
     uint skip_start_lines ();
-
-    void skip_end_lines (uint num);
     uint skip_end_lines ();
-
-    void skip_alt_lines (bool skip);
     bool skip_alt_lines ();
-
-    void skip_errors (bool skip);
-    bool skip_errors ();
+    bool skip_err_lines ();
 
     void separators (std::string separators);
     std::string separators ();
@@ -170,8 +166,6 @@ private:
     void verify_column_selections (ErrorList& error_msg);
     void verify_data(ErrorList& error_msg);
 
-    void update_skipped_lines();
-
     /* Internal helper function that does the actual conversion from property lists
      * to real (possibly unbalanced) transaction with splits.
      */

commit f85e52beeef1afdd99a955e5ae90789e600ae38d
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sat Dec 24 13:00:18 2016 +0100

    Consistently use gnucash dialogs instead of generic gtk ones

diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index 73d5727..a5fc07e 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -325,17 +325,9 @@ csv_tximp_preview_settings_sel_changed_cb (GtkComboBox *combo, CsvImpTransAssist
 
     info->tx_imp->settings (*preset);
     if (preset->m_load_error)
-    {
-        auto dialog = gtk_message_dialog_new (GTK_WINDOW(info->csv_imp_asst),
-                                    (GtkDialogFlags) 0,
-                                    GTK_MESSAGE_ERROR,
-                                    GTK_BUTTONS_OK,
-                                    "%s", _("Load the Import Settings."));
-        gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
-            "%s", _("There were problems reading some saved settings, continuing to load.\n Please review and save again."));
-        gtk_dialog_run (GTK_DIALOG (dialog));
-        gtk_widget_destroy (dialog);
-    }
+        gnc_error_dialog (GTK_WIDGET(info->csv_imp_asst),
+            "%s", _("There were problems reading some saved settings, continuing to load.\n"
+                    "Please review and save again."));
 
     info->preview_refresh ();
     info->preview_handle_save_del_sensitivity (combo);
@@ -370,16 +362,9 @@ csv_tximp_preview_del_settings_cb (GtkWidget *button, CsvImpTransAssist *info)
     auto model = gtk_combo_box_get_model (info->settings_combo);
     gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1);
 
-    auto dialog = gtk_message_dialog_new (GTK_WINDOW(info->csv_imp_asst),
-                                (GtkDialogFlags) 0,
-                                GTK_MESSAGE_QUESTION,
-                                GTK_BUTTONS_OK_CANCEL,
+    auto response = gnc_ok_cancel_dialog (GTK_WIDGET(info->csv_imp_asst),
+                                GTK_RESPONSE_CANCEL,
                                 "%s", _("Delete the Import Settings."));
-    gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
-        "%s", _("Do you really want to delete the selection?"));
-    auto response = gtk_dialog_run (GTK_DIALOG (dialog));
-    gtk_widget_destroy (dialog);
-
     if (response == GTK_RESPONSE_OK)
     {
         preset->remove();
@@ -412,16 +397,9 @@ csv_tximp_preview_save_settings_cb (GtkWidget *button, CsvImpTransAssist *info)
 
             if (preset && (preset->m_name == std::string(new_name)))
             {
-                auto dialog = gtk_message_dialog_new (GTK_WINDOW(info->csv_imp_asst),
-                                        (GtkDialogFlags) 0,
-                                        GTK_MESSAGE_QUESTION,
-                                        GTK_BUTTONS_OK_CANCEL,
-                                        "%s", title);
-                gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
-                "%s", _("Setting name already exists, over write?"));
-                auto response = gtk_dialog_run (GTK_DIALOG (dialog));
-                gtk_widget_destroy (dialog);
-
+                auto response = gnc_ok_cancel_dialog (GTK_WIDGET(info->csv_imp_asst),
+                        GTK_RESPONSE_OK,
+                        "%s", _("Setting name already exists, over write?"));
                 if (response != GTK_RESPONSE_OK)
                     return;
 
@@ -434,15 +412,8 @@ csv_tximp_preview_save_settings_cb (GtkWidget *button, CsvImpTransAssist *info)
     /* All checks passed, let's save this preset */
     if (!info->tx_imp->save_settings())
     {
-        auto dialog = gtk_message_dialog_new (GTK_WINDOW(info->csv_imp_asst),
-                                    (GtkDialogFlags) 0,
-                                    GTK_MESSAGE_INFO,
-                                    GTK_BUTTONS_OK,
-                                    "%s", title);
-        gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+        gnc_info_dialog (GTK_WIDGET(info->csv_imp_asst),
             "%s", _("The settings have been saved."));
-        gtk_dialog_run (GTK_DIALOG (dialog));
-        gtk_widget_destroy (dialog);
 
         // Update the settings store
         info->preview_populate_settings_combo();
@@ -466,17 +437,8 @@ csv_tximp_preview_save_settings_cb (GtkWidget *button, CsvImpTransAssist *info)
         }
     }
     else
-    {
-        auto dialog = gtk_message_dialog_new (GTK_WINDOW(info->csv_imp_asst),
-                                    (GtkDialogFlags) 0,
-                                    GTK_MESSAGE_ERROR,
-                                    GTK_BUTTONS_OK,
-                                    "%s", title);
-        gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+        gnc_error_dialog (GTK_WIDGET(info->csv_imp_asst),
             "%s", _("There was a problem saving the settings, please try again."));
-        gtk_dialog_run (GTK_DIALOG (dialog));
-        gtk_widget_destroy (dialog);
-    }
 }/* Callback triggered when user adjusts skip start lines
  */
 void csv_tximp_preview_srow_cb (GtkSpinButton *spin, CsvImpTransAssist *info)
@@ -1538,16 +1500,9 @@ CsvImpTransAssist::assist_match_page_prepare ()
         /* Oops! This shouldn't happen when using the import assistant !
          * Inform the user and go back to the preview page.
          */
-        auto dialog = gtk_message_dialog_new (GTK_WINDOW(csv_imp_asst),
-                                    (GtkDialogFlags) 0,
-                                    GTK_MESSAGE_ERROR,
-                                    GTK_BUTTONS_OK,
-                                    "%s", _("Import Error"));
-        gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+        gnc_error_dialog (GTK_WIDGET(csv_imp_asst),
             _("An unexpected error has occurred. Please report this as a bug.\n\n"
               "Error message:\n%s"), err.what());
-        gtk_dialog_run (GTK_DIALOG (dialog));
-        gtk_widget_destroy (dialog);
         gtk_assistant_set_current_page (csv_imp_asst, 2);
     }
 

commit d2098bfc84121794077129c2d9c53efca77455a2
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sat Dec 24 12:59:47 2016 +0100

    Use std::unique_ptr for the TxImport object for better memory management

diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index fd7baa0..73d5727 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -143,7 +143,7 @@ struct  CsvImpTransAssist
     GtkWidget            *summary_label;            /**< The summary text */
 
     bool                  new_book;                 /**< Are we importing into a new book?; if yes, call book options */
-    GncTxImport          *tx_imp;                   /**< The actual data we are previewing */
+    std::unique_ptr<GncTxImport> tx_imp;            /**< The actual data we are previewing */
 };
 
 
@@ -209,32 +209,27 @@ csv_tximp_file_confirm_cb (GtkWidget *button, CsvImpTransAssist *info)
     g_free (starting_dir);
 
     /* Load the file into parse_data. */
-    auto parse_data = new GncTxImport;
+    info->tx_imp = std::unique_ptr<GncTxImport>(new GncTxImport);
     /* Assume data is CSV. User can later override to Fixed Width if needed */
     try
     {
-        parse_data->file_format (GncImpFileFormat::CSV);
-        parse_data->load_file (info->file_name);
-        parse_data->tokenize (true);
+        info->tx_imp->file_format (GncImpFileFormat::CSV);
+        info->tx_imp->load_file (info->file_name);
+        info->tx_imp->tokenize (true);
     }
     catch (std::ifstream::failure& e)
     {
         /* File loading failed ... */
-        gnc_error_dialog (nullptr, "%s", e.what());
-            delete parse_data;
-            return;
+        gnc_error_dialog (GTK_WIDGET(info->csv_imp_asst), "%s", e.what());
+        return;
     }
     catch (std::range_error &e)
     {
         /* Parsing failed ... */
-        gnc_error_dialog (nullptr, "%s", e.what());
-        delete parse_data;
+        gnc_error_dialog (GTK_WIDGET(info->csv_imp_asst), "%s", e.what());
         return;
     }
 
-    if (info->tx_imp) // Free parse_data if we have come back here
-        delete info->tx_imp;
-    info->tx_imp = parse_data;
     info->preview_refresh ();
 
     /* Get settings store and populate */
@@ -1663,10 +1658,6 @@ csv_tximp_close_handler (gpointer user_data)
 {
     auto info = (CsvImpTransAssist*)user_data;
 
-    /* Free the memory we allocated. */
-    if (info->tx_imp)
-        delete info->tx_imp;
-
     if (info->gnc_csv_importer_gui)
         info->gnc_csv_importer_gui = nullptr;
 

commit b629fc97a67888db42e1aa048e8b888779f8b173
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sat Dec 24 12:26:21 2016 +0100

    Convert assistant struct into a real c++ class

diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index 85873fe..fd7baa0 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -22,9 +22,10 @@
  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
  * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
 \********************************************************************/
-/** @file assistant-csv-trans-import.c
+/** @file assistant-csv-trans-import.cpp
     @brief CSV Import Assistant
     @author Copyright (c) 2012 Robert Fewell
+    @author Copyright (c) 2016 Geert Janssens
 */
 
 #include <guid.hpp>
@@ -69,8 +70,26 @@ extern "C"
 /* This static indicates the debugging module that this .o belongs to.  */
 static QofLogModule log_module = GNC_MOD_ASSISTANT;
 
-struct  CsvImportTrans
+struct  CsvImpTransAssist
 {
+    CsvImpTransAssist ();
+
+    void assist_file_page_prepare ();
+    void assist_preview_page_prepare ();
+    void assist_account_match_page_prepare ();
+    void assist_doc_page_prepare ();
+    void assist_match_page_prepare ();
+    void assist_summary_page_prepare ();
+
+    void preview_populate_settings_combo();
+    void preview_handle_save_del_sensitivity (GtkComboBox* combo);
+    void preview_row_sel_update ();
+    void preview_split_column (int col, int dx);
+    void preview_refresh_table ();
+    void preview_refresh ();
+    void preview_validate_settings ();
+    void acct_match_select(GtkTreeModel *model, GtkTreeIter* iter);
+    void acct_match_set_accounts ();
 
     GtkAssistant    *csv_imp_asst;
 
@@ -119,14 +138,12 @@ struct  CsvImportTrans
     GNCImportMainMatcher *gnc_csv_importer_gui;     /**< The GNCImportMainMatcher structure */
     GtkWidget            *help_button;              /**< The widget for the help button on the matcher page */
     GtkWidget            *cancel_button;            /**< The widget for the new cancel button when going back is blocked */
-    bool                  match_parse_run = false;  /**< This is set after the first run */
 
     GtkWidget            *summary_page;             /**< Assistant summary page widget */
     GtkWidget            *summary_label;            /**< The summary text */
 
     bool                  new_book;                 /**< Are we importing into a new book?; if yes, call book options */
     GncTxImport          *tx_imp;                   /**< The actual data we are previewing */
-
 };
 
 
@@ -134,45 +151,30 @@ struct  CsvImportTrans
 
 extern "C"
 {
-void csv_tximp_assist_prepare_cb (GtkAssistant  *assistant, GtkWidget *page, CsvImportTrans* info);
-void csv_tximp_assist_finish_cb (GtkAssistant *gtkassistant, CsvImportTrans* info);
-void csv_tximp_assist_cancel_cb (GtkAssistant *gtkassistant, CsvImportTrans* info);
-void csv_tximp_assist_close_cb (GtkAssistant *gtkassistant, CsvImportTrans* info);
-void csv_tximp_assist_destroy_cb (GtkWidget *object, CsvImportTrans* info);
-void csv_tximp_file_confirm_cb (GtkWidget *button, CsvImportTrans *info);
-void csv_tximp_preview_srow_cb (GtkSpinButton *spin, CsvImportTrans *info);
-void csv_tximp_preview_erow_cb (GtkSpinButton *spin, CsvImportTrans *info);
-void csv_tximp_preview_skiperrors_cb (GtkToggleButton *checkbox, CsvImportTrans *info);
-void csv_tximp_preview_skiprows_cb (GtkToggleButton *checkbox, CsvImportTrans *info);
-void csv_tximp_preview_multisplit_cb (GtkToggleButton *checkbox, CsvImportTrans *info);
-void csv_tximp_preview_del_settings_cb (GtkWidget *button, CsvImportTrans *info);
-void csv_tximp_preview_save_settings_cb (GtkWidget *button, CsvImportTrans *info);
-void csv_tximp_preview_settings_sel_changed_cb (GtkComboBox *combo, CsvImportTrans *info);
-void csv_tximp_preview_settings_text_changed_cb (GtkEntry *entry, CsvImportTrans *info);
-void csv_tximp_preview_sep_button_cb (GtkWidget* widget, CsvImportTrans* info);
-void csv_tximp_preview_sep_fixed_sel_cb (GtkToggleButton* csv_button, CsvImportTrans* info);
-void csv_tximp_preview_acct_sel_cb (GtkWidget* widget, CsvImportTrans* info);
+void csv_tximp_assist_prepare_cb (GtkAssistant  *assistant, GtkWidget *page, CsvImpTransAssist* info);
+void csv_tximp_assist_finish_cb (GtkAssistant *gtkassistant, CsvImpTransAssist* info);
+void csv_tximp_assist_cancel_cb (GtkAssistant *gtkassistant, CsvImpTransAssist* info);
+void csv_tximp_assist_close_cb (GtkAssistant *gtkassistant, CsvImpTransAssist* info);
+void csv_tximp_assist_destroy_cb (GtkWidget *object, CsvImpTransAssist* info);
+void csv_tximp_file_confirm_cb (GtkWidget *button, CsvImpTransAssist *info);
+void csv_tximp_preview_srow_cb (GtkSpinButton *spin, CsvImpTransAssist *info);
+void csv_tximp_preview_erow_cb (GtkSpinButton *spin, CsvImpTransAssist *info);
+void csv_tximp_preview_skiperrors_cb (GtkToggleButton *checkbox, CsvImpTransAssist *info);
+void csv_tximp_preview_skiprows_cb (GtkToggleButton *checkbox, CsvImpTransAssist *info);
+void csv_tximp_preview_multisplit_cb (GtkToggleButton *checkbox, CsvImpTransAssist *info);
+void csv_tximp_preview_del_settings_cb (GtkWidget *button, CsvImpTransAssist *info);
+void csv_tximp_preview_save_settings_cb (GtkWidget *button, CsvImpTransAssist *info);
+void csv_tximp_preview_settings_sel_changed_cb (GtkComboBox *combo, CsvImpTransAssist *info);
+void csv_tximp_preview_settings_text_changed_cb (GtkEntry *entry, CsvImpTransAssist *info);
+void csv_tximp_preview_sep_button_cb (GtkWidget* widget, CsvImpTransAssist* info);
+void csv_tximp_preview_sep_fixed_sel_cb (GtkToggleButton* csv_button, CsvImpTransAssist* info);
+void csv_tximp_preview_acct_sel_cb (GtkWidget* widget, CsvImpTransAssist* info);
 void csv_tximp_preview_enc_sel_cb (GOCharmapSel* selector, const char* encoding,
-                              CsvImportTrans* info);
-void csv_tximp_acct_match_button_clicked_cb (GtkWidget *widget, CsvImportTrans* info);
-bool csv_tximp_acct_match_view_clicked_cb (GtkWidget *widget, GdkEventButton *event, CsvImportTrans* info);
+                              CsvImpTransAssist* info);
+void csv_tximp_acct_match_button_clicked_cb (GtkWidget *widget, CsvImpTransAssist* info);
+bool csv_tximp_acct_match_view_clicked_cb (GtkWidget *widget, GdkEventButton *event, CsvImpTransAssist* info);
 }
 
-void csv_tximp_assist_file_page_prepare (GtkAssistant *assistant, CsvImportTrans* info);
-void csv_tximp_assist_preview_page_prepare (GtkAssistant *gtkassistant, CsvImportTrans* info);
-void csv_tximp_assist_account_match_page_prepare (GtkAssistant *assistant, CsvImportTrans* info);
-void csv_tximp_assist_doc_page_prepare (GtkAssistant *assistant, CsvImportTrans* info);
-void csv_tximp_assist_match_page_prepare (GtkAssistant *assistant, CsvImportTrans* info);
-void csv_tximp_assist_summary_page_prepare (GtkAssistant *assistant, CsvImportTrans* info);
-
-void csv_tximp_preview_populate_settings_combo(GtkComboBox* combo);
-void csv_tximp_preview_handle_save_del_sensitivity (GtkComboBox* combo, CsvImportTrans *info);
-void csv_tximp_preview_row_sel_update (CsvImportTrans* info);
-void csv_tximp_preview_refresh_table (CsvImportTrans* info);
-void csv_tximp_preview_refresh (CsvImportTrans *info);
-void csv_tximp_preview_validate_settings (CsvImportTrans* info);
-void csv_tximp_acct_match_set_accounts (CsvImportTrans* info);
-
 /*************************************************************************/
 
 
@@ -185,7 +187,7 @@ void csv_tximp_acct_match_set_accounts (CsvImportTrans* info);
  * call back for ok button in file chooser widget
  */
 void
-csv_tximp_file_confirm_cb (GtkWidget *button, CsvImportTrans *info)
+csv_tximp_file_confirm_cb (GtkWidget *button, CsvImpTransAssist *info)
 {
     gtk_assistant_set_page_complete (info->csv_imp_asst, info->account_match_page, false);
 
@@ -233,10 +235,10 @@ csv_tximp_file_confirm_cb (GtkWidget *button, CsvImportTrans *info)
     if (info->tx_imp) // Free parse_data if we have come back here
         delete info->tx_imp;
     info->tx_imp = parse_data;
-    csv_tximp_preview_refresh (info);
+    info->preview_refresh ();
 
     /* Get settings store and populate */
-    csv_tximp_preview_populate_settings_combo(info->settings_combo);
+    info->preview_populate_settings_combo();
     gtk_combo_box_set_active (info->settings_combo, 0);
 
     gtk_assistant_set_page_complete (info->csv_imp_asst, info->account_match_page, true);
@@ -251,10 +253,10 @@ csv_tximp_file_confirm_cb (GtkWidget *button, CsvImportTrans *info)
 
 /* Set the available presets in the settings combo box
  */
-void csv_tximp_preview_populate_settings_combo(GtkComboBox* combo)
+void CsvImpTransAssist::preview_populate_settings_combo()
 {
     // Clear the list store
-    auto model = gtk_combo_box_get_model (combo);
+    auto model = gtk_combo_box_get_model (settings_combo);
     gtk_list_store_clear (GTK_LIST_STORE(model));
 
     // Append the default entry
@@ -277,7 +279,7 @@ void csv_tximp_preview_populate_settings_combo(GtkComboBox* combo)
 /* Enable or disable the save and delete settings buttons
  * depending on what is selected and entered as settings name
  */
-void csv_tximp_preview_handle_save_del_sensitivity (GtkComboBox* combo, CsvImportTrans *info)
+void CsvImpTransAssist::preview_handle_save_del_sensitivity (GtkComboBox* combo)
 {
     GtkTreeIter iter;
     auto can_delete = false;
@@ -302,8 +304,8 @@ void csv_tximp_preview_handle_save_del_sensitivity (GtkComboBox* combo, CsvImpor
             !trans_preset_is_reserved_name (std::string(entry_text)))
         can_save = true;
 
-    gtk_widget_set_sensitive (info->save_button, can_save);
-    gtk_widget_set_sensitive (info->del_button, can_delete);
+    gtk_widget_set_sensitive (save_button, can_save);
+    gtk_widget_set_sensitive (del_button, can_delete);
 
 }
 
@@ -312,7 +314,7 @@ void csv_tximp_preview_handle_save_del_sensitivity (GtkComboBox* combo, CsvImpor
  * a preset is selected in the settings combo.
  */
 void
-csv_tximp_preview_settings_sel_changed_cb (GtkComboBox *combo, CsvImportTrans *info)
+csv_tximp_preview_settings_sel_changed_cb (GtkComboBox *combo, CsvImpTransAssist *info)
 {
     // Get the Active Selection
     GtkTreeIter iter;
@@ -340,29 +342,29 @@ csv_tximp_preview_settings_sel_changed_cb (GtkComboBox *combo, CsvImportTrans *i
         gtk_widget_destroy (dialog);
     }
 
-    csv_tximp_preview_refresh (info);
-    csv_tximp_preview_handle_save_del_sensitivity (combo, info);
+    info->preview_refresh ();
+    info->preview_handle_save_del_sensitivity (combo);
 }
 
 
 /* Callback triggered while the user is editing text the settings combo.
  */
 void
-csv_tximp_preview_settings_text_changed_cb (GtkEntry *entry, CsvImportTrans *info)
+csv_tximp_preview_settings_text_changed_cb (GtkEntry *entry, CsvImpTransAssist *info)
 {
     auto text = gtk_entry_get_text (entry);
     if (text)
         info->tx_imp->settings_name(text);
 
     auto combo = gtk_widget_get_parent (GTK_WIDGET(entry));
-    csv_tximp_preview_handle_save_del_sensitivity (GTK_COMBO_BOX(combo), info);
+    info->preview_handle_save_del_sensitivity (GTK_COMBO_BOX(combo));
 }
 
 
 /* Callback to delete a settings entry
  */
 void
-csv_tximp_preview_del_settings_cb (GtkWidget *button, CsvImportTrans *info)
+csv_tximp_preview_del_settings_cb (GtkWidget *button, CsvImpTransAssist *info)
 {
     // Get the Active Selection
     GtkTreeIter iter;
@@ -386,16 +388,16 @@ csv_tximp_preview_del_settings_cb (GtkWidget *button, CsvImportTrans *info)
     if (response == GTK_RESPONSE_OK)
     {
         preset->remove();
-        csv_tximp_preview_populate_settings_combo(info->settings_combo);
+        info->preview_populate_settings_combo();
         gtk_combo_box_set_active (info->settings_combo, 0); // Default
-        csv_tximp_preview_refresh (info); // Reset the widgets
+        info->preview_refresh (); // Reset the widgets
     }
 }
 
 /* Callback to save the current settings to the gnucash state file.
  */
 void
-csv_tximp_preview_save_settings_cb (GtkWidget *button, CsvImportTrans *info)
+csv_tximp_preview_save_settings_cb (GtkWidget *button, CsvImpTransAssist *info)
 {
     auto title = _("Save the Import Settings.");
     auto new_name = info->tx_imp->settings_name();
@@ -448,7 +450,7 @@ csv_tximp_preview_save_settings_cb (GtkWidget *button, CsvImportTrans *info)
         gtk_widget_destroy (dialog);
 
         // Update the settings store
-        csv_tximp_preview_populate_settings_combo(info->settings_combo);
+        info->preview_populate_settings_combo();
         auto model = gtk_combo_box_get_model (info->settings_combo);
 
         // Get the first entry in model
@@ -482,7 +484,7 @@ csv_tximp_preview_save_settings_cb (GtkWidget *button, CsvImportTrans *info)
     }
 }/* Callback triggered when user adjusts skip start lines
  */
-void csv_tximp_preview_srow_cb (GtkSpinButton *spin, CsvImportTrans *info)
+void csv_tximp_preview_srow_cb (GtkSpinButton *spin, CsvImpTransAssist *info)
 {
 
     /* Get number of lines to skip at the beginning */
@@ -493,13 +495,13 @@ void csv_tximp_preview_srow_cb (GtkSpinButton *spin, CsvImportTrans *info)
     gtk_adjustment_set_upper (adj, info->tx_imp->m_parsed_lines.size()
             - info->tx_imp->skip_start_lines());
 
-    csv_tximp_preview_refresh_table (info);
+    info->preview_refresh_table ();
 }
 
 
 /* Callback triggered when user adjusts skip end lines
  */
-void csv_tximp_preview_erow_cb (GtkSpinButton *spin, CsvImportTrans *info)
+void csv_tximp_preview_erow_cb (GtkSpinButton *spin, CsvImpTransAssist *info)
 {
     /* Get number of lines to skip at the end */
     info->tx_imp->skip_end_lines (gtk_spin_button_get_value_as_int (spin));
@@ -509,34 +511,34 @@ void csv_tximp_preview_erow_cb (GtkSpinButton *spin, CsvImportTrans *info)
     gtk_adjustment_set_upper (adj, info->tx_imp->m_parsed_lines.size()
             - info->tx_imp->skip_end_lines());
 
-    csv_tximp_preview_refresh_table (info);
+    info->preview_refresh_table ();
 }
 
 
 /* Callback triggered when user clicks the skip errors button
  */
-void csv_tximp_preview_skiperrors_cb (GtkToggleButton *checkbox, CsvImportTrans *info)
+void csv_tximp_preview_skiperrors_cb (GtkToggleButton *checkbox, CsvImpTransAssist *info)
 {
     info->tx_imp->skip_errors(gtk_toggle_button_get_active (checkbox));
-    csv_tximp_preview_refresh_table (info);
+    info->preview_refresh_table ();
 }
 
 
 /* Callback triggered when user clicks the multi split button
  */
-void csv_tximp_preview_multisplit_cb (GtkToggleButton *checkbox, CsvImportTrans *info)
+void csv_tximp_preview_multisplit_cb (GtkToggleButton *checkbox, CsvImpTransAssist *info)
 {
     info->tx_imp->multi_split (gtk_toggle_button_get_active (checkbox));
-    csv_tximp_preview_refresh_table (info);
+    info->preview_refresh_table ();
 }
 
 
 /* Callback triggered when user clicks the skip alternating lines button
  */
-void csv_tximp_preview_skiprows_cb (GtkToggleButton *checkbox, CsvImportTrans *info)
+void csv_tximp_preview_skiprows_cb (GtkToggleButton *checkbox, CsvImpTransAssist *info)
 {
     info->tx_imp->skip_alt_lines (gtk_toggle_button_get_active (checkbox));
-    csv_tximp_preview_refresh_table (info);
+    info->preview_refresh_table ();
 }
 
 
@@ -545,7 +547,7 @@ void csv_tximp_preview_skiprows_cb (GtkToggleButton *checkbox, CsvImportTrans *i
  * @param col The number of the column whose cell renderer is being retrieved
  * @return The cell renderer of column number col
  */
-static GtkCellRenderer* gnc_csv_preview_get_cell_renderer (CsvImportTrans* info, int col)
+static GtkCellRenderer* gnc_csv_preview_get_cell_renderer (CsvImpTransAssist* info, int col)
 {
     auto renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT(gtk_tree_view_get_column (info->treeview, col)));
     auto cell = GTK_CELL_RENDERER(renderers->data);
@@ -561,7 +563,7 @@ static GtkCellRenderer* gnc_csv_preview_get_cell_renderer (CsvImportTrans* info,
  * @param widget The widget that was changed
  * @param info The data that is being configured
  */
-void csv_tximp_preview_sep_button_cb (GtkWidget* widget, CsvImportTrans* info)
+void csv_tximp_preview_sep_button_cb (GtkWidget* widget, CsvImpTransAssist* info)
 {
 
     /* Only manipulate separator characters if the currently open file is
@@ -596,7 +598,7 @@ void csv_tximp_preview_sep_button_cb (GtkWidget* widget, CsvImportTrans* info)
     try
     {
         info->tx_imp->tokenize (false);
-        csv_tximp_preview_refresh_table (info);
+        info->preview_refresh_table ();
     }
     catch (std::range_error &e)
     {
@@ -621,12 +623,12 @@ void csv_tximp_preview_sep_button_cb (GtkWidget* widget, CsvImportTrans* info)
 
 /* Callback triggered when user selects an account in the account selection
  */
-void csv_tximp_preview_acct_sel_cb (GtkWidget* widget, CsvImportTrans* info)
+void csv_tximp_preview_acct_sel_cb (GtkWidget* widget, CsvImpTransAssist* info)
 {
 
     auto acct = gnc_account_sel_get_account( GNC_ACCOUNT_SEL(widget) );
     info->tx_imp->base_account(acct);
-    csv_tximp_preview_refresh_table (info);
+    info->preview_refresh_table ();
 }
 
 
@@ -635,7 +637,7 @@ void csv_tximp_preview_acct_sel_cb (GtkWidget* widget, CsvImportTrans* info)
  * @param csv_button The "Separated" radio button
  * @param info The display of the data being imported
  */
-void csv_tximp_preview_sep_fixed_sel_cb (GtkToggleButton* csv_button, CsvImportTrans* info)
+void csv_tximp_preview_sep_fixed_sel_cb (GtkToggleButton* csv_button, CsvImpTransAssist* info)
 {
     /* Set the parsing type correctly. */
     try
@@ -646,7 +648,7 @@ void csv_tximp_preview_sep_fixed_sel_cb (GtkToggleButton* csv_button, CsvImportT
             info->tx_imp->file_format (GncImpFileFormat::FIXED_WIDTH);
 
         info->tx_imp->tokenize (false);
-        csv_tximp_preview_refresh_table (info);
+        info->preview_refresh_table ();
     }
     catch (std::range_error &e)
     {
@@ -670,7 +672,7 @@ void csv_tximp_preview_sep_fixed_sel_cb (GtkToggleButton* csv_button, CsvImportT
  * @param info The display of the data being imported
  */
 void csv_tximp_preview_enc_sel_cb (GOCharmapSel* selector, const char* encoding,
-                              CsvImportTrans* info)
+                              CsvImpTransAssist* info)
 {
     /* This gets called twice every time a new encoding is selected. The
      * second call actually passes the correct data; thus, we only do
@@ -686,7 +688,7 @@ void csv_tximp_preview_enc_sel_cb (GOCharmapSel* selector, const char* encoding,
             info->tx_imp->encoding (encoding);
             info->tx_imp->tokenize (false);
 
-            csv_tximp_preview_refresh_table (info);
+            info->preview_refresh_table ();
 
             info->encoding_selected_called = false;
         }
@@ -709,10 +711,10 @@ void csv_tximp_preview_enc_sel_cb (GOCharmapSel* selector, const char* encoding,
  * @param format_selector The combo box for selecting date formats
  * @param info The display of the data being imported
  */
-static void csv_tximp_preview_date_fmt_sel_cb (GtkComboBox* format_selector, CsvImportTrans* info)
+static void csv_tximp_preview_date_fmt_sel_cb (GtkComboBox* format_selector, CsvImpTransAssist* info)
 {
     info->tx_imp->date_format (gtk_combo_box_get_active (format_selector));
-    csv_tximp_preview_refresh_table (info);
+    info->preview_refresh_table ();
 }
 
 
@@ -720,10 +722,10 @@ static void csv_tximp_preview_date_fmt_sel_cb (GtkComboBox* format_selector, Csv
  * @param currency_selector The combo box for selecting currency formats
  * @param info The display of the data being imported
  */
-static void csv_tximp_preview_currency_fmt_sel_cb (GtkComboBox* currency_selector, CsvImportTrans* info)
+static void csv_tximp_preview_currency_fmt_sel_cb (GtkComboBox* currency_selector, CsvImpTransAssist* info)
 {
     info->tx_imp->currency_format (gtk_combo_box_get_active (currency_selector));
-    csv_tximp_preview_refresh_table (info);
+    info->preview_refresh_table ();
 }
 
 
@@ -734,7 +736,7 @@ static void csv_tximp_preview_currency_fmt_sel_cb (GtkComboBox* currency_selecto
  * @param allocation The size of the data treeview
  * @param info The display of the data being imported
  */
-static void csv_tximp_preview_treeview_resized_cb (GtkWidget* widget, GtkAllocation* allocation, CsvImportTrans* info)
+static void csv_tximp_preview_treeview_resized_cb (GtkWidget* widget, GtkAllocation* allocation, CsvImpTransAssist* info)
 {
     /* Go through each column except for the last. (We don't want to set
      * the width of the last column because the user won't be able to
@@ -766,7 +768,7 @@ static void csv_tximp_preview_treeview_resized_cb (GtkWidget* widget, GtkAllocat
  * @param info The display of the data being imported
  */
 static void csv_tximp_preview_col_type_changed_cb (GtkCellRenderer* renderer, gchar* path,
-                                GtkTreeIter* new_text_iter, CsvImportTrans* info)
+                                GtkTreeIter* new_text_iter, CsvImpTransAssist* info)
 {
     /* Get the new text */
     auto col_num = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT(renderer), "col-num"));
@@ -778,7 +780,7 @@ static void csv_tximp_preview_col_type_changed_cb (GtkCellRenderer* renderer, gc
             -1);
 
     info->tx_imp->set_column_type (col_num, new_col_type);
-    csv_tximp_preview_refresh_table (info);
+    info->preview_refresh_table ();
 }
 
 /*======================================================================*/
@@ -844,7 +846,7 @@ static GnumericPopupMenuElement const popup_elements[] =
     { nullptr, nullptr, 0, 0, 0 },
 };
 
-static uint get_new_col_rel_pos (CsvImportTrans* info, int col, int dx)
+static uint get_new_col_rel_pos (CsvImpTransAssist* info, int col, int dx)
 {
     auto cell = gnc_csv_preview_get_cell_renderer (info, col);
     PangoFontDescription *font_desc;
@@ -866,7 +868,7 @@ static gboolean
 fixed_context_menu_handler (GnumericPopupMenuElement const *element,
         gpointer userdata)
 {
-    auto info = (CsvImportTrans*)userdata;
+    auto info = (CsvImpTransAssist*)userdata;
     auto col = info->fixed_context_col;
     auto rel_pos = get_new_col_rel_pos (info, col, info->fixed_context_dx);
     auto fwtok = dynamic_cast<GncFwTokenizer*>(info->tx_imp->m_tokenizer.get());
@@ -901,11 +903,11 @@ fixed_context_menu_handler (GnumericPopupMenuElement const *element,
         gnc_error_dialog (nullptr, "%s", e.what());
         return false;
     }
-    csv_tximp_preview_refresh_table (info);
+    info->preview_refresh_table ();
     return true;
 }
 static void
-select_column (CsvImportTrans* info, int col)
+select_column (CsvImpTransAssist* info, int col)
 {
     if (col < 0)
         return;
@@ -915,7 +917,7 @@ select_column (CsvImportTrans* info, int col)
 }
 
 static void
-fixed_context_menu (CsvImportTrans* info, GdkEventButton *event,
+fixed_context_menu (CsvImpTransAssist* info, GdkEventButton *event,
                     int col, int dx)
 {
     auto fwtok = dynamic_cast<GncFwTokenizer*>(info->tx_imp->m_tokenizer.get());
@@ -944,22 +946,22 @@ fixed_context_menu (CsvImportTrans* info, GdkEventButton *event,
 /*===================== End of Gnumeric Code ===========================*/
 /*======================================================================*/
 
-static void
-csv_tximp_preview_split_column (CsvImportTrans* info, int col, int dx)
+void
+CsvImpTransAssist::preview_split_column (int col, int dx)
 {
-    auto rel_pos = get_new_col_rel_pos (info, col, dx);
-    auto fwtok = dynamic_cast<GncFwTokenizer*>(info->tx_imp->m_tokenizer.get());
+    auto rel_pos = get_new_col_rel_pos (this, col, dx);
+    auto fwtok = dynamic_cast<GncFwTokenizer*>(tx_imp->m_tokenizer.get());
     fwtok->col_split (col, rel_pos);
     try
     {
-        info->tx_imp->tokenize (false);
+        tx_imp->tokenize (false);
     }
     catch (std::range_error& e)
     {
         gnc_error_dialog (nullptr, "%s", e.what());
         return;
     }
-    csv_tximp_preview_refresh_table (info);
+    preview_refresh_table ();
 }
 
 
@@ -972,7 +974,7 @@ csv_tximp_preview_split_column (CsvImportTrans* info, int col, int dx)
  */
 static void
 csv_tximp_preview_header_clicked_cb (GtkWidget* button, GdkEventButton* event,
-                                        CsvImportTrans* info)
+                                        CsvImpTransAssist* info)
 {
     /* Find the column that was clicked. */
     auto col = GPOINTER_TO_UINT(g_object_get_data (G_OBJECT(button), "col-num"));
@@ -987,7 +989,7 @@ csv_tximp_preview_header_clicked_cb (GtkWidget* button, GdkEventButton* event,
     auto offset = alloc.x - alloc.x;
     if (event->type == GDK_2BUTTON_PRESS && event->button == 1)
         /* Double clicks can split columns. */
-        csv_tximp_preview_split_column (info, col, (int)event->x - offset);
+        info->preview_split_column (col, (int)event->x - offset);
     else if (event->type == GDK_BUTTON_PRESS && event->button == 3)
         /* Right clicking brings up a context menu. */
         fixed_context_menu (info, event, col, (int)event->x - offset);
@@ -996,14 +998,14 @@ csv_tximp_preview_header_clicked_cb (GtkWidget* button, GdkEventButton* event,
 
 /* visualize skipped lines
  */
-void csv_tximp_preview_row_sel_update (CsvImportTrans* info)
+void CsvImpTransAssist::preview_row_sel_update ()
 {
     GtkTreeIter iter;
-    auto store = GTK_LIST_STORE(gtk_tree_view_get_model (info->treeview));
+    auto store = GTK_LIST_STORE(gtk_tree_view_get_model (treeview));
 
     /* Colorize rows that will be skipped */
     int i = 0;
-    for (auto parsed_line : info->tx_imp->m_parsed_lines)
+    for (auto parsed_line : tx_imp->m_parsed_lines)
     {
         const char *color = nullptr;
         if ((std::get<4>(parsed_line)))
@@ -1023,12 +1025,12 @@ void csv_tximp_preview_row_sel_update (CsvImportTrans* info)
  *
  * @param info The data being previewed
  */
-void csv_tximp_preview_refresh_table (CsvImportTrans* info)
+void CsvImpTransAssist::preview_refresh_table ()
 {
-    csv_tximp_preview_validate_settings (info);
+    preview_validate_settings ();
 
     /* ncols is the number of columns in the file data. */
-    auto column_types = info->tx_imp->column_types();
+    auto column_types = tx_imp->column_types();
     auto ncols = column_types.size();
 
     // Set up the header liststore
@@ -1064,7 +1066,7 @@ void csv_tximp_preview_refresh_table (CsvImportTrans* info)
                 -1);
     }
     gtk_list_store_set (ctstore, &iter, 2 * ncols, _("Errors"), -1);
-    gtk_tree_view_set_model (info->ctreeview, GTK_TREE_MODEL(ctstore));
+    gtk_tree_view_set_model (ctreeview, GTK_TREE_MODEL(ctstore));
 
 
     // Set up file data liststore
@@ -1078,7 +1080,7 @@ void csv_tximp_preview_refresh_table (CsvImportTrans* info)
     g_free (bodytypes);
 
     /* Fill the data liststore with data from the file. */
-    for (auto parse_line : info->tx_imp->m_parsed_lines)
+    for (auto parse_line : tx_imp->m_parsed_lines)
     {
         GtkTreeIter iter;
         gtk_list_store_append (store, &iter);
@@ -1098,22 +1100,22 @@ void csv_tximp_preview_refresh_table (CsvImportTrans* info)
             err_msg = std::get<1>(parse_line);
         gtk_list_store_set (store, &iter, ncols + 1, err_msg.c_str(), -1);
     }
-    gtk_tree_view_set_model (info->treeview, GTK_TREE_MODEL(store));
+    gtk_tree_view_set_model (treeview, GTK_TREE_MODEL(store));
 
     // Set up the two header and file data treeviews using the liststores created above
 
     /* Clear any columns from a previous invocation */
-    auto column = gtk_tree_view_get_column (info->ctreeview, 0);
+    auto column = gtk_tree_view_get_column (ctreeview, 0);
     while (column)
     {
-        gtk_tree_view_remove_column (info->ctreeview, column);
-        column = gtk_tree_view_get_column (info->ctreeview, 0);
+        gtk_tree_view_remove_column (ctreeview, column);
+        column = gtk_tree_view_get_column (ctreeview, 0);
     }
-    column = gtk_tree_view_get_column (info->treeview, 0);
+    column = gtk_tree_view_get_column (treeview, 0);
     while (column)
     {
-        gtk_tree_view_remove_column (info->treeview, column);
-        column = gtk_tree_view_get_column (info->treeview, 0);
+        gtk_tree_view_remove_column (treeview, column);
+        column = gtk_tree_view_get_column (treeview, 0);
     }
 
     /* combostore is a shared store for the header combocells in the header row.
@@ -1124,7 +1126,7 @@ void csv_tximp_preview_refresh_table (CsvImportTrans* info)
         /* Only add column types that make sense in
          * the chosen import mode (multi-split vs two-split).
          */
-        if (sanitize_trans_prop(col_type.first, info->tx_imp->multi_split()) == col_type.first)
+        if (sanitize_trans_prop(col_type.first, tx_imp->multi_split()) == col_type.first)
         {
             GtkTreeIter iter;
             gtk_list_store_append (combostore, &iter);
@@ -1147,9 +1149,9 @@ void csv_tximp_preview_refresh_table (CsvImportTrans* info)
         g_object_set_data (G_OBJECT(crenderer),
                            "col-num", GUINT_TO_POINTER(i));
         g_signal_connect (G_OBJECT(crenderer), "changed",
-                         G_CALLBACK(csv_tximp_preview_col_type_changed_cb), (gpointer)info);
+                         G_CALLBACK(csv_tximp_preview_col_type_changed_cb), (gpointer)this);
         /* Insert the column */
-        gtk_tree_view_insert_column_with_attributes (info->ctreeview,
+        gtk_tree_view_insert_column_with_attributes (ctreeview,
                 -1, "", crenderer, "text", 2 * i, nullptr);
 
         /* The file data treeview cells are simple text cells. */
@@ -1160,7 +1162,7 @@ void csv_tximp_preview_refresh_table (CsvImportTrans* info)
         /* Add a single column for the treeview. */
         auto col = gtk_tree_view_column_new_with_attributes ("", renderer, "background", 0,
                                                              "text", i + 1, nullptr);
-        gtk_tree_view_insert_column (info->treeview, col, -1);
+        gtk_tree_view_insert_column (treeview, col, -1);
         /* Enable resizing of the columns. */
         gtk_tree_view_column_set_resizable (col, TRUE);
 
@@ -1169,26 +1171,26 @@ void csv_tximp_preview_refresh_table (CsvImportTrans* info)
         g_object_set (G_OBJECT(col), "clickable", TRUE, nullptr);
         auto button = gtk_tree_view_column_get_widget(col);
         g_signal_connect (G_OBJECT(button), "button_press_event",
-                         G_CALLBACK(csv_tximp_preview_header_clicked_cb), (gpointer)info);
+                         G_CALLBACK(csv_tximp_preview_header_clicked_cb), (gpointer)this);
         /* Store the column number in the button to know which one was clicked later on */
         g_object_set_data (G_OBJECT(button), "col-num",GINT_TO_POINTER(i));
     }
 
     /* Add a column for the error messages */
     auto crenderer = gtk_cell_renderer_text_new();
-    gtk_tree_view_insert_column_with_attributes (info->ctreeview,
+    gtk_tree_view_insert_column_with_attributes (ctreeview,
             0, "", crenderer, "text", 2 * ncols, nullptr);
 
     auto renderer = gtk_cell_renderer_text_new();
     auto col = gtk_tree_view_column_new_with_attributes ("", renderer, "background", 0,
                                                          "text", ncols + 1, nullptr);
-    gtk_tree_view_insert_column (info->treeview, col, 0);
+    gtk_tree_view_insert_column (treeview, col, 0);
     /* Enable resizing of the columns. */
     gtk_tree_view_column_set_resizable (col, TRUE);
 
     /* Select the header row */
     gtk_tree_model_get_iter_first (GTK_TREE_MODEL(ctstore), &iter);
-    auto selection = gtk_tree_view_get_selection (info->ctreeview);
+    auto selection = gtk_tree_view_get_selection (ctreeview);
     gtk_tree_selection_select_iter (selection, &iter);
 
     /* Release our reference for the stores to allow proper memory management. */
@@ -1197,11 +1199,11 @@ void csv_tximp_preview_refresh_table (CsvImportTrans* info)
     g_object_unref (combostore);
 
     /* Make the things actually appear. */
-    gtk_widget_show_all (GTK_WIDGET(info->treeview));
-    gtk_widget_show_all (GTK_WIDGET(info->ctreeview));
+    gtk_widget_show_all (GTK_WIDGET(treeview));
+    gtk_widget_show_all (GTK_WIDGET(ctreeview));
 
     /* Update the row selection highlight */
-    csv_tximp_preview_row_sel_update (info);
+    preview_row_sel_update ();
 
 }
 
@@ -1209,51 +1211,51 @@ void csv_tximp_preview_refresh_table (CsvImportTrans* info)
  * Should be called when settings are changed.
  */
 void
-csv_tximp_preview_refresh (CsvImportTrans *info)
+CsvImpTransAssist::preview_refresh ()
 {
     // Set start row
-    auto adj = gtk_spin_button_get_adjustment (info->start_row_spin);
-    gtk_adjustment_set_upper (adj, info->tx_imp->m_parsed_lines.size());
-    gtk_spin_button_set_value (info->start_row_spin,
-            info->tx_imp->skip_start_lines());
+    auto adj = gtk_spin_button_get_adjustment (start_row_spin);
+    gtk_adjustment_set_upper (adj, tx_imp->m_parsed_lines.size());
+    gtk_spin_button_set_value (start_row_spin,
+            tx_imp->skip_start_lines());
 
     // Set end row
-    adj = gtk_spin_button_get_adjustment (info->end_row_spin);
-    gtk_adjustment_set_upper (adj, info->tx_imp->m_parsed_lines.size());
-    gtk_spin_button_set_value (info->end_row_spin,
-            info->tx_imp->skip_end_lines());
+    adj = gtk_spin_button_get_adjustment (end_row_spin);
+    gtk_adjustment_set_upper (adj, tx_imp->m_parsed_lines.size());
+    gtk_spin_button_set_value (end_row_spin,
+            tx_imp->skip_end_lines());
 
     // Set Alternate rows
-    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->skip_alt_rows_button),
-            info->tx_imp->skip_alt_lines());
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(skip_alt_rows_button),
+            tx_imp->skip_alt_lines());
 
     // Set multi-split indicator
-    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->multi_split_cbutton),
-            info->tx_imp->multi_split());
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(multi_split_cbutton),
+            tx_imp->multi_split());
 
     // Set Import Format
-    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->csv_button),
-            (info->tx_imp->file_format() == GncImpFileFormat::CSV));
-    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->fixed_button),
-            (info->tx_imp->file_format() != GncImpFileFormat::CSV));
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(csv_button),
+            (tx_imp->file_format() == GncImpFileFormat::CSV));
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(fixed_button),
+            (tx_imp->file_format() != GncImpFileFormat::CSV));
 
     // This section deals with the combo's and character encoding
-    gtk_combo_box_set_active (GTK_COMBO_BOX(info->date_format_combo),
-            info->tx_imp->date_format());
-    gtk_combo_box_set_active (GTK_COMBO_BOX(info->currency_format_combo),
-            info->tx_imp->currency_format());
-    go_charmap_sel_set_encoding (info->encselector, info->tx_imp->encoding().c_str());
+    gtk_combo_box_set_active (GTK_COMBO_BOX(date_format_combo),
+            tx_imp->date_format());
+    gtk_combo_box_set_active (GTK_COMBO_BOX(currency_format_combo),
+            tx_imp->currency_format());
+    go_charmap_sel_set_encoding (encselector, tx_imp->encoding().c_str());
 
-    gnc_account_sel_set_account(GNC_ACCOUNT_SEL(info->acct_selector),
-            info->tx_imp->base_account() , false);
+    gnc_account_sel_set_account(GNC_ACCOUNT_SEL(acct_selector),
+            tx_imp->base_account() , false);
 
     // Handle separator checkboxes and custom field, only relevant if the file format is csv
-    if (info->tx_imp->file_format() == GncImpFileFormat::CSV)
+    if (tx_imp->file_format() == GncImpFileFormat::CSV)
     {
-        auto separators = info->tx_imp->separators();
+        auto separators = tx_imp->separators();
         const auto stock_sep_chars = std::string (" \t,:;-");
         for (int i = 0; i < SEP_NUM_OF_TYPES; i++)
-            gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->sep_button[i]),
+            gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(sep_button[i]),
                 separators.find (stock_sep_chars[i]) != std::string::npos);
 
         // If there are any other separators in the separators string,
@@ -1264,30 +1266,30 @@ csv_tximp_preview_refresh (CsvImportTrans *info)
             separators.erase(pos);
             pos = separators.find_first_of (stock_sep_chars);
         }
-        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->custom_cbutton),
+        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(custom_cbutton),
                 !separators.empty());
-        gtk_entry_set_text (GTK_ENTRY(info->custom_entry), separators.c_str());
+        gtk_entry_set_text (GTK_ENTRY(custom_entry), separators.c_str());
     }
 
     // Repopulate the parsed data table
-    csv_tximp_preview_refresh_table (info);
+    preview_refresh_table ();
 }
 
 /* Check if all selected data can be parsed sufficiently to continue
  */
-void csv_tximp_preview_validate_settings (CsvImportTrans* info)
+void CsvImpTransAssist::preview_validate_settings ()
 {
     /* Allow the user to proceed only if there are no inconsistencies in the settings */
-    auto error_msg = info->tx_imp->verify();
-    gtk_assistant_set_page_complete (info->csv_imp_asst, info->preview_page, error_msg.empty());
-    gtk_label_set_markup(GTK_LABEL(info->instructions_label), error_msg.c_str());
-    gtk_widget_set_visible (GTK_WIDGET(info->instructions_image), !error_msg.empty());
+    auto error_msg = tx_imp->verify();
+    gtk_assistant_set_page_complete (csv_imp_asst, preview_page, error_msg.empty());
+    gtk_label_set_markup(GTK_LABEL(instructions_label), error_msg.c_str());
+    gtk_widget_set_visible (GTK_WIDGET(instructions_image), !error_msg.empty());
 
     /* Show or hide the account match page based on whether there are
      * accounts in the user data according to the importer configuration
      */
-    gtk_widget_set_visible (GTK_WIDGET(info->account_match_page),
-            !info->tx_imp->accounts().empty());
+    gtk_widget_set_visible (GTK_WIDGET(account_match_page),
+            !tx_imp->accounts().empty());
 }
 
 
@@ -1300,12 +1302,12 @@ void csv_tximp_preview_validate_settings (CsvImportTrans* info)
  *
  * @param info The data being previewed
  */
-void csv_tximp_acct_match_set_accounts (CsvImportTrans* info)
+void CsvImpTransAssist::acct_match_set_accounts ()
 {
-    auto store = gtk_tree_view_get_model (GTK_TREE_VIEW(info->account_match_view));
+    auto store = gtk_tree_view_get_model (GTK_TREE_VIEW(account_match_view));
     gtk_list_store_clear (GTK_LIST_STORE(store));
 
-    auto accts = info->tx_imp->accounts();
+    auto accts = tx_imp->accounts();
     for (auto acct : accts)
     {
         GtkTreeIter acct_iter;
@@ -1376,14 +1378,14 @@ csv_tximp_acct_match_text_parse (std::string acct_name)
     }
 }
 
-static void
-csv_tximp_acct_match_select_internal(CsvImportTrans* info, GtkTreeModel *model, GtkTreeIter& iter)
+void
+CsvImpTransAssist::acct_match_select(GtkTreeModel *model, GtkTreeIter* iter)
 {
     // Get the the stored string and account (if any)
     gchar *text = nullptr;
     Account *account = nullptr;
-    gtk_tree_model_get (model, &iter, MAPPING_STRING, &text,
-                                      MAPPING_ACCOUNT, &account, -1);
+    gtk_tree_model_get (model, iter, MAPPING_STRING, &text,
+                                     MAPPING_ACCOUNT, &account, -1);
 
     auto acct_name = csv_tximp_acct_match_text_parse (text);
     auto gnc_acc = gnc_import_select_account (nullptr, nullptr, true,
@@ -1392,7 +1394,7 @@ csv_tximp_acct_match_select_internal(CsvImportTrans* info, GtkTreeModel *model,
     if (gnc_acc) // We may have canceled
     {
         auto fullpath = gnc_account_get_full_name (gnc_acc);
-        gtk_list_store_set (GTK_LIST_STORE(model), &iter,
+        gtk_list_store_set (GTK_LIST_STORE(model), iter,
                 MAPPING_ACCOUNT, gnc_acc,
                 MAPPING_FULLPATH, fullpath, -1);
 
@@ -1403,26 +1405,26 @@ csv_tximp_acct_match_select_internal(CsvImportTrans* info, GtkTreeModel *model,
     }
     g_free (text);
 
-    gtk_assistant_set_page_complete (info->csv_imp_asst, info->account_match_page,
+    gtk_assistant_set_page_complete (csv_imp_asst, account_match_page,
             csv_tximp_acct_match_check_all (model));
 
 }
 
 void
-csv_tximp_acct_match_button_clicked_cb (GtkWidget *widget, CsvImportTrans* info)
+csv_tximp_acct_match_button_clicked_cb (GtkWidget *widget, CsvImpTransAssist* info)
 {
     auto model = gtk_tree_view_get_model (GTK_TREE_VIEW(info->account_match_view));
     auto selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(info->account_match_view));
 
     GtkTreeIter iter;
     if (gtk_tree_selection_get_selected (selection, &model, &iter))
-        csv_tximp_acct_match_select_internal (info, model, iter);
+        info->acct_match_select (model, &iter);
 }
 
 
 /* This is the callback for the mouse click */
 bool
-csv_tximp_acct_match_view_clicked_cb (GtkWidget *widget, GdkEventButton *event, CsvImportTrans* info)
+csv_tximp_acct_match_view_clicked_cb (GtkWidget *widget, GdkEventButton *event, CsvImpTransAssist* info)
 {
     /* This is for a double click */
     if (event->button == 1 && event->type == GDK_2BUTTON_PRESS)
@@ -1441,7 +1443,7 @@ csv_tximp_acct_match_view_clicked_cb (GtkWidget *widget, GdkEventButton *event,
             auto model = gtk_tree_view_get_model (GTK_TREE_VIEW(info->account_match_view));
             GtkTreeIter iter;
             if (gtk_tree_model_get_iter (model, &iter, path))
-                csv_tximp_acct_match_select_internal (info, model, iter);
+                info->acct_match_select (model, &iter);
             gtk_tree_path_free (path);
         }
         return true;
@@ -1455,102 +1457,93 @@ csv_tximp_acct_match_view_clicked_cb (GtkWidget *widget, GdkEventButton *event,
  *******************************************************/
 
 void
-csv_tximp_assist_file_page_prepare (GtkAssistant *assistant,
-        CsvImportTrans* info)
+CsvImpTransAssist::assist_file_page_prepare ()
 {
     /* Set the default directory */
     auto starting_dir = gnc_get_default_directory (GNC_PREFS_GROUP);
     if (starting_dir)
     {
-        gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER(info->file_chooser), starting_dir);
+        gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER(file_chooser), starting_dir);
         g_free (starting_dir);
     }
 
     /* Disable the Forward Assistant Button */
-    gtk_assistant_set_page_complete (assistant, info->account_match_page, false);
+    gtk_assistant_set_page_complete (csv_imp_asst, account_match_page, false);
 }
 
 
 void
-csv_tximp_assist_preview_page_prepare (GtkAssistant *assistant,
-        CsvImportTrans* info)
+CsvImpTransAssist::assist_preview_page_prepare ()
 {
-    g_signal_connect (G_OBJECT(info->treeview), "size-allocate",
-                     G_CALLBACK(csv_tximp_preview_treeview_resized_cb), (gpointer)info);
+    g_signal_connect (G_OBJECT(treeview), "size-allocate",
+                     G_CALLBACK(csv_tximp_preview_treeview_resized_cb), (gpointer)this);
 
     /* Disable the Forward Assistant Button */
-    gtk_assistant_set_page_complete (assistant, info->preview_page, false);
+    gtk_assistant_set_page_complete (csv_imp_asst, preview_page, false);
 
     /* Load the data into the treeview. */
-    csv_tximp_preview_refresh_table (info);
+    preview_refresh_table ();
 }
 
 void
-csv_tximp_assist_account_match_page_prepare (GtkAssistant *assistant,
-        CsvImportTrans* info)
+CsvImpTransAssist::assist_account_match_page_prepare ()
 {
     // Load the account strings into the store
-    csv_tximp_acct_match_set_accounts (info);
+    acct_match_set_accounts ();
 
     // Match the account strings to the mappings
-    auto store = gtk_tree_view_get_model (GTK_TREE_VIEW(info->account_match_view));
+    auto store = gtk_tree_view_get_model (GTK_TREE_VIEW(account_match_view));
     gnc_csv_account_map_load_mappings (store);
 
     auto text = std::string ("<span size=\"medium\" color=\"red\"><b>");
     text += _("To change mapping, double click on a row or select a row and press the button...");
     text += "</b></span>";
-    gtk_label_set_markup (GTK_LABEL(info->account_match_label), text.c_str());
+    gtk_label_set_markup (GTK_LABEL(account_match_label), text.c_str());
 
     // Enable the view, possibly after an error
-    gtk_widget_set_sensitive (info->account_match_view, true);
-    gtk_widget_set_sensitive (info->account_match_btn, true);
+    gtk_widget_set_sensitive (account_match_view, true);
+    gtk_widget_set_sensitive (account_match_btn, true);
 
     /* Enable the Forward Assistant Button */
-       gtk_assistant_set_page_complete (assistant, info->account_match_page,
+       gtk_assistant_set_page_complete (csv_imp_asst, account_match_page,
                csv_tximp_acct_match_check_all (store));
 }
 
 
 void
-csv_tximp_assist_doc_page_prepare (GtkAssistant *assistant,
-        CsvImportTrans* info)
+CsvImpTransAssist::assist_doc_page_prepare ()
 {
     /* Block going back */
-    gtk_assistant_commit (info->csv_imp_asst);
+    gtk_assistant_commit (csv_imp_asst);
 
     /* Before creating transactions, if this is a new book, let user specify
      * book options, since they affect how transactions are created */
-    if (info->new_book)
-        info->new_book = gnc_new_book_option_display (GTK_WIDGET(info->csv_imp_asst));
-
-    if (!info->match_parse_run)
-    {
-        /* Add the Cancel button for the matcher */
-        info->cancel_button = gtk_button_new_with_mnemonic (_("_Cancel"));
-        gtk_assistant_add_action_widget (assistant, info->cancel_button);
-        g_signal_connect (info->cancel_button, "clicked",
-                         G_CALLBACK(csv_tximp_assist_cancel_cb), info);
-        gtk_widget_show (GTK_WIDGET(info->cancel_button));
-    }
-    info->match_parse_run = true;
+    if (new_book)
+        new_book = gnc_new_book_option_display (GTK_WIDGET(csv_imp_asst));
+
+    /* Add the Cancel button for the matcher */
+    cancel_button = gtk_button_new_with_mnemonic (_("_Cancel"));
+    gtk_assistant_add_action_widget (csv_imp_asst, cancel_button);
+    g_signal_connect (cancel_button, "clicked",
+                     G_CALLBACK(csv_tximp_assist_cancel_cb), this);
+    gtk_widget_show (GTK_WIDGET(cancel_button));
 }
 
 
 void
-csv_tximp_assist_match_page_prepare (GtkAssistant *assistant,
-        CsvImportTrans* info)
+CsvImpTransAssist::assist_match_page_prepare ()
 {
     /* Create transactions from the parsed data */
     try
     {
-        info->tx_imp->create_transactions ();
+        tx_imp->create_transactions ();
     }
     catch (const std::invalid_argument& err)
     {
         /* Oops! This shouldn't happen when using the import assistant !
          * Inform the user and go back to the preview page.
          */
-        auto dialog = gtk_message_dialog_new (GTK_WINDOW(info->csv_imp_asst),
+        auto dialog = gtk_message_dialog_new (GTK_WINDOW(csv_imp_asst),
                                     (GtkDialogFlags) 0,
                                     GTK_MESSAGE_ERROR,
                                     GTK_BUTTONS_OK,
@@ -1560,77 +1553,73 @@ csv_tximp_assist_match_page_prepare (GtkAssistant *assistant,
               "Error message:\n%s"), err.what());
         gtk_dialog_run (GTK_DIALOG (dialog));
         gtk_widget_destroy (dialog);
-        gtk_assistant_set_current_page (info->csv_imp_asst, 2);
+        gtk_assistant_set_current_page (csv_imp_asst, 2);
     }
 
     /* Block going back */
-    gtk_assistant_commit (info->csv_imp_asst);
+    gtk_assistant_commit (csv_imp_asst);
 
     auto text = std::string( "<span size=\"medium\" color=\"red\"><b>");
     text += _("Double click on rows to change, then click on Apply to Import");
     text += "</b></span>";
-    gtk_label_set_markup (GTK_LABEL(info->match_label), text.c_str());
+    gtk_label_set_markup (GTK_LABEL(match_label), text.c_str());
 
-    if (!info->gnc_csv_importer_gui)
+    if (!gnc_csv_importer_gui)
     {
         /* Create the generic transaction importer GUI. */
-        info->gnc_csv_importer_gui = gnc_gen_trans_assist_new (info->match_page, nullptr, false, 42);
+        gnc_csv_importer_gui = gnc_gen_trans_assist_new (match_page, nullptr, false, 42);
 
         /* Add the help button for the matcher */
-        info->help_button = gtk_button_new_with_mnemonic (_("_Help"));
-        gtk_assistant_add_action_widget (assistant, info->help_button);
-        g_signal_connect (info->help_button, "clicked",
-                         G_CALLBACK(on_matcher_help_clicked), info->gnc_csv_importer_gui);
-        gtk_widget_show (GTK_WIDGET(info->help_button));
+        help_button = gtk_button_new_with_mnemonic (_("_Help"));
+        gtk_assistant_add_action_widget (csv_imp_asst, help_button);
+        g_signal_connect (help_button, "clicked",
+                         G_CALLBACK(on_matcher_help_clicked), gnc_csv_importer_gui);
+        gtk_widget_show (GTK_WIDGET(help_button));
 
         /* Copy all of the transactions to the importer GUI. */
-        for (auto trans_it : info->tx_imp->m_transactions)
+        for (auto trans_it : tx_imp->m_transactions)
         {
             auto draft_trans = trans_it.second;
             if (draft_trans->trans)
             {
-                gnc_gen_trans_list_add_trans (info->gnc_csv_importer_gui, draft_trans->trans);
+                gnc_gen_trans_list_add_trans (gnc_csv_importer_gui, draft_trans->trans);
                 draft_trans->trans = nullptr;
             }
         }
     }
-
-    /* Enable the Forward Assistant Button */
-    gtk_assistant_set_page_complete (assistant, info->match_page, true);
 }
 
 
 void
-csv_tximp_assist_summary_page_prepare (GtkAssistant *assistant,
-        CsvImportTrans* info)
+CsvImpTransAssist::assist_summary_page_prepare ()
 {
     /* Remove the added buttons */
-    gtk_assistant_remove_action_widget (assistant, info->help_button);
-    gtk_assistant_remove_action_widget (assistant, info->cancel_button);
+    gtk_assistant_remove_action_widget (csv_imp_asst, help_button);
+    gtk_assistant_remove_action_widget (csv_imp_asst, cancel_button);
 
     auto text = std::string("<span size=\"medium\"><b>");
-    text += _("The transactions were imported from the file '") + info->file_name + "'.";
+    text += _("The transactions were imported from the file '") + file_name + "'.";
     text += "</b></span>";
-    gtk_label_set_markup (GTK_LABEL(info->summary_label), text.c_str());
+    gtk_label_set_markup (GTK_LABEL(summary_label), text.c_str());
 }
 
 
 void
 csv_tximp_assist_prepare_cb (GtkAssistant *assistant, GtkWidget *page,
-        CsvImportTrans* info)
+        CsvImpTransAssist* info)
 {
     if (page == info->file_page)
-        csv_tximp_assist_file_page_prepare (assistant, info);
+        info->assist_file_page_prepare ();
     else if (page == info->preview_page)
-        csv_tximp_assist_preview_page_prepare (assistant, info);
+        info->assist_preview_page_prepare ();
     else if (page == info->account_match_page)
-        csv_tximp_assist_account_match_page_prepare (assistant, info);
+        info->assist_account_match_page_prepare ();
     else if (page == info->doc_page)
-        csv_tximp_assist_doc_page_prepare (assistant, info);
+        info->assist_doc_page_prepare ();
     else if (page == info->match_page)
-        csv_tximp_assist_match_page_prepare (assistant, info);
+        info->assist_match_page_prepare ();
     else if (page == info->summary_page)
-        csv_tximp_assist_summary_page_prepare (assistant, info);
+        info->assist_summary_page_prepare ();
 }
 
 
@@ -1638,14 +1627,14 @@ csv_tximp_assist_prepare_cb (GtkAssistant *assistant, GtkWidget *page,
  * Assistant call back functions
  *******************************************************/
 void
-csv_tximp_assist_destroy_cb (GtkWidget *object, CsvImportTrans* info)
+csv_tximp_assist_destroy_cb (GtkWidget *object, CsvImpTransAssist* info)
 {
     gnc_unregister_gui_component_by_data (ASSISTANT_CSV_IMPORT_TRANS_CM_CLASS, info);
-    g_free (info);
+    delete info;
 }
 
 void
-csv_tximp_assist_cancel_cb (GtkAssistant *assistant, CsvImportTrans* info)
+csv_tximp_assist_cancel_cb (GtkAssistant *assistant, CsvImpTransAssist* info)
 {
     if (info->gnc_csv_importer_gui)
         gnc_gen_trans_list_delete (info->gnc_csv_importer_gui);
@@ -1654,13 +1643,13 @@ csv_tximp_assist_cancel_cb (GtkAssistant *assistant, CsvImportTrans* info)
 }
 
 void
-csv_tximp_assist_close_cb (GtkAssistant *assistant, CsvImportTrans* info)
+csv_tximp_assist_close_cb (GtkAssistant *assistant, CsvImpTransAssist* info)
 {
     gnc_close_gui_component_by_data (ASSISTANT_CSV_IMPORT_TRANS_CM_CLASS, info);
 }
 
 void
-csv_tximp_assist_finish_cb (GtkAssistant *assistant, CsvImportTrans* info)
+csv_tximp_assist_finish_cb (GtkAssistant *assistant, CsvImpTransAssist* info)
 {
     /* Start the import */
     if (!info->tx_imp->m_transactions.empty())
@@ -1672,7 +1661,7 @@ csv_tximp_assist_finish_cb (GtkAssistant *assistant, CsvImportTrans* info)
 static void
 csv_tximp_close_handler (gpointer user_data)
 {
-    auto info = (CsvImportTrans*)user_data;
+    auto info = (CsvImpTransAssist*)user_data;
 
     /* Free the memory we allocated. */
     if (info->tx_imp)
@@ -1686,95 +1675,94 @@ csv_tximp_close_handler (gpointer user_data)
 }
 
 /*******************************************************
- * Create the Assistant
+ * Assistant Constructor
  *******************************************************/
-static void
-csv_tximp_assist_create (CsvImportTrans *info)
+CsvImpTransAssist::CsvImpTransAssist ()
 {
     auto builder = gtk_builder_new();
     gnc_builder_add_from_file  (builder , "assistant-csv-trans-import.glade", "start_row_adj");
     gnc_builder_add_from_file  (builder , "assistant-csv-trans-import.glade", "end_row_adj");
     gnc_builder_add_from_file  (builder , "assistant-csv-trans-import.glade", "account_match_store");
     gnc_builder_add_from_file  (builder , "assistant-csv-trans-import.glade", "CSV Transaction Assistant");
-    info->csv_imp_asst = GTK_ASSISTANT(gtk_builder_get_object (builder, "CSV Transaction Assistant"));
+    csv_imp_asst = GTK_ASSISTANT(gtk_builder_get_object (builder, "CSV Transaction Assistant"));
 
     /* Enable buttons on all page. */
-    gtk_assistant_set_page_complete (info->csv_imp_asst,
+    gtk_assistant_set_page_complete (csv_imp_asst,
                                      GTK_WIDGET(gtk_builder_get_object (builder, "start_page")),
                                      true);
-    gtk_assistant_set_page_complete (info->csv_imp_asst,
+    gtk_assistant_set_page_complete (csv_imp_asst,
                                      GTK_WIDGET(gtk_builder_get_object (builder, "file_page")),
-                                     FALSE);
-    gtk_assistant_set_page_complete (info->csv_imp_asst,
+                                     false);
+    gtk_assistant_set_page_complete (csv_imp_asst,
                                      GTK_WIDGET(gtk_builder_get_object (builder, "preview_page")),
-                                     FALSE);
-    gtk_assistant_set_page_complete (info->csv_imp_asst,
+                                     false);
+    gtk_assistant_set_page_complete (csv_imp_asst,
                                      GTK_WIDGET(gtk_builder_get_object (builder, "account_match_page")),
-                                     FALSE);
-    gtk_assistant_set_page_complete (info->csv_imp_asst,
+                                     false);
+    gtk_assistant_set_page_complete (csv_imp_asst,
                                      GTK_WIDGET(gtk_builder_get_object (builder, "doc_page")),
-                                     TRUE);
-    gtk_assistant_set_page_complete (info->csv_imp_asst,
+                                     true);
+    gtk_assistant_set_page_complete (csv_imp_asst,
                                      GTK_WIDGET(gtk_builder_get_object (builder, "match_page")),
-                                     FALSE);
-    gtk_assistant_set_page_complete (info->csv_imp_asst,
+                                     true);
+    gtk_assistant_set_page_complete (csv_imp_asst,
                                      GTK_WIDGET(gtk_builder_get_object (builder, "summary_page")),
-                                     TRUE);
+                                     true);
 
     /* File chooser Page */
-    info->file_page = GTK_WIDGET(gtk_builder_get_object (builder, "file_page"));
-    info->file_chooser = gtk_file_chooser_widget_new (GTK_FILE_CHOOSER_ACTION_OPEN);
-    g_signal_connect (G_OBJECT(info->file_chooser), "file-activated",
-                      G_CALLBACK(csv_tximp_file_confirm_cb), info);
+    file_page = GTK_WIDGET(gtk_builder_get_object (builder, "file_page"));
+    file_chooser = gtk_file_chooser_widget_new (GTK_FILE_CHOOSER_ACTION_OPEN);
+    g_signal_connect (G_OBJECT(file_chooser), "file-activated",
+                      G_CALLBACK(csv_tximp_file_confirm_cb), this);
     auto button = gtk_button_new_from_stock (GTK_STOCK_OK);
     gtk_widget_set_size_request (button, 100, -1);
     gtk_widget_show (button);
     auto h_box = gtk_hbox_new (TRUE, 0);
     gtk_box_pack_start (GTK_BOX(h_box), button, FALSE, FALSE, 0);
-    gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER(info->file_chooser), h_box);
+    gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER(file_chooser), h_box);
     g_signal_connect (G_OBJECT(button), "clicked",
-                      G_CALLBACK(csv_tximp_file_confirm_cb), info);
+                      G_CALLBACK(csv_tximp_file_confirm_cb), this);
 
     auto box = GTK_WIDGET(gtk_builder_get_object (builder, "file_page"));
-    gtk_box_pack_start (GTK_BOX(box), info->file_chooser, TRUE, TRUE, 6);
-    gtk_widget_show (info->file_chooser);
+    gtk_box_pack_start (GTK_BOX(box), file_chooser, TRUE, TRUE, 6);
+    gtk_widget_show (file_chooser);
 
     /* Preview Settings Page */
     {
-        info->preview_page = GTK_WIDGET(gtk_builder_get_object (builder, "preview_page"));
+        preview_page = GTK_WIDGET(gtk_builder_get_object (builder, "preview_page"));
 
         // Add Settings combo
         auto settings_store = gtk_list_store_new (2, G_TYPE_POINTER, G_TYPE_STRING);
-        info->settings_combo = GTK_COMBO_BOX(gtk_combo_box_new_with_model_and_entry (GTK_TREE_MODEL(settings_store)));
-        gtk_combo_box_set_entry_text_column (GTK_COMBO_BOX(info->settings_combo), SET_NAME);
-        gtk_combo_box_set_active (GTK_COMBO_BOX(info->settings_combo), 0);
+        settings_combo = GTK_COMBO_BOX(gtk_combo_box_new_with_model_and_entry (GTK_TREE_MODEL(settings_store)));
+        gtk_combo_box_set_entry_text_column (GTK_COMBO_BOX(settings_combo), SET_NAME);
+        gtk_combo_box_set_active (GTK_COMBO_BOX(settings_combo), 0);
 
-        info->combo_hbox = GTK_WIDGET(gtk_builder_get_object (builder, "combo_hbox"));
-        gtk_box_pack_start (GTK_BOX(info->combo_hbox), GTK_WIDGET(info->settings_combo), true, true, 6);
-        gtk_widget_show (GTK_WIDGET(info->settings_combo));
+        combo_hbox = GTK_WIDGET(gtk_builder_get_object (builder, "combo_hbox"));
+        gtk_box_pack_start (GTK_BOX(combo_hbox), GTK_WIDGET(settings_combo), true, true, 6);
+        gtk_widget_show (GTK_WIDGET(settings_combo));
 
-        g_signal_connect (G_OBJECT(info->settings_combo), "changed",
-                         G_CALLBACK(csv_tximp_preview_settings_sel_changed_cb), (gpointer)info);
+        g_signal_connect (G_OBJECT(settings_combo), "changed",
+                         G_CALLBACK(csv_tximp_preview_settings_sel_changed_cb), this);
 
         // Additionally connect to the changed signal of the embedded GtkEntry
-        auto emb_entry = gtk_bin_get_child (GTK_BIN (info->settings_combo));
+        auto emb_entry = gtk_bin_get_child (GTK_BIN (settings_combo));
         g_signal_connect (G_OBJECT(emb_entry), "changed",
-                         G_CALLBACK(csv_tximp_preview_settings_text_changed_cb), (gpointer)info);
+                         G_CALLBACK(csv_tximp_preview_settings_text_changed_cb), this);
 
         // Add Save Settings button
-        info->save_button = GTK_WIDGET(gtk_builder_get_object (builder, "save_settings"));
+        save_button = GTK_WIDGET(gtk_builder_get_object (builder, "save_settings"));
 
         // Add Delete Settings button
-        info->del_button = GTK_WIDGET(gtk_builder_get_object (builder, "delete_settings"));
+        del_button = GTK_WIDGET(gtk_builder_get_object (builder, "delete_settings"));
 
         /* The table containing the separator configuration widgets */
-        info->start_row_spin = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "start_row"));
-        info->end_row_spin = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "end_row"));
-        info->skip_alt_rows_button = GTK_WIDGET(gtk_builder_get_object (builder, "skip_rows"));
-        info->multi_split_cbutton = GTK_WIDGET(gtk_builder_get_object (builder, "multi_split_button"));
+        start_row_spin = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "start_row"));
+        end_row_spin = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "end_row"));
+        skip_alt_rows_button = GTK_WIDGET(gtk_builder_get_object (builder, "skip_rows"));
+        multi_split_cbutton = GTK_WIDGET(gtk_builder_get_object (builder, "multi_split_button"));
 
         /* Load the separator buttons from the glade builder file into the
-         * info->sep_buttons array. */
+         * sep_buttons array. */
         const char* sep_button_names[] = {
                 "space_cbutton",
                 "tab_cbutton",
@@ -1784,108 +1772,108 @@ csv_tximp_assist_create (CsvImportTrans *info)
                 "hyphen_cbutton"
             };
         for (int i = 0; i < SEP_NUM_OF_TYPES; i++)
-            info->sep_button[i]
+            sep_button[i]
                 = (GtkCheckButton*)GTK_WIDGET(gtk_builder_get_object (builder, sep_button_names[i]));
 
         /* Load and connect the custom separator checkbutton in the same way
          * as the other separator buttons. */
-        info->custom_cbutton
+        custom_cbutton
             = (GtkCheckButton*)GTK_WIDGET(gtk_builder_get_object (builder, "custom_cbutton"));
 
         /* Load the entry for the custom separator entry. Connect it to the
          * sep_button_clicked event handler as well. */
-        info->custom_entry = (GtkEntry*)GTK_WIDGET(gtk_builder_get_object (builder, "custom_entry"));
+        custom_entry = (GtkEntry*)GTK_WIDGET(gtk_builder_get_object (builder, "custom_entry"));
 
         /* Add account selection widget */
-        info->acct_selector = gnc_account_sel_new();
+        acct_selector = gnc_account_sel_new();
         auto account_hbox = GTK_WIDGET(gtk_builder_get_object (builder, "account_hbox"));
-        gtk_box_pack_start (GTK_BOX(account_hbox), info->acct_selector, TRUE, TRUE, 6);
-        gtk_widget_show (info->acct_selector);
+        gtk_box_pack_start (GTK_BOX(account_hbox), acct_selector, TRUE, TRUE, 6);
+        gtk_widget_show (acct_selector);
 
-        g_signal_connect(G_OBJECT(info->acct_selector), "account_sel_changed",
-                         G_CALLBACK(csv_tximp_preview_acct_sel_cb), (gpointer)info);
+        g_signal_connect(G_OBJECT(acct_selector), "account_sel_changed",
+                         G_CALLBACK(csv_tximp_preview_acct_sel_cb), this);
 
 
         /* Create the encoding selector widget and add it to the assistant */
-        info->encselector = GO_CHARMAP_SEL(go_charmap_sel_new(GO_CHARMAP_SEL_TO_UTF8));
+        encselector = GO_CHARMAP_SEL(go_charmap_sel_new(GO_CHARMAP_SEL_TO_UTF8));
         /* Connect the selector to the encoding_selected event handler. */
-        g_signal_connect (G_OBJECT(info->encselector), "charmap_changed",
-                         G_CALLBACK(csv_tximp_preview_enc_sel_cb), (gpointer)info);
+        g_signal_connect (G_OBJECT(encselector), "charmap_changed",
+                         G_CALLBACK(csv_tximp_preview_enc_sel_cb), this);
 
         auto encoding_container = GTK_CONTAINER(gtk_builder_get_object (builder, "encoding_container"));
-        gtk_container_add (encoding_container, GTK_WIDGET(info->encselector));
+        gtk_container_add (encoding_container, GTK_WIDGET(encselector));
         gtk_widget_show_all (GTK_WIDGET(encoding_container));
 
         /* The instructions label and image */
-        info->instructions_label = GTK_LABEL(gtk_builder_get_object (builder, "instructions_label"));
-        info->instructions_image = GTK_IMAGE(gtk_builder_get_object (builder, "instructions_image"));
+        instructions_label = GTK_LABEL(gtk_builder_get_object (builder, "instructions_label"));
+        instructions_image = GTK_IMAGE(gtk_builder_get_object (builder, "instructions_image"));
 
         /* Add in the date format combo box and hook it up to an event handler. */
-        info->date_format_combo = GTK_COMBO_BOX_TEXT(gtk_combo_box_text_new());
+        date_format_combo = GTK_COMBO_BOX_TEXT(gtk_combo_box_text_new());
         for (int i = 0; i < num_date_formats; i++)
         {
-            gtk_combo_box_text_append_text (info->date_format_combo, _(date_format_user[i]));
+            gtk_combo_box_text_append_text (date_format_combo, _(date_format_user[i]));
         }
-        gtk_combo_box_set_active (GTK_COMBO_BOX(info->date_format_combo), 0);
-        g_signal_connect (G_OBJECT(info->date_format_combo), "changed",
-                         G_CALLBACK(csv_tximp_preview_date_fmt_sel_cb), (gpointer)info);
+        gtk_combo_box_set_active (GTK_COMBO_BOX(date_format_combo), 0);
+        g_signal_connect (G_OBJECT(date_format_combo), "changed",
+                         G_CALLBACK(csv_tximp_preview_date_fmt_sel_cb), this);
 
         /* Add it to the assistant. */
         auto date_format_container = GTK_CONTAINER(gtk_builder_get_object (builder, "date_format_container"));
-        gtk_container_add (date_format_container, GTK_WIDGET(info->date_format_combo));
+        gtk_container_add (date_format_container, GTK_WIDGET(date_format_combo));
         gtk_widget_show_all (GTK_WIDGET(date_format_container));
 
         /* Add in the currency format combo box and hook it up to an event handler. */
-        info->currency_format_combo = GTK_COMBO_BOX_TEXT(gtk_combo_box_text_new());
+        currency_format_combo = GTK_COMBO_BOX_TEXT(gtk_combo_box_text_new());
         for (int i = 0; i < num_currency_formats; i++)
         {
-            gtk_combo_box_text_append_text (info->currency_format_combo, _(currency_format_user[i]));
+            gtk_combo_box_text_append_text (currency_format_combo, _(currency_format_user[i]));
         }
         /* Default will the locale */
-        gtk_combo_box_set_active (GTK_COMBO_BOX(info->currency_format_combo), 0);
-        g_signal_connect (G_OBJECT(info->currency_format_combo), "changed",
-                         G_CALLBACK(csv_tximp_preview_currency_fmt_sel_cb), (gpointer)info);
+        gtk_combo_box_set_active (GTK_COMBO_BOX(currency_format_combo), 0);
+        g_signal_connect (G_OBJECT(currency_format_combo), "changed",
+                         G_CALLBACK(csv_tximp_preview_currency_fmt_sel_cb), this);
 
         /* Add it to the assistant. */
         auto currency_format_container = GTK_CONTAINER(gtk_builder_get_object (builder, "currency_format_container"));
-        gtk_container_add (currency_format_container, GTK_WIDGET(info->currency_format_combo));
+        gtk_container_add (currency_format_container, GTK_WIDGET(currency_format_combo));
         gtk_widget_show_all (GTK_WIDGET(currency_format_container));
 
         /* Connect the CSV/Fixed-Width radio button event handler. */
-        info->csv_button = GTK_WIDGET(gtk_builder_get_object (builder, "csv_button"));
-        info->fixed_button = GTK_WIDGET(gtk_builder_get_object (builder, "fixed_button"));
+        csv_button = GTK_WIDGET(gtk_builder_get_object (builder, "csv_button"));
+        fixed_button = GTK_WIDGET(gtk_builder_get_object (builder, "fixed_button"));
 
         /* Load the data treeview and connect it to its resizing event handler. */
-        info->treeview = (GtkTreeView*)GTK_WIDGET(gtk_builder_get_object (builder, "treeview"));
+        treeview = (GtkTreeView*)GTK_WIDGET(gtk_builder_get_object (builder, "treeview"));
 
         /* Load the column type treeview. */
-        info->ctreeview = (GtkTreeView*)GTK_WIDGET(gtk_builder_get_object (builder, "ctreeview"));
+        ctreeview = (GtkTreeView*)GTK_WIDGET(gtk_builder_get_object (builder, "ctreeview"));
 
         /* This is true only after encoding_selected is called, so we must
          * set it initially to false. */
-        info->encoding_selected_called = false;
+        encoding_selected_called = false;
     }
 
     /* Account Match Page */
-    info->account_match_page  = GTK_WIDGET(gtk_builder_get_object (builder, "account_match_page"));
-    info->account_match_view  = GTK_WIDGET(gtk_builder_get_object (builder, "account_match_view"));
-    info->account_match_label = GTK_WIDGET(gtk_builder_get_object (builder, "account_match_label"));
-    info->account_match_btn = GTK_WIDGET(gtk_builder_get_object (builder, "account_match_change"));
+    account_match_page  = GTK_WIDGET(gtk_builder_get_object (builder, "account_match_page"));
+    account_match_view  = GTK_WIDGET(gtk_builder_get_object (builder, "account_match_view"));
+    account_match_label = GTK_WIDGET(gtk_builder_get_object (builder, "account_match_label"));
+    account_match_btn = GTK_WIDGET(gtk_builder_get_object (builder, "account_match_change"));
 
     /* Doc Page */
-    info->doc_page = GTK_WIDGET(gtk_builder_get_object (builder, "doc_page"));
+    doc_page = GTK_WIDGET(gtk_builder_get_object (builder, "doc_page"));
 
     /* Matcher page */
-    info->match_page  = GTK_WIDGET(gtk_builder_get_object (builder, "match_page"));
-    info->match_label = GTK_WIDGET(gtk_builder_get_object (builder, "match_label"));
+    match_page  = GTK_WIDGET(gtk_builder_get_object (builder, "match_page"));
+    match_label = GTK_WIDGET(gtk_builder_get_object (builder, "match_label"));
 
     /* Summary Page */
-    info->summary_page  = GTK_WIDGET(gtk_builder_get_object (builder, "summary_page"));
-    info->summary_label = GTK_WIDGET(gtk_builder_get_object (builder, "summary_label"));
+    summary_page  = GTK_WIDGET(gtk_builder_get_object (builder, "summary_page"));
+    summary_label = GTK_WIDGET(gtk_builder_get_object (builder, "summary_label"));
 
-    gnc_restore_window_size (GNC_PREFS_GROUP, GTK_WINDOW(info->csv_imp_asst));
+    gnc_restore_window_size (GNC_PREFS_GROUP, GTK_WINDOW(csv_imp_asst));
 
-    gtk_builder_connect_signals (builder, info);
+    gtk_builder_connect_signals (builder, this);
     g_object_unref (G_OBJECT(builder));
 }
 
@@ -1900,16 +1888,12 @@ csv_tximp_assist_create (CsvImportTrans *info)
 void
 gnc_file_csv_trans_import(void)
 {
-    CsvImportTrans *info;
-
-    info = g_new0 (CsvImportTrans, 1);
+    auto info = new CsvImpTransAssist;
 
     /* In order to trigger a book options display on the creation of a new book,
      * we need to detect when we are dealing with a new book. */
     info->new_book = gnc_is_new_book();
 
-    csv_tximp_assist_create (info);
-
     gnc_register_gui_component (ASSISTANT_CSV_IMPORT_TRANS_CM_CLASS,
                                 nullptr, csv_tximp_close_handler,
                                 info);

commit b13718ee0b3fa748f3af577a040b7357770cb461
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Fri Dec 23 20:08:20 2016 +0100

    Handle error skipping on the preview page as well and drop the custom page forwarder
    
    With the preview page blocking as long as there are (unskipped) errors
    there is no need any more for the assistant to go back from the documentation
    page to the preview page in case of errors - the documentation page
    can only be reached if all required data is valid.
    This required some additional tweaks to other functions that were
    written to be called twice in TxImport and no longer will be.

diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index 913d98d..85873fe 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -84,7 +84,6 @@ struct  CsvImportTrans
     GtkWidget       *del_button;                    /**< The Delete Settings button */
     GtkWidget       *acct_selector;                 /**< The Account selector */
     GtkWidget       *combo_hbox;                    /**< The Settings Combo hbox */
-    GtkWidget       *skip_errors_button;            /**< The widget for the check label button */
     GtkSpinButton   *start_row_spin;                /**< The widget for the start row spinner */
     GtkSpinButton   *end_row_spin;                  /**< The widget for the end row spinner */
     GtkWidget       *skip_alt_rows_button;          /**< The widget for Skip alternate rows from start row */
@@ -102,8 +101,6 @@ struct  CsvImportTrans
     GtkLabel        *instructions_label;            /**< The instructions label */
     GtkImage        *instructions_image;            /**< The instructions image */
     bool             encoding_selected_called;      /**< Before encoding_selected is first called, this is false.
-                                                       * (See description of encoding_selected.) */
-    bool             previewing_errors;             /**< true if the dialog is displaying
                                                        * error lines, instead of all the file data. */
     bool             skip_errors;                   /**< This is false until the user checks the skip errors. */
     int              fixed_context_col;             /**< The number of the column whose the user has clicked */
@@ -129,8 +126,6 @@ struct  CsvImportTrans
 
     bool                  new_book;                 /**< Are we importing into a new book?; if yes, call book options */
     GncTxImport          *tx_imp;                   /**< The actual data we are previewing */
-    int                   callcount;                /**< Number of times the assistant page forward function called */
-    int                   next_page;                /**< The saved assistant next page number */
 
 };
 
@@ -238,8 +233,6 @@ csv_tximp_file_confirm_cb (GtkWidget *button, CsvImportTrans *info)
     if (info->tx_imp) // Free parse_data if we have come back here
         delete info->tx_imp;
     info->tx_imp = parse_data;
-    info->previewing_errors = false; /* We're looking at all the data. */
-    info->skip_errors = false; // Set skip_errors to False
     csv_tximp_preview_refresh (info);
 
     /* Get settings store and populate */
@@ -524,8 +517,8 @@ void csv_tximp_preview_erow_cb (GtkSpinButton *spin, CsvImportTrans *info)
  */
 void csv_tximp_preview_skiperrors_cb (GtkToggleButton *checkbox, CsvImportTrans *info)
 {
-    info->skip_errors = gtk_toggle_button_get_active (checkbox);
-    csv_tximp_preview_validate_settings (info);
+    info->tx_imp->skip_errors(gtk_toggle_button_get_active (checkbox));
+    csv_tximp_preview_refresh_table (info);
 }
 
 
@@ -1009,14 +1002,11 @@ void csv_tximp_preview_row_sel_update (CsvImportTrans* info)
     auto store = GTK_LIST_STORE(gtk_tree_view_get_model (info->treeview));
 
     /* Colorize rows that will be skipped */
-    for (uint i = 0; i < info->tx_imp->m_parsed_lines.size(); i++)
+    int i = 0;
+    for (auto parsed_line : info->tx_imp->m_parsed_lines)
     {
         const char *color = nullptr;
-        if ((i < info->tx_imp->skip_start_lines()) ||             // start rows to skip
-            (i >= info->tx_imp->m_parsed_lines.size()
-                    - info->tx_imp->skip_end_lines()) ||          // end rows to skip
-            (((i - info->tx_imp->skip_start_lines()) % 2 == 1) && // skip every second row...
-             info->tx_imp->skip_alt_lines()))                     // ...if requested
+        if ((std::get<4>(parsed_line)))
             color = "pink";
         else
             color = nullptr;                                          // all other rows
@@ -1024,6 +1014,7 @@ void csv_tximp_preview_row_sel_update (CsvImportTrans* info)
         bool valid = gtk_tree_model_iter_nth_child (GTK_TREE_MODEL(store), &iter, nullptr, i);
         if (valid)
             gtk_list_store_set (store, &iter, 0, color, -1);
+        i++;
     }
 }
 
@@ -1089,10 +1080,6 @@ void csv_tximp_preview_refresh_table (CsvImportTrans* info)
     /* Fill the data liststore with data from the file. */
     for (auto parse_line : info->tx_imp->m_parsed_lines)
     {
-        // When previewing errors skip all lines that don't have errors
-        if (info->previewing_errors && std::get<1>(parse_line).empty())
-            continue;
-
         GtkTreeIter iter;
         gtk_list_store_append (store, &iter);
 
@@ -1106,7 +1093,10 @@ void csv_tximp_preview_refresh_table (CsvImportTrans* info)
             gtk_list_store_set (store, &iter, pos, cell_str_it->c_str(), -1);
         }
         /* Add the optional error messages in the last column of the store */
-        gtk_list_store_set (store, &iter, ncols + 1, std::get<1>(parse_line).c_str(), -1);
+        auto err_msg = std::string();
+        if (!std::get<4>(parse_line))
+            err_msg = std::get<1>(parse_line);
+        gtk_list_store_set (store, &iter, ncols + 1, err_msg.c_str(), -1);
     }
     gtk_tree_view_set_model (info->treeview, GTK_TREE_MODEL(store));
 
@@ -1292,6 +1282,12 @@ void csv_tximp_preview_validate_settings (CsvImportTrans* info)
     gtk_assistant_set_page_complete (info->csv_imp_asst, info->preview_page, error_msg.empty());
     gtk_label_set_markup(GTK_LABEL(info->instructions_label), error_msg.c_str());
     gtk_widget_set_visible (GTK_WIDGET(info->instructions_image), !error_msg.empty());
+
+    /* Show or hide the account match page based on whether there are
+     * accounts in the user data according to the importer configuration
+     */
+    gtk_widget_set_visible (GTK_WIDGET(info->account_match_page),
+            !info->tx_imp->accounts().empty());
 }
 
 
@@ -1462,9 +1458,6 @@ void
 csv_tximp_assist_file_page_prepare (GtkAssistant *assistant,
         CsvImportTrans* info)
 {
-    info->previewing_errors = false; // We're looking at all the data.
-    info->skip_errors = false; // Set skip_errors to False to start with.
-
     /* Set the default directory */
     auto starting_dir = gnc_get_default_directory (GNC_PREFS_GROUP);
     if (starting_dir)
@@ -1485,46 +1478,11 @@ csv_tximp_assist_preview_page_prepare (GtkAssistant *assistant,
     g_signal_connect (G_OBJECT(info->treeview), "size-allocate",
                      G_CALLBACK(csv_tximp_preview_treeview_resized_cb), (gpointer)info);
 
-    // Hide the skip errors check button
-    gtk_widget_hide (GTK_WIDGET(info->skip_errors_button));
-
-    if (info->previewing_errors) // We are looking at errors to display
-    {
-        /* Block going back */
-        gtk_assistant_commit (info->csv_imp_asst);
-
-        gchar* name;
-        GtkIconSize size;
-        gtk_image_get_stock (info->instructions_image, &name, &size);
-        gtk_image_set_from_stock (info->instructions_image, GTK_STOCK_DIALOG_ERROR, size);
-        gtk_label_set_text (info->instructions_label,
-                           _("The rows displayed below had errors which are in the last column. You can attempt to correct them by changing the configuration."));
-        gtk_widget_show (GTK_WIDGET(info->instructions_image));
-        gtk_widget_show (GTK_WIDGET(info->instructions_label));
-
-        /* Reset start and end row */
-        gtk_spin_button_set_value (info->start_row_spin, 0);
-        gtk_spin_button_set_value (info->end_row_spin, 0);
-
-        /* Set spin buttons and settings combo hbox not sensitive */
-        gtk_widget_set_sensitive (info->combo_hbox, FALSE);
-        gtk_widget_set_sensitive (GTK_WIDGET(info->start_row_spin), FALSE);
-        gtk_widget_set_sensitive (GTK_WIDGET(info->end_row_spin), FALSE);
-        gtk_widget_set_sensitive (info->skip_alt_rows_button, FALSE);
-        gtk_widget_set_sensitive (info->multi_split_cbutton, FALSE);
-        info->tx_imp->skip_alt_lines (false);
-
-        /* Show the skip errors check button */
-        gtk_widget_show (GTK_WIDGET(info->skip_errors_button));
-        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->skip_errors_button), FALSE);
-    }
-
     /* Disable the Forward Assistant Button */
     gtk_assistant_set_page_complete (assistant, info->preview_page, false);
 
     /* Load the data into the treeview. */
     csv_tximp_preview_refresh_table (info);
-
 }
 
 void
@@ -1582,40 +1540,61 @@ void
 csv_tximp_assist_match_page_prepare (GtkAssistant *assistant,
         CsvImportTrans* info)
 {
+    /* Create transactions from the parsed data */
+    try
+    {
+        info->tx_imp->create_transactions ();
+    }
+    catch (const std::invalid_argument& err)
+    {
+        /* Oops! This shouldn't happen when using the import assistant !
+         * Inform the user and go back to the preview page.
+         */
+        auto dialog = gtk_message_dialog_new (GTK_WINDOW(info->csv_imp_asst),
+                                    (GtkDialogFlags) 0,
+                                    GTK_MESSAGE_ERROR,
+                                    GTK_BUTTONS_OK,
+                                    "%s", _("Import Error"));
+        gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+            _("An unexpected error has occurred. Please report this as a bug.\n\n"
+              "Error message:\n%s"), err.what());
+        gtk_dialog_run (GTK_DIALOG (dialog));
+        gtk_widget_destroy (dialog);
+        gtk_assistant_set_current_page (info->csv_imp_asst, 2);
+    }
+
     /* Block going back */
     gtk_assistant_commit (info->csv_imp_asst);
 
-    if (!info->tx_imp->m_parse_errors || info->skip_errors)
-    {
-        auto text = std::string( "<span size=\"medium\" color=\"red\"><b>");
-        text += _("Double click on rows to change, then click on Apply to Import");
-        text += "</b></span>";
-        gtk_label_set_markup (GTK_LABEL(info->match_label), text.c_str());
+    auto text = std::string( "<span size=\"medium\" color=\"red\"><b>");
+    text += _("Double click on rows to change, then click on Apply to Import");
+    text += "</b></span>";
+    gtk_label_set_markup (GTK_LABEL(info->match_label), text.c_str());
 
-        if (!info->gnc_csv_importer_gui)
+    if (!info->gnc_csv_importer_gui)
+    {
+        /* Create the generic transaction importer GUI. */
+        info->gnc_csv_importer_gui = gnc_gen_trans_assist_new (info->match_page, nullptr, false, 42);
+
+        /* Add the help button for the matcher */
+        info->help_button = gtk_button_new_with_mnemonic (_("_Help"));
+        gtk_assistant_add_action_widget (assistant, info->help_button);
+        g_signal_connect (info->help_button, "clicked",
+                         G_CALLBACK(on_matcher_help_clicked), info->gnc_csv_importer_gui);
+        gtk_widget_show (GTK_WIDGET(info->help_button));
+
+        /* Copy all of the transactions to the importer GUI. */
+        for (auto trans_it : info->tx_imp->m_transactions)
         {
-            /* Create the generic transaction importer GUI. */
-            info->gnc_csv_importer_gui = gnc_gen_trans_assist_new (info->match_page, nullptr, false, 42);
-
-            /* Add the help button for the matcher */
-            info->help_button = gtk_button_new_with_mnemonic (_("_Help"));
-            gtk_assistant_add_action_widget (assistant, info->help_button);
-            g_signal_connect (info->help_button, "clicked",
-                             G_CALLBACK(on_matcher_help_clicked), info->gnc_csv_importer_gui);
-            gtk_widget_show (GTK_WIDGET(info->help_button));
-
-            /* Copy all of the transactions to the importer GUI. */
-            for (auto trans_it : info->tx_imp->m_transactions)
+            auto draft_trans = trans_it.second;
+            if (draft_trans->trans)
             {
-                auto draft_trans = trans_it.second;
-                if (draft_trans->trans)
-                {
-                    gnc_gen_trans_list_add_trans (info->gnc_csv_importer_gui, draft_trans->trans);
-                    draft_trans->trans = nullptr;
-                }
+                gnc_gen_trans_list_add_trans (info->gnc_csv_importer_gui, draft_trans->trans);
+                draft_trans->trans = nullptr;
             }
         }
     }
+
     /* Enable the Forward Assistant Button */
     gtk_assistant_set_page_complete (assistant, info->match_page, true);
 }
@@ -1636,110 +1615,10 @@ csv_tximp_assist_summary_page_prepare (GtkAssistant *assistant,
 }
 
 
-static gint
-csv_tximp_forward_page_func (gint current_page, gpointer user_data)
-{
-    auto info = (CsvImportTrans*)user_data;
-
-    /* Note: This function gets called multiple times by the GtkAssistant code and so
-       as we need to run tests only once I have used a counter that gets incremented on
-       every call but the tests only get run when the counter is at 1 */
-    info->callcount = info->callcount + 1;
-
-    auto next_page = info->next_page;
-    switch (current_page)
-    {
-	case 0: //from start page
-            if (info->callcount == 1)
-            {
-                next_page = 1;
-                info->next_page = next_page;
-            }
-            break;
-
-        case 1: //from file page
-            if (info->callcount == 1)
-            {
-                next_page = 2;
-                info->next_page = next_page;
-            }
-            break;
-
-        case 2: //from preview page
-            if (info->callcount == 1)
-            {
-                // Skip Errors set, goto to doc page
-                if (info->skip_errors)
-                    next_page = 4;
-                // Check to see if we have an account / other account columns
-                else if (info->tx_imp->check_for_column_type (GncTransPropType::ACCOUNT) ||
-                        info->tx_imp->check_for_column_type (GncTransPropType::TACCOUNT))
-                    next_page = 3;
-                else
-                    next_page = 4;
-
-                info->next_page = next_page;
-            }
-            break;
-
-        case 3: //from account match page
-            if (info->callcount == 1)
-            {
-                next_page = 4;
-                info->next_page = next_page;
-            }
-            break;
-
-        case 4: //from doc page
-            if (info->callcount == 1)
-            {
-                /* Create transactions from the parsed data, first time with false
-                   Subsequent times with true */
-                info->tx_imp->create_transactions (info->match_parse_run);
-
-                /* if there are errors, we jump back to preview to correct */
-                if (info->tx_imp->m_parse_errors && !info->skip_errors)
-                {
-                    info->previewing_errors = true; /* We're looking at errors. */
-                    next_page = 2;
-                }
-                else
-                    next_page = 5;
-
-                info->next_page = next_page;
-            }
-            break;
-
-        case 5: //from match page
-            if (info->callcount == 1)
-            {
-                next_page = 6;
-                info->next_page = next_page;
-            }
-            break;
-
-        case 6: //from summary page
-            if (info->callcount == 1)
-            {
-                next_page = 7;
-                info->next_page = next_page;
-            }
-            break;
-
-        default:
-            next_page = -1;
-    }
-    return next_page;
-}
-
-
 void
 csv_tximp_assist_prepare_cb (GtkAssistant *assistant, GtkWidget *page,
         CsvImportTrans* info)
 {
-    // Reset callcount on every prepare
-    info->callcount = 0;
-
     if (page == info->file_page)
         csv_tximp_assist_file_page_prepare (assistant, info);
     else if (page == info->preview_page)
@@ -1819,9 +1698,6 @@ csv_tximp_assist_create (CsvImportTrans *info)
     gnc_builder_add_from_file  (builder , "assistant-csv-trans-import.glade", "CSV Transaction Assistant");
     info->csv_imp_asst = GTK_ASSISTANT(gtk_builder_get_object (builder, "CSV Transaction Assistant"));
 
-    /* Set the forward function */
-    gtk_assistant_set_forward_page_func (info->csv_imp_asst, csv_tximp_forward_page_func, info, nullptr);
-
     /* Enable buttons on all page. */
     gtk_assistant_set_page_complete (info->csv_imp_asst,
                                      GTK_WIDGET(gtk_builder_get_object (builder, "start_page")),
@@ -1895,7 +1771,6 @@ csv_tximp_assist_create (CsvImportTrans *info)
         info->start_row_spin = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "start_row"));
         info->end_row_spin = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "end_row"));
         info->skip_alt_rows_button = GTK_WIDGET(gtk_builder_get_object (builder, "skip_rows"));
-        info->skip_errors_button = GTK_WIDGET(gtk_builder_get_object (builder, "skip_errors_button"));
         info->multi_split_cbutton = GTK_WIDGET(gtk_builder_get_object (builder, "multi_split_button"));
 
         /* Load the separator buttons from the glade builder file into the
diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.glade b/src/import-export/csv-imp/assistant-csv-trans-import.glade
index 3fc098c..2ac667d 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.glade
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.glade
@@ -855,7 +855,6 @@ For example
                 <property name="receives_default">False</property>
                 <property name="xalign">1</property>
                 <property name="image_position">right</property>
-                <property name="active">True</property>
                 <property name="draw_indicator">True</property>
                 <signal name="toggled" handler="csv_tximp_preview_skiperrors_cb" swapped="no"/>
               </object>
diff --git a/src/import-export/csv-imp/gnc-tx-import.cpp b/src/import-export/csv-imp/gnc-tx-import.cpp
index 49255db..9418438 100644
--- a/src/import-export/csv-imp/gnc-tx-import.cpp
+++ b/src/import-export/csv-imp/gnc-tx-import.cpp
@@ -66,7 +66,7 @@ GncTxImport::GncTxImport(GncImpFileFormat format)
     /* All of the data pointers are initially NULL. This is so that, if
      * gnc_csv_parse_data_free is called before all of the data is
      * initialized, only the data that needs to be freed is freed. */
-    m_parse_errors = false;
+    m_skip_errors = false;
     file_format(m_settings.m_file_format = format);
 }
 
@@ -189,17 +189,51 @@ void GncTxImport::encoding (const std::string& encoding)
 
 std::string GncTxImport::encoding () { return m_settings.m_encoding; }
 
+void GncTxImport::update_skipped_lines()
+{
+    for (uint i = 0; i < m_parsed_lines.size(); i++)
+    {
+        std::get<4>(m_parsed_lines[i]) =
+            ((i < skip_start_lines()) ||             // start rows to skip
+             (i >= m_parsed_lines.size() - skip_end_lines()) ||          // end rows to skip
+             (((i - skip_start_lines()) % 2 == 1) && // skip every second row...
+                  skip_alt_lines()) ||                   // ...if requested
+             (m_skip_errors && !std::get<1>(m_parsed_lines[i]).empty())); // skip lines with errors
+    }
+}
+
 void GncTxImport::skip_start_lines (uint num)
-    { m_settings.m_skip_start_lines = num; }
+{
+    m_settings.m_skip_start_lines = num;
+    update_skipped_lines();
+}
+
 uint GncTxImport::skip_start_lines () { return m_settings.m_skip_start_lines; }
 
-void GncTxImport::skip_end_lines (uint num) { m_settings.m_skip_end_lines = num; }
+void GncTxImport::skip_end_lines (uint num)
+{
+    m_settings.m_skip_end_lines = num;
+    update_skipped_lines();
+}
+
 uint GncTxImport::skip_end_lines () { return m_settings.m_skip_end_lines; }
 
 void GncTxImport::skip_alt_lines (bool skip)
-    { m_settings.m_skip_alt_lines = skip; }
+{
+    m_settings.m_skip_alt_lines = skip;
+    update_skipped_lines();
+}
+
 bool GncTxImport::skip_alt_lines () { return m_settings.m_skip_alt_lines; }
 
+void GncTxImport::skip_errors (bool skip)
+{
+    m_skip_errors = skip;
+    update_skipped_lines();
+}
+
+bool GncTxImport::skip_errors () { return m_skip_errors; }
+
 void GncTxImport::separators (std::string separators)
 {
     if (file_format() != GncImpFileFormat::CSV)
@@ -285,7 +319,7 @@ void GncTxImport::tokenize (bool guessColTypes)
     for (auto tokenized_line : m_tokenizer->get_tokens())
     {
         m_parsed_lines.push_back (std::make_tuple (tokenized_line, std::string(),
-                nullptr, nullptr));
+                nullptr, nullptr, false));
         auto length = tokenized_line.size();
         if (length > max_cols)
             max_cols = length;
@@ -335,15 +369,8 @@ void GncTxImport::verify_data(ErrorList& error_msg)
     auto have_amount_errors = false;
     for (uint i = 0; i < m_parsed_lines.size(); i++)
     {
-        if ((i < m_settings.m_skip_start_lines) ||             // start rows to skip
-            (i >= m_parsed_lines.size()
-                    - m_settings.m_skip_end_lines) ||          // end rows to skip
-            (((i - m_settings.m_skip_start_lines) % 2 == 1) && // skip every second row...
-                    m_settings.m_skip_alt_lines))              // ...if requested
-        {
-            std::get<1>(m_parsed_lines[i]).clear();
+        if (std::get<4>(m_parsed_lines[i])) // Ignore skipped lines
             continue;
-        }
         else
         {
             auto line_err = ErrorList();
@@ -540,7 +567,7 @@ static void trans_properties_verify_essentials (std::vector<parse_line_t>::itera
     std::shared_ptr<GncPreTrans> trans_props;
     std::shared_ptr<GncPreSplit> split_props;
 
-    std::tie(std::ignore, error_message, trans_props, split_props) = *parsed_line;
+    std::tie(std::ignore, error_message, trans_props, split_props, std::ignore) = *parsed_line;
 
     auto trans_error = trans_props->verify_essentials();
     auto split_error = split_props->verify_essentials();
@@ -571,7 +598,7 @@ std::shared_ptr<DraftTransaction> GncTxImport::trans_properties_to_trans (std::v
     std::string error_message;
     std::shared_ptr<GncPreTrans> trans_props;
     std::shared_ptr<GncPreSplit> split_props;
-    std::tie(std::ignore, error_message, trans_props, split_props) = *parsed_line;
+    std::tie(std::ignore, error_message, trans_props, split_props, std::ignore) = *parsed_line;
     auto account = split_props->get_account();
 
     QofBook* book = gnc_account_get_book (account);
@@ -621,7 +648,7 @@ void GncTxImport::create_transaction (std::vector<parse_line_t>::iterator& parse
     std::string error_message;
     auto trans_props = std::make_shared<GncPreTrans>(date_format());
     auto split_props = std::make_shared<GncPreSplit>(date_format(), currency_format());
-    std::tie(line, error_message, std::ignore, std::ignore) = *parsed_line;
+    std::tie(line, error_message, std::ignore, std::ignore, std::ignore) = *parsed_line;
     error_message.clear();
 
     /* Convert all tokens in this line into transaction/split properties. */
@@ -647,7 +674,6 @@ void GncTxImport::create_transaction (std::vector<parse_line_t>::iterator& parse
         }
         catch (const std::exception& e)
         {
-            m_parse_errors = true;
             if (!error_message.empty())
                 error_message += "\n";
             error_message += _(gnc_csv_col_type_strs[*col_types_it]);
@@ -695,7 +721,6 @@ void GncTxImport::create_transaction (std::vector<parse_line_t>::iterator& parse
         {
             // Oops - the user didn't select an Account column *and* we didn't get a default value either!
             // Note if you get here this suggests a bug in the code!
-            m_parse_errors = true;
             error_message = _("No account column selected and no default account specified either.\n"
                                        "This should never happen. Please report this as a bug.");
             PINFO("User warning: %s", error_message.c_str());
@@ -719,59 +744,38 @@ void GncTxImport::create_transaction (std::vector<parse_line_t>::iterator& parse
     }
     catch (const std::invalid_argument& e)
     {
-        m_parse_errors = true;
         error_message = e.what();
         PINFO("User warning: %s", error_message.c_str());
     }
 }
 
 
-/** Creates a list of transactions from parsed data. Transactions that
- * could be created from rows are placed in transactions; Lines that couldn't
- * be converted are marked with the failure reason. These can be redone in
- * a subsequent run with redo_errors set to true.
- * @param account Account with which transactions are created
- * @param redo_errors true to convert only error data, false to convert all data
+/** Creates a list of transactions from parsed data. The parsed data
+ * will first be validated. If any errors are found this function will
+ * throw an error unless skip_errors was set.
+ * @param skip_errors true skip over lines with errors
+ * @exception throws std::invalid_argument if data validation fails and
+ *            skip_errors wasn't set.
  */
-void GncTxImport::create_transactions (bool redo_errors)
+void GncTxImport::create_transactions ()
 {
-    /* If a full conversion is requested (as opposed to only
-     * attempting to convers the lines which had errors in the previous run)
-     * clear all errors and possibly already created transactions. */
-    if (!redo_errors)
-    {
-        /* Clear error messages on full run */
-        for (auto orig_line : m_parsed_lines)
-            std::get<1>(orig_line).clear();
-
-        /* Drop all existing draft transactions on a full run */
-        m_transactions.clear();
-    }
-
-    /* compute start and end iterators based on user-set restrictions */
-    auto parsed_lines_it = m_parsed_lines.begin();
-    std::advance(parsed_lines_it, skip_start_lines());
+    /* Start with verifying the current data. */
+    auto verify_result = verify();
+    if (!verify_result.empty() && !m_skip_errors)
+        throw std::invalid_argument (verify_result);
 
-    auto parsed_lines_max = m_parsed_lines.begin();
-    std::advance(parsed_lines_max, m_parsed_lines.size() - skip_end_lines());
+    /* Drop all existing draft transactions */
+    m_transactions.clear();
 
-    auto odd_line = false;
-    m_parse_errors = false;
     m_parent = nullptr;
 
     /* Iterate over all parsed lines */
-    for (parsed_lines_it, odd_line;
-            parsed_lines_it < parsed_lines_max;
-            ++parsed_lines_it, odd_line = !odd_line)
+    for (auto parsed_lines_it = m_parsed_lines.begin();
+            parsed_lines_it != m_parsed_lines.end();
+            ++parsed_lines_it)
     {
-        /* Skip current line if:
-           1. only looking for lines with error AND no error on current line
-           OR
-           2. looking for all lines AND
-              skip_rows is enabled AND
-              current line is an odd line */
-        if ((redo_errors && std::get<1>(*parsed_lines_it).empty()) ||
-           (!redo_errors && skip_alt_lines() && odd_line))
+        /* Skip current line if the user specified so */
+        if ((std::get<4>(*parsed_lines_it)))
             continue;
 
         try
@@ -827,26 +831,15 @@ GncTxImport::accounts ()
                            m_settings.m_column_types.end(), GncTransPropType::TACCOUNT);
     uint tacct_col = tacct_col_it - m_settings.m_column_types.begin();
 
-    /* compute start and end iterators based on user-set restrictions */
-    auto parsed_lines_it = m_parsed_lines.begin();
-    std::advance(parsed_lines_it, skip_start_lines());
-
-    auto parsed_lines_max = m_parsed_lines.begin();
-    std::advance(parsed_lines_max, m_parsed_lines.size() - skip_end_lines());
-
     /* Iterate over all parsed lines */
     auto odd_line = false;
-    for (parsed_lines_it, odd_line;
-            parsed_lines_it < parsed_lines_max;
-            ++parsed_lines_it, odd_line = !odd_line)
+    for (auto parsed_line : m_parsed_lines)
     {
-        /* Skip current line if
-           skip_rows is enabled AND
-           current line is an odd line */
-        if (skip_alt_lines() && odd_line)
+        /* Skip current line if the user specified so */
+        if ((std::get<4>(parsed_line)))
             continue;
 
-        auto col_strs = std::get<0>(*parsed_lines_it);
+        auto col_strs = std::get<0>(parsed_line);
         if ((acct_col_it != m_settings.m_column_types.end()) && !col_strs[acct_col].empty())
             accts.insert(col_strs[acct_col]);
         if ((tacct_col_it != m_settings.m_column_types.end()) && !col_strs[tacct_col].empty())
diff --git a/src/import-export/csv-imp/gnc-tx-import.hpp b/src/import-export/csv-imp/gnc-tx-import.hpp
index e556653..db5bfb3 100644
--- a/src/import-export/csv-imp/gnc-tx-import.hpp
+++ b/src/import-export/csv-imp/gnc-tx-import.hpp
@@ -75,7 +75,8 @@ extern const gchar* date_format_user[];
 using parse_line_t = std::tuple<StrVec,
                                 std::string,
                                 std::shared_ptr<GncPreTrans>,
-                                std::shared_ptr<GncPreSplit>>;
+                                std::shared_ptr<GncPreSplit>,
+                                bool>;
 
 struct ErrorList;
 
@@ -123,6 +124,9 @@ public:
     void skip_alt_lines (bool skip);
     bool skip_alt_lines ();
 
+    void skip_errors (bool skip);
+    bool skip_errors ();
+
     void separators (std::string separators);
     std::string separators ();
 
@@ -142,7 +146,7 @@ public:
     /** This function will attempt to convert all tokenized lines into
      *  transactions using the column types the user has set.
      */
-    void create_transactions (bool redo_errors);
+    void create_transactions ();
     bool check_for_column_type (GncTransPropType type);
     void set_column_type (uint position, GncTransPropType type);
     std::vector<GncTransPropType> column_types ();
@@ -155,7 +159,6 @@ public:
                                                      and split properties. */
     std::multimap <time64, std::shared_ptr<DraftTransaction>> m_transactions;  /**< map of transaction objects created
                                                      from parsed_lines and column_types, ordered by date */
-    bool m_parse_errors;          /**< Indicates whether the last parse_to_trans run had any errors */
 
 private:
     /** A helper function used by create_transactions. It will attempt
@@ -167,6 +170,8 @@ private:
     void verify_column_selections (ErrorList& error_msg);
     void verify_data(ErrorList& error_msg);
 
+    void update_skipped_lines();
+
     /* Internal helper function that does the actual conversion from property lists
      * to real (possibly unbalanced) transaction with splits.
      */
@@ -174,6 +179,7 @@ private:
 
     struct CsvTranSettings;
     CsvTransSettings m_settings;
+    bool m_skip_errors;
 
     /* The parameters below are only used while creating
      * transactions. They keep state information during the conversion.

commit cfeb1f638809dd117ba6bd1bd6a8fc234859347d
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Thu Dec 22 21:48:55 2016 +0100

    Complete error checking on the preview page
    
    - Not all possible issues were reported to the user.
    - Report column conflicts above the table, and line issues in the first column of the line having the issue

diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index 5becccf..913d98d 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -349,7 +349,6 @@ csv_tximp_preview_settings_sel_changed_cb (GtkComboBox *combo, CsvImportTrans *i
 
     csv_tximp_preview_refresh (info);
     csv_tximp_preview_handle_save_del_sensitivity (combo, info);
-    csv_tximp_preview_validate_settings (info);
 }
 
 
@@ -501,8 +500,7 @@ void csv_tximp_preview_srow_cb (GtkSpinButton *spin, CsvImportTrans *info)
     gtk_adjustment_set_upper (adj, info->tx_imp->m_parsed_lines.size()
             - info->tx_imp->skip_start_lines());
 
-    csv_tximp_preview_row_sel_update (info);
-    csv_tximp_preview_validate_settings (info);
+    csv_tximp_preview_refresh_table (info);
 }
 
 
@@ -518,8 +516,7 @@ void csv_tximp_preview_erow_cb (GtkSpinButton *spin, CsvImportTrans *info)
     gtk_adjustment_set_upper (adj, info->tx_imp->m_parsed_lines.size()
             - info->tx_imp->skip_end_lines());
 
-    csv_tximp_preview_row_sel_update (info);
-    csv_tximp_preview_validate_settings (info);
+    csv_tximp_preview_refresh_table (info);
 }
 
 
@@ -538,7 +535,6 @@ void csv_tximp_preview_multisplit_cb (GtkToggleButton *checkbox, CsvImportTrans
 {
     info->tx_imp->multi_split (gtk_toggle_button_get_active (checkbox));
     csv_tximp_preview_refresh_table (info);
-    csv_tximp_preview_validate_settings (info);
 }
 
 
@@ -547,8 +543,7 @@ void csv_tximp_preview_multisplit_cb (GtkToggleButton *checkbox, CsvImportTrans
 void csv_tximp_preview_skiprows_cb (GtkToggleButton *checkbox, CsvImportTrans *info)
 {
     info->tx_imp->skip_alt_lines (gtk_toggle_button_get_active (checkbox));
-    csv_tximp_preview_row_sel_update (info);
-    csv_tximp_preview_validate_settings (info);
+    csv_tximp_preview_refresh_table (info);
 }
 
 
@@ -609,7 +604,6 @@ void csv_tximp_preview_sep_button_cb (GtkWidget* widget, CsvImportTrans* info)
     {
         info->tx_imp->tokenize (false);
         csv_tximp_preview_refresh_table (info);
-        csv_tximp_preview_validate_settings (info);
     }
     catch (std::range_error &e)
     {
@@ -640,7 +634,6 @@ void csv_tximp_preview_acct_sel_cb (GtkWidget* widget, CsvImportTrans* info)
     auto acct = gnc_account_sel_get_account( GNC_ACCOUNT_SEL(widget) );
     info->tx_imp->base_account(acct);
     csv_tximp_preview_refresh_table (info);
-    csv_tximp_preview_validate_settings (info);
 }
 
 
@@ -661,7 +654,6 @@ void csv_tximp_preview_sep_fixed_sel_cb (GtkToggleButton* csv_button, CsvImportT
 
         info->tx_imp->tokenize (false);
         csv_tximp_preview_refresh_table (info);
-        csv_tximp_preview_validate_settings (info);
     }
     catch (std::range_error &e)
     {
@@ -702,7 +694,6 @@ void csv_tximp_preview_enc_sel_cb (GOCharmapSel* selector, const char* encoding,
             info->tx_imp->tokenize (false);
 
             csv_tximp_preview_refresh_table (info);
-            csv_tximp_preview_validate_settings (info);
 
             info->encoding_selected_called = false;
         }
@@ -728,7 +719,7 @@ void csv_tximp_preview_enc_sel_cb (GOCharmapSel* selector, const char* encoding,
 static void csv_tximp_preview_date_fmt_sel_cb (GtkComboBox* format_selector, CsvImportTrans* info)
 {
     info->tx_imp->date_format (gtk_combo_box_get_active (format_selector));
-    csv_tximp_preview_validate_settings (info);
+    csv_tximp_preview_refresh_table (info);
 }
 
 
@@ -739,7 +730,7 @@ static void csv_tximp_preview_date_fmt_sel_cb (GtkComboBox* format_selector, Csv
 static void csv_tximp_preview_currency_fmt_sel_cb (GtkComboBox* currency_selector, CsvImportTrans* info)
 {
     info->tx_imp->currency_format (gtk_combo_box_get_active (currency_selector));
-    csv_tximp_preview_validate_settings (info);
+    csv_tximp_preview_refresh_table (info);
 }
 
 
@@ -795,7 +786,6 @@ static void csv_tximp_preview_col_type_changed_cb (GtkCellRenderer* renderer, gc
 
     info->tx_imp->set_column_type (col_num, new_col_type);
     csv_tximp_preview_refresh_table (info);
-    csv_tximp_preview_validate_settings (info);
 }
 
 /*======================================================================*/
@@ -919,7 +909,6 @@ fixed_context_menu_handler (GnumericPopupMenuElement const *element,
         return false;
     }
     csv_tximp_preview_refresh_table (info);
-    csv_tximp_preview_validate_settings (info);
     return true;
 }
 static void
@@ -978,7 +967,6 @@ csv_tximp_preview_split_column (CsvImportTrans* info, int col, int dx)
         return;
     }
     csv_tximp_preview_refresh_table (info);
-    csv_tximp_preview_validate_settings (info);
 }
 
 
@@ -1046,6 +1034,8 @@ void csv_tximp_preview_row_sel_update (CsvImportTrans* info)
  */
 void csv_tximp_preview_refresh_table (CsvImportTrans* info)
 {
+    csv_tximp_preview_validate_settings (info);
+
     /* ncols is the number of columns in the file data. */
     auto column_types = info->tx_imp->column_types();
     auto ncols = column_types.size();
@@ -1194,20 +1184,17 @@ void csv_tximp_preview_refresh_table (CsvImportTrans* info)
         g_object_set_data (G_OBJECT(button), "col-num",GINT_TO_POINTER(i));
     }
 
-    /* Add a column for the error messages when reviewing errors */
-    if (info->previewing_errors)
-    {
-        auto crenderer = gtk_cell_renderer_text_new();
-        gtk_tree_view_insert_column_with_attributes (info->ctreeview,
-                -1, "", crenderer, "text", 2 * ncols, nullptr);
+    /* Add a column for the error messages */
+    auto crenderer = gtk_cell_renderer_text_new();
+    gtk_tree_view_insert_column_with_attributes (info->ctreeview,
+            0, "", crenderer, "text", 2 * ncols, nullptr);
 
-        auto renderer = gtk_cell_renderer_text_new();
-        auto col = gtk_tree_view_column_new_with_attributes ("", renderer, "background", 0,
-                                                             "text", ncols + 1, nullptr);
-        gtk_tree_view_insert_column (info->treeview, col, -1);
-        /* Enable resizing of the columns. */
-        gtk_tree_view_column_set_resizable (col, TRUE);
-    }
+    auto renderer = gtk_cell_renderer_text_new();
+    auto col = gtk_tree_view_column_new_with_attributes ("", renderer, "background", 0,
+                                                         "text", ncols + 1, nullptr);
+    gtk_tree_view_insert_column (info->treeview, col, 0);
+    /* Enable resizing of the columns. */
+    gtk_tree_view_column_set_resizable (col, TRUE);
 
     /* Select the header row */
     gtk_tree_model_get_iter_first (GTK_TREE_MODEL(ctstore), &iter);
@@ -1303,7 +1290,7 @@ void csv_tximp_preview_validate_settings (CsvImportTrans* info)
     /* Allow the user to proceed only if there are no inconsistencies in the settings */
     auto error_msg = info->tx_imp->verify();
     gtk_assistant_set_page_complete (info->csv_imp_asst, info->preview_page, error_msg.empty());
-    gtk_label_set_text(GTK_LABEL(info->instructions_label), error_msg.c_str());
+    gtk_label_set_markup(GTK_LABEL(info->instructions_label), error_msg.c_str());
     gtk_widget_set_visible (GTK_WIDGET(info->instructions_image), !error_msg.empty());
 }
 
@@ -1537,7 +1524,6 @@ csv_tximp_assist_preview_page_prepare (GtkAssistant *assistant,
 
     /* Load the data into the treeview. */
     csv_tximp_preview_refresh_table (info);
-    csv_tximp_preview_validate_settings (info);
 
 }
 
@@ -1766,8 +1752,6 @@ csv_tximp_assist_prepare_cb (GtkAssistant *assistant, GtkWidget *page,
         csv_tximp_assist_match_page_prepare (assistant, info);
     else if (page == info->summary_page)
         csv_tximp_assist_summary_page_prepare (assistant, info);
-    else
-        g_assert_not_reached();
 }
 
 
diff --git a/src/import-export/csv-imp/gnc-trans-props.cpp b/src/import-export/csv-imp/gnc-trans-props.cpp
index 6567cf9..4f29cc7 100644
--- a/src/import-export/csv-imp/gnc-trans-props.cpp
+++ b/src/import-export/csv-imp/gnc-trans-props.cpp
@@ -203,9 +203,10 @@ time64 parse_date (const std::string &date_str, int format)
 /** Convert str into a gnc_numeric using the user-specified (import) currency format.
  * @param str The string to be parsed
  * @param currency_format The currency format to use.
- * @return a gnc_numeric on success, boost::none on failure
+ * @return a gnc_numeric
+ * @exception May throw std::invalid argument if string can't be parsed properly
  */
-static boost::optional<gnc_numeric> parse_amount (const std::string &str, int currency_format)
+gnc_numeric parse_amount (const std::string &str, int currency_format)
 {
     /* If a cell is empty or just spaces return invalid amount */
     if(!boost::regex_search(str, boost::regex("[0-9]")))
@@ -255,7 +256,7 @@ static char parse_reconciled (const std::string& reconcile)
         throw std::invalid_argument ("String can't be parsed into a valid reconcile state.");
 }
 
-static gnc_commodity* parse_commodity (const std::string& comm_str)
+gnc_commodity* parse_commodity (const std::string& comm_str)
 {
     if (comm_str.empty())
         return nullptr;
@@ -575,9 +576,9 @@ static void trans_add_split (Transaction* trans, Account* account, gnc_numeric a
     if (action)
         xaccSplitSetAction (split, action->c_str());
 
-    if (rec_state && *rec_state != ' ')
+    if (rec_state && *rec_state != 'n')
         xaccSplitSetReconcile (split, *rec_state);
-    if (rec_state && *rec_state == YREC)
+    if (rec_state && *rec_state == YREC && rec_date)
         xaccSplitSetDateReconciledSecs (split, *rec_date);
 
 }
diff --git a/src/import-export/csv-imp/gnc-trans-props.hpp b/src/import-export/csv-imp/gnc-trans-props.hpp
index 45145a8..9a7e2c9 100644
--- a/src/import-export/csv-imp/gnc-trans-props.hpp
+++ b/src/import-export/csv-imp/gnc-trans-props.hpp
@@ -103,6 +103,8 @@ GncTransPropType sanitize_trans_prop (GncTransPropType prop, bool multi_split);
 
 
 time64 parse_date (const std::string &date_str, int format);
+gnc_commodity* parse_commodity (const std::string& comm_str);
+gnc_numeric parse_amount (const std::string &str, int currency_format);
 
 struct GncPreTrans
 {
diff --git a/src/import-export/csv-imp/gnc-tx-import.cpp b/src/import-export/csv-imp/gnc-tx-import.cpp
index a430b76..49255db 100644
--- a/src/import-export/csv-imp/gnc-tx-import.cpp
+++ b/src/import-export/csv-imp/gnc-tx-import.cpp
@@ -308,67 +308,157 @@ void GncTxImport::tokenize (bool guessColTypes)
     }
 }
 
-/* Test for the required minimum number of columns selected and
- * a valid date format.
- * @return An empty string if all checks passed or the reason
- *         verification failed otherwise.
- */
-std::string GncTxImport::verify ()
+
+struct ErrorList
 {
-    auto newline = std::string();
-    auto error_text = std::string();
+public:
+    void add_error (std::string msg);
+    std::string str();
+    bool empty() { return m_error.empty(); }
+private:
+    std::string m_error;
+};
+
+void ErrorList::add_error (std::string msg)
+{
+    m_error += "- " + msg + "\n";
+}
 
-    /* Check if the import file did actually contain any information */
-    if (m_parsed_lines.size() == 0)
-    {
-        error_text = _("No valid data found in the selected file. It may be empty or the selected encoding is wrong.");
-        return error_text;
-    }
+std::string ErrorList::str()
+{
+    return m_error.substr(0, m_error.size() - 1);
+}
 
-    /* Check if at least one line is selected for importing */
-    auto skip_alt_offset = m_settings.m_skip_alt_lines ? 1 : 0;
-    if (m_settings.m_skip_start_lines + m_settings.m_skip_end_lines + skip_alt_offset >= m_parsed_lines.size())
+void GncTxImport::verify_data(ErrorList& error_msg)
+{
+    auto have_date_errors = false;
+    auto have_amount_errors = false;
+    for (uint i = 0; i < m_parsed_lines.size(); i++)
     {
-        error_text = _("No lines are selected for importing. Please reduce the number of lines to skip.");
-        return error_text;
+        if ((i < m_settings.m_skip_start_lines) ||             // start rows to skip
+            (i >= m_parsed_lines.size()
+                    - m_settings.m_skip_end_lines) ||          // end rows to skip
+            (((i - m_settings.m_skip_start_lines) % 2 == 1) && // skip every second row...
+                    m_settings.m_skip_alt_lines))              // ...if requested
+        {
+            std::get<1>(m_parsed_lines[i]).clear();
+            continue;
+        }
+        else
+        {
+            auto line_err = ErrorList();
+            auto line_data = std::get<0>(m_parsed_lines[i]);
+
+            /* Attempt to parse date column values */
+            auto date_col_it = std::find(m_settings.m_column_types.begin(),
+                m_settings.m_column_types.end(), GncTransPropType::DATE);
+            if (date_col_it != m_settings.m_column_types.end())
+            try
+            {
+                auto date_col = date_col_it -m_settings.m_column_types.begin();
+                auto date_str = line_data[date_col];
+                if (!m_settings.m_multi_split || !date_str.empty())
+                    parse_date (date_str, date_format());
+            }
+            catch (...)
+            {
+                have_date_errors = true;
+                line_err.add_error(_("Date could not be understood"));
+            }
+
+            /* Attempt to parse reconcile date column values */
+            date_col_it = std::find(m_settings.m_column_types.begin(),
+                m_settings.m_column_types.end(), GncTransPropType::REC_DATE);
+            if (date_col_it != m_settings.m_column_types.end())
+            try
+            {
+                auto date_col = date_col_it -m_settings.m_column_types.begin();
+                auto date_str = line_data[date_col];
+                if (!date_str.empty())
+                    parse_date (date_str, date_format());
+            }
+            catch (...)
+            {
+                have_date_errors = true;
+                line_err.add_error(_("Reconcile date could not be understood"));
+            }
+
+            /* Attempt to parse transfer reconcile date column values */
+            date_col_it = std::find(m_settings.m_column_types.begin(),
+                m_settings.m_column_types.end(), GncTransPropType::REC_DATE);
+            if (date_col_it != m_settings.m_column_types.end())
+            try
+            {
+                auto date_col = date_col_it -m_settings.m_column_types.begin();
+                auto date_str = line_data[date_col];
+                if (!date_str.empty())
+                    parse_date (date_str, date_format());
+            }
+            catch (...)
+            {
+                have_date_errors = true;
+                line_err.add_error(_("Transfer reconcile date could not be understood"));
+            }
+
+            /* Attempt to parse deposit column values */
+            auto num_col_it = std::find(m_settings.m_column_types.begin(),
+                m_settings.m_column_types.end(), GncTransPropType::DEPOSIT);
+            if (num_col_it != m_settings.m_column_types.end())
+            try
+            {
+                auto num_col = num_col_it -m_settings.m_column_types.begin();
+                auto num_str = line_data[num_col];
+                if (!m_settings.m_multi_split || !num_str.empty())
+                    parse_amount (num_str, currency_format());
+            }
+            catch (...)
+            {
+                have_amount_errors = true;
+                line_err.add_error(_("Deposit amount could not be understood"));
+            }
+
+            /* Attempt to parse withdrawal column values */
+            num_col_it = std::find(m_settings.m_column_types.begin(),
+                m_settings.m_column_types.end(), GncTransPropType::WITHDRAWAL);
+            if (num_col_it != m_settings.m_column_types.end())
+            try
+            {
+                auto num_col = num_col_it -m_settings.m_column_types.begin();
+                auto num_str = line_data[num_col];
+                if (!m_settings.m_multi_split || !num_str.empty())
+                    parse_amount (num_str, currency_format());
+            }
+            catch (...)
+            {
+                have_amount_errors = true;
+                line_err.add_error(_("Withdrawal amount could not be understood"));
+            }
+
+            if (!line_err.empty())
+                std::get<1>(m_parsed_lines[i]) = line_err.str();
+            else
+                std::get<1>(m_parsed_lines[i]).clear();
+        }
     }
 
+    if (have_date_errors)
+        error_msg.add_error( _("Not all dates could be parsed. Please verify your chosen date format or adjust the lines to skip."));
+    if (have_amount_errors)
+        error_msg.add_error( _("Not all amounts could be parsed. Please verify your chosen currency format or adjust the lines to skip."));
+}
+
+
+/* Test for the required minimum number of columns selected and
+ * the selection is consistent.
+ * @param An ErrorList object to which all found issues are added.
+ */
+void GncTxImport::verify_column_selections (ErrorList& error_msg)
+{
+
     /* Verify if a date column is selected and it's parsable.
      */
     if (!check_for_column_type(GncTransPropType::DATE))
-    {
-        error_text += newline + _("Please select a date column.");
-        newline = "\n";
-    }
-    else
-        /* Attempt to parse the date column for each selected line */
-        try
-        {
-            auto date_col = std::find(m_settings.m_column_types.begin(),
-                    m_settings.m_column_types.end(), GncTransPropType::DATE) -
-                            m_settings.m_column_types.begin();
-            for (uint i = 0; i < m_parsed_lines.size(); i++)
-            {
-                if ((i < m_settings.m_skip_start_lines) ||             // start rows to skip
-                    (i >= m_parsed_lines.size()
-                            - m_settings.m_skip_end_lines) ||          // end rows to skip
-                    (((i - m_settings.m_skip_start_lines) % 2 == 1) && // skip every second row...
-                            m_settings.m_skip_alt_lines))              // ...if requested
-                    continue;
-                else
-                {
-                    auto first_line = std::get<0>(m_parsed_lines[i]);
-                    auto date_str = first_line[date_col];
-                    if (!date_str.empty())
-                        parse_date (date_str, date_format());
-                }
-            }
-        }
-        catch (...)
-        {
-            error_text += newline + _("Not all dates could be parsed. Please verify your chosen date format or adjust the lines to skip.");
-            newline = "\n";
-        }
+        error_msg.add_error( _("Please select a date column."));
 
     /* Verify if an account is selected either in the base account selector
      * or via a column in the import data.
@@ -376,33 +466,21 @@ std::string GncTxImport::verify ()
     if (!check_for_column_type(GncTransPropType::ACCOUNT))
     {
         if (m_settings.m_multi_split)
-        {
-            error_text += newline + _("Please select an account column.");
-            newline = "\n";
-        }
+            error_msg.add_error( _("Please select an account column."));
         else if (!m_settings.m_base_account)
-        {
-            error_text += newline + _("Please select an account column or set a base account in the Account field.");
-            newline = "\n";
-        }
+            error_msg.add_error( _("Please select an account column or set a base account in the Account field."));
     }
 
     /* Verify a description column is selected.
      */
     if (!check_for_column_type(GncTransPropType::DESCRIPTION))
-    {
-        error_text += newline + _("Please select a description column.");
-        newline = "\n";
-    }
+        error_msg.add_error( _("Please select a description column."));
 
     /* Verify at least one amount column (deposit or withdrawal) column is selected.
      */
     if (!check_for_column_type(GncTransPropType::DEPOSIT) &&
         !check_for_column_type(GncTransPropType::WITHDRAWAL))
-    {
-        error_text += newline + _("Please select a deposit or withdrawal column.");
-        newline = "\n";
-    }
+        error_msg.add_error( _("Please select a deposit or withdrawal column."));
 
     /* Verify a transfer account is selected if any of the other transfer properties
      * are selected.
@@ -412,12 +490,38 @@ std::string GncTxImport::verify ()
          check_for_column_type(GncTransPropType::TREC_STATE) ||
          check_for_column_type(GncTransPropType::TREC_DATE)) &&
         !check_for_column_type(GncTransPropType::TACCOUNT))
+        error_msg.add_error( _("Please select a transfer account column or remove the other transfer related columns."));
+}
+
+
+/* Test for the required minimum number of columns selected and
+ * a valid date format.
+ * @return An empty string if all checks passed or the reason
+ *         verification failed otherwise.
+ */
+std::string GncTxImport::verify ()
+{
+    auto newline = std::string();
+    auto error_msg = ErrorList();
+
+    /* Check if the import file did actually contain any information */
+    if (m_parsed_lines.size() == 0)
+    {
+        error_msg.add_error(_("No valid data found in the selected file. It may be empty or the selected encoding is wrong."));
+        return error_msg.str();
+    }
+
+    /* Check if at least one line is selected for importing */
+    auto skip_alt_offset = m_settings.m_skip_alt_lines ? 1 : 0;
+    if (m_settings.m_skip_start_lines + m_settings.m_skip_end_lines + skip_alt_offset >= m_parsed_lines.size())
     {
-        error_text += newline + _("Please select a transfer account column or remove the other transfer related columns.");
-        newline = "\n";
+        error_msg.add_error(_("No lines are selected for importing. Please reduce the number of lines to skip."));
+        return error_msg.str();
     }
 
-    return error_text;
+    verify_column_selections (error_msg);
+    verify_data (error_msg);
+    return error_msg.str();
 }
 
 
diff --git a/src/import-export/csv-imp/gnc-tx-import.hpp b/src/import-export/csv-imp/gnc-tx-import.hpp
index d7ac519..e556653 100644
--- a/src/import-export/csv-imp/gnc-tx-import.hpp
+++ b/src/import-export/csv-imp/gnc-tx-import.hpp
@@ -77,6 +77,8 @@ using parse_line_t = std::tuple<StrVec,
                                 std::shared_ptr<GncPreTrans>,
                                 std::shared_ptr<GncPreSplit>>;
 
+struct ErrorList;
+
 /** The actual TxImport class
  * It's intended to use in the following sequence of actions:
  * - set a file format
@@ -162,12 +164,8 @@ private:
      */
     void create_transaction (std::vector<parse_line_t>::iterator& parsed_line);
 
-    /** A helper function used by create_transactions. If the input data has
-     *  a balance column (an no deposit and withdrawal columns)
-     *  it will iterate over all created transactions
-     *  to set the split amount(s) based on the desired balance for that line.
-     */
-    void adjust_balances (void);
+    void verify_column_selections (ErrorList& error_msg);
+    void verify_data(ErrorList& error_msg);
 
     /* Internal helper function that does the actual conversion from property lists
      * to real (possibly unbalanced) transaction with splits.

commit 522b75ee18c39bd27cb5b70f35b110dd99c632a2
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Tue Dec 20 21:18:28 2016 +0100

    Move account searching to GncTxImport as it's responsible for the data
    
    And use c++ containers to do the heavy lifting

diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index 996e046..5becccf 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -74,8 +74,6 @@ struct  CsvImportTrans
 
     GtkAssistant    *csv_imp_asst;
 
-    GtkWidget       *start_page;                    /**< Assistant start page widget */
-
     GtkWidget       *file_page;                     /**< Assistant file page widget */
     GtkWidget       *file_chooser;                  /**< The widget for the file chooser */
     std::string      file_name;                     /**< The import file name */
@@ -178,7 +176,7 @@ void csv_tximp_preview_row_sel_update (CsvImportTrans* info);
 void csv_tximp_preview_refresh_table (CsvImportTrans* info);
 void csv_tximp_preview_refresh (CsvImportTrans *info);
 void csv_tximp_preview_validate_settings (CsvImportTrans* info);
-bool csv_tximp_acct_match_get_list_of_accounts (CsvImportTrans* info, GtkTreeModel *store);
+void csv_tximp_acct_match_set_accounts (CsvImportTrans* info);
 
 /*************************************************************************/
 
@@ -1314,97 +1312,24 @@ void csv_tximp_preview_validate_settings (CsvImportTrans* info)
  * Code related to the account match page
  **************************************************/
 
-/* Test for the string being in the liststore
- * Returns true if it is or false if not.
- *
- * @param liststore The data being reviewed
- * @param string to check for
- */
-static bool
-csv_tximp_acct_match_check_for_duplicates (GtkListStore *liststore, const gchar *string)
-{
-    GtkTreeIter iter;
-    auto valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL(liststore), &iter);
-    while (valid)
-    {
-        gchar *text;
-        // Walk through the list, reading each row of column string
-        gtk_tree_model_get (GTK_TREE_MODEL(liststore), &iter, MAPPING_STRING, &text, -1);
-
-        if(!(g_strcmp0 (text, string)))
-        {
-            g_free (text);
-            return true;
-        }
-        g_free (text);
-
-        valid = gtk_tree_model_iter_next (GTK_TREE_MODEL(liststore), &iter);
-    }
-    return false;
-}
-
-
-/* Get the list of accounts
- * Returns true if we have any accounts
+/* Populates the account match view with all potential
+ * account names found in the parse data.
  *
  * @param info The data being previewed
- * @param store for the account match page
  */
-bool csv_tximp_acct_match_get_list_of_accounts (CsvImportTrans* info, GtkTreeModel *store)
+void csv_tximp_acct_match_set_accounts (CsvImportTrans* info)
 {
-    /* ctstore contains the actual strings appearing in the column types treeview. */
-    auto ctstore = gtk_tree_view_get_model (info->ctreeview);
-    GtkTreeIter ct_iter;
-    gtk_tree_model_get_iter_first (ctstore, &ct_iter);
-
-    /* datastore contains the actual strings appearing in the preview treeview. */
-    auto datastore = gtk_tree_view_get_model (info->treeview);
-    auto have_accounts = false;
-    for (uint j = info->tx_imp->skip_start_lines();
-            j < info->tx_imp->m_parsed_lines.size() - info->tx_imp->skip_end_lines() - 1;
-            j++)
+    auto store = gtk_tree_view_get_model (GTK_TREE_VIEW(info->account_match_view));
+    gtk_list_store_clear (GTK_LIST_STORE(store));
+
+    auto accts = info->tx_imp->accounts();
+    for (auto acct : accts)
     {
-        /* Go through each of the columns. */
-        for (uint i = 0; i < info->tx_imp->column_types().size(); i++)
-        {
-            auto col_type = GncTransPropType::NONE;
-
-            /* Get the column type. Store is arranged so that every two
-             * columns is a pair of
-             * - the column type as a user visible (translated) string
-             * - the internal type for this column
-             * So store looks like:
-             * col_type_str 0, col_type, col_type_str 1, col_type 1, ..., col_type_str ncols, col_type ncols. */
-            gtk_tree_model_get (ctstore, &ct_iter, 2 * i + 1, &col_type, -1);
-
-            /* We're only interested in columns of type ACCOUNT and OACCOUNT. */
-            if ((col_type == GncTransPropType::ACCOUNT) || (col_type == GncTransPropType::TACCOUNT))
-            {
-                /* Get an iterator for the row in the data store. */
-                GtkTreeIter data_iter;
-                if (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL(datastore), &data_iter, nullptr, j))
-                {
-                    gchar* accstr = nullptr;   /* The string in this column from datastore. */
-                    gtk_tree_model_get (datastore, &data_iter, i + 1, &accstr, -1);
-                    if (!accstr || *accstr == '\0')
-                        continue;
-
-                    // Append the entry
-                    if (!csv_tximp_acct_match_check_for_duplicates (GTK_LIST_STORE(store), accstr))
-                    {
-                        GtkTreeIter acct_iter;
-                        gtk_list_store_append (GTK_LIST_STORE(store), &acct_iter);
-                        gtk_list_store_set (GTK_LIST_STORE(store), &acct_iter, MAPPING_STRING, accstr,
-                                            MAPPING_FULLPATH, _("No Linked Account"), MAPPING_ACCOUNT, nullptr, -1);
-                        have_accounts = true;
-                    }
-                    g_free (accstr);
-                }
-            }
-        }
+        GtkTreeIter acct_iter;
+        gtk_list_store_append (GTK_LIST_STORE(store), &acct_iter);
+        gtk_list_store_set (GTK_LIST_STORE(store), &acct_iter, MAPPING_STRING, acct.c_str(),
+                            MAPPING_FULLPATH, _("No Linked Account"), MAPPING_ACCOUNT, nullptr, -1);
     }
-
-    return have_accounts;
 }
 
 
@@ -1416,111 +1341,70 @@ csv_tximp_acct_match_check_all (GtkTreeModel *model)
     auto valid = gtk_tree_model_get_iter_first (model, &iter);
 
     // Walk through the store looking for nullptr accounts
-    auto ret = true;
     while (valid)
     {
         Account *account;
-
-        // Walk through the list, reading each row
         gtk_tree_model_get (model, &iter, MAPPING_ACCOUNT, &account, -1);
-
-        if (account == nullptr)
-            ret = false;
+        if (!account)
+            return false;
 
         valid = gtk_tree_model_iter_next (model, &iter);
     }
-    return ret;
+    return true;
 }
 
 
-/* Parse the text splitting into a path and the last_part based on
- * account separator. If the path is valid, add the last_part and
- * return this. If the path is invalid, use the new separator path
- * with the last_part and return that so there is only one new
- * account dialog.
+/* Evaluate acct_name as a full account name. Try if it
+ * contains a path to an existing parent account. If not,
+ * alter the full path name to use a fake separator to
+ * avoid calling multiple new account windows for each
+ * non-existent parent account.
  */
-static gchar *
-csv_tximp_acct_match_text_parse (gchar *text)
+static std::string
+csv_tximp_acct_match_text_parse (std::string acct_name)
 {
-    // Split the incoming string by current separator
     auto sep = gnc_get_account_separator_string ();
-    auto names = g_strsplit (text, sep, -1);
+    auto sep_pos = acct_name.rfind(sep);
+    if (sep_pos == std::string::npos)
+        // No separators found in acct_name -> return as is
+        return acct_name;
 
-    /* find the last_part and count parts */
-    int count = 0;
-    gchar *last_part = nullptr;
-    for (auto ptr = names; *ptr; ptr++)
-    {
-        if (g_strcmp0 (*ptr,"") != 0) //separator is last in string
-        {
-            if (last_part)
-                g_free (last_part);
-            last_part = g_strdup (*ptr);
-            count = count + 1;
-        }
-    }
+    auto parent = acct_name.substr(0, sep_pos);
+    auto root = gnc_get_current_root_account ();
 
-    // If count is 1 we have no path
-    gchar *ret_string = nullptr;
-    if (count == 1)
-        ret_string = g_strdup (last_part);
+    if (gnc_account_lookup_by_full_name (root, parent.c_str()))
+        // acct_name's parent matches an existing account -> acct_name as is
+        return acct_name;
     else
     {
-        // Start to create two paths based on current and possibly new separator
-        auto sep_path = g_strdup (names[0]);
-        auto newsep_path = g_strdup (names[0]);
-
-        // Setup an alternative separator
+        // Acct name doesn't match an existing account
+        // -> return the name with a fake separator to avoid
+        // asking the user to create each intermediary account as well
         const gchar *alt_sep;
         if (g_strcmp0 (sep,":") == 0)
             alt_sep = "-";
         else
             alt_sep = ":";
-
-        // Join the parts together apart from last_part which could be account name
-        for (int i = 1; i < count - 1; i++)
-        {
-            gchar *temp_sep_path, *temp_newsep_path;
-
-            temp_sep_path = g_strdup (sep_path);
-            temp_newsep_path = g_strdup (newsep_path);
-            g_free (sep_path);
-            g_free (newsep_path);
-            sep_path = g_strconcat (temp_sep_path, sep, names[i], nullptr);
-            newsep_path = g_strconcat (temp_newsep_path, alt_sep, names[i], nullptr);
-            g_free (temp_sep_path);
-            g_free (temp_newsep_path);
-        }
-        auto book = gnc_get_current_book ();
-        auto account = gnc_account_lookup_by_full_name (gnc_book_get_root_account (book), sep_path);
-
-        if (!account) // path not found
-            ret_string = g_strconcat (newsep_path, alt_sep, last_part, nullptr);
-        else
-            ret_string = g_strconcat (sep_path, sep, last_part, nullptr);
-
-        g_free (sep_path);
-        g_free (newsep_path);
+        sep_pos = acct_name.find(sep);
+        for (sep_pos = acct_name.find(sep); sep_pos != std::string::npos;
+                sep_pos = acct_name.find(sep))
+            acct_name.replace (sep_pos, strlen(sep), alt_sep);
+        return acct_name;
     }
-    g_free (last_part);
-    g_strfreev (names);
-    return ret_string;
 }
 
 static void
 csv_tximp_acct_match_select_internal(CsvImportTrans* info, GtkTreeModel *model, GtkTreeIter& iter)
 {
-    // Get the the String
+    // Get the the stored string and account (if any)
     gchar *text = nullptr;
-    gtk_tree_model_get (model, &iter, MAPPING_STRING, &text, -1);
-
-    // Get the pointer to the Account
     Account *account = nullptr;
-    gtk_tree_model_get (model, &iter, MAPPING_ACCOUNT, &account, -1);
+    gtk_tree_model_get (model, &iter, MAPPING_STRING, &text,
+                                      MAPPING_ACCOUNT, &account, -1);
 
-    auto parsed_text = csv_tximp_acct_match_text_parse (text);
+    auto acct_name = csv_tximp_acct_match_text_parse (text);
     auto gnc_acc = gnc_import_select_account (nullptr, nullptr, true,
-            parsed_text, nullptr, ACCT_TYPE_NONE, account, nullptr);
+            acct_name.c_str(), nullptr, ACCT_TYPE_NONE, account, nullptr);
 
     if (gnc_acc) // We may have canceled
     {
@@ -1535,10 +1419,9 @@ csv_tximp_acct_match_select_internal(CsvImportTrans* info, GtkTreeModel *model,
         g_free (fullpath);
     }
     g_free (text);
-    g_free (parsed_text);
 
-    if (csv_tximp_acct_match_check_all (model))
-        gtk_assistant_set_page_complete (info->csv_imp_asst, info->account_match_page, true);
+    gtk_assistant_set_page_complete (info->csv_imp_asst, info->account_match_page,
+            csv_tximp_acct_match_check_all (model));
 
 }
 
@@ -1663,11 +1546,10 @@ csv_tximp_assist_account_match_page_prepare (GtkAssistant *assistant,
         CsvImportTrans* info)
 {
     // Load the account strings into the store
-    auto store = gtk_tree_view_get_model (GTK_TREE_VIEW(info->account_match_view));
-    gtk_list_store_clear (GTK_LIST_STORE(store));
-    csv_tximp_acct_match_get_list_of_accounts (info, store);
+    csv_tximp_acct_match_set_accounts (info);
 
     // Match the account strings to the mappings
+    auto store = gtk_tree_view_get_model (GTK_TREE_VIEW(info->account_match_view));
     gnc_csv_account_map_load_mappings (store);
 
     auto text = std::string ("<span size=\"medium\" color=\"red\"><b>");
@@ -1979,9 +1861,6 @@ csv_tximp_assist_create (CsvImportTrans *info)
                                      GTK_WIDGET(gtk_builder_get_object (builder, "summary_page")),
                                      TRUE);
 
-    /* Start Page */
-    info->start_page = GTK_WIDGET(gtk_builder_get_object (builder, "start_page"));
-
     /* File chooser Page */
     info->file_page = GTK_WIDGET(gtk_builder_get_object (builder, "file_page"));
     info->file_chooser = gtk_file_chooser_widget_new (GTK_FILE_CHOOSER_ACTION_OPEN);
diff --git a/src/import-export/csv-imp/gnc-tx-import.cpp b/src/import-export/csv-imp/gnc-tx-import.cpp
index 189011d..a430b76 100644
--- a/src/import-export/csv-imp/gnc-tx-import.cpp
+++ b/src/import-export/csv-imp/gnc-tx-import.cpp
@@ -711,3 +711,43 @@ std::vector<GncTransPropType> GncTxImport::column_types ()
 {
     return m_settings.m_column_types;
 }
+
+std::set<std::string>
+GncTxImport::accounts ()
+{
+    auto accts = std::set<std::string>();
+    auto acct_col_it = std::find (m_settings.m_column_types.begin(),
+                           m_settings.m_column_types.end(), GncTransPropType::ACCOUNT);
+    uint acct_col = acct_col_it - m_settings.m_column_types.begin();
+    auto tacct_col_it = std::find (m_settings.m_column_types.begin(),
+                           m_settings.m_column_types.end(), GncTransPropType::TACCOUNT);
+    uint tacct_col = tacct_col_it - m_settings.m_column_types.begin();
+
+    /* compute start and end iterators based on user-set restrictions */
+    auto parsed_lines_it = m_parsed_lines.begin();
+    std::advance(parsed_lines_it, skip_start_lines());
+
+    auto parsed_lines_max = m_parsed_lines.begin();
+    std::advance(parsed_lines_max, m_parsed_lines.size() - skip_end_lines());
+
+    /* Iterate over all parsed lines */
+    auto odd_line = false;
+    for (parsed_lines_it, odd_line;
+            parsed_lines_it < parsed_lines_max;
+            ++parsed_lines_it, odd_line = !odd_line)
+    {
+        /* Skip current line if
+           skip_rows is enabled AND
+           current line is an odd line */
+        if (skip_alt_lines() && odd_line)
+            continue;
+
+        auto col_strs = std::get<0>(*parsed_lines_it);
+        if ((acct_col_it != m_settings.m_column_types.end()) && !col_strs[acct_col].empty())
+            accts.insert(col_strs[acct_col]);
+        if ((tacct_col_it != m_settings.m_column_types.end()) && !col_strs[tacct_col].empty())
+            accts.insert(col_strs[tacct_col]);
+    }
+
+    return accts;
+}
diff --git a/src/import-export/csv-imp/gnc-tx-import.hpp b/src/import-export/csv-imp/gnc-tx-import.hpp
index 49ef643..d7ac519 100644
--- a/src/import-export/csv-imp/gnc-tx-import.hpp
+++ b/src/import-export/csv-imp/gnc-tx-import.hpp
@@ -37,6 +37,7 @@ extern "C" {
 }
 
 #include <vector>
+#include <set>
 #include <map>
 #include <memory>
 
@@ -144,6 +145,8 @@ public:
     void set_column_type (uint position, GncTransPropType type);
     std::vector<GncTransPropType> column_types ();
 
+    std::set<std::string> accounts ();
+
     std::unique_ptr<GncTokenizer> m_tokenizer;    /**< Will handle file loading/encoding conversion/splitting into fields */
     std::vector<parse_line_t> m_parsed_lines;     /**< source file parsed into a two-dimensional array of strings.
                                                      Per line also holds possible error messages and objects with extracted transaction

commit 1a1a3fc9512ca45b08269c94541ebffed9f760a6
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Tue Dec 20 17:42:44 2016 +0100

    Remove redundant code

diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index 48c1e69..996e046 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -165,7 +165,6 @@ void csv_tximp_acct_match_button_clicked_cb (GtkWidget *widget, CsvImportTrans*
 bool csv_tximp_acct_match_view_clicked_cb (GtkWidget *widget, GdkEventButton *event, CsvImportTrans* info);
 }
 
-void csv_tximp_assist_start_page_prepare (GtkAssistant *gtkassistant, CsvImportTrans* info);
 void csv_tximp_assist_file_page_prepare (GtkAssistant *assistant, CsvImportTrans* info);
 void csv_tximp_assist_preview_page_prepare (GtkAssistant *gtkassistant, CsvImportTrans* info);
 void csv_tximp_assist_account_match_page_prepare (GtkAssistant *assistant, CsvImportTrans* info);
@@ -195,10 +194,7 @@ bool csv_tximp_acct_match_get_list_of_accounts (CsvImportTrans* info, GtkTreeMod
 void
 csv_tximp_file_confirm_cb (GtkWidget *button, CsvImportTrans *info)
 {
-    auto num = gtk_assistant_get_current_page (info->csv_imp_asst);
-    auto page = gtk_assistant_get_nth_page (info->csv_imp_asst, num);
-
-    gtk_assistant_set_page_complete (info->csv_imp_asst, page, false);
+    gtk_assistant_set_page_complete (info->csv_imp_asst, info->account_match_page, false);
 
     auto file_name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(info->file_chooser));
     if (!file_name)
@@ -252,7 +248,8 @@ csv_tximp_file_confirm_cb (GtkWidget *button, CsvImportTrans *info)
     csv_tximp_preview_populate_settings_combo(info->settings_combo);
     gtk_combo_box_set_active (info->settings_combo, 0);
 
-    gtk_assistant_set_page_complete (info->csv_imp_asst, page, true);
+    gtk_assistant_set_page_complete (info->csv_imp_asst, info->account_match_page, true);
+    auto num = gtk_assistant_get_current_page (info->csv_imp_asst);
     gtk_assistant_set_current_page (info->csv_imp_asst, num + 1);
 }
 
@@ -1522,7 +1519,7 @@ csv_tximp_acct_match_select_internal(CsvImportTrans* info, GtkTreeModel *model,
     gtk_tree_model_get (model, &iter, MAPPING_ACCOUNT, &account, -1);
 
     auto parsed_text = csv_tximp_acct_match_text_parse (text);
-    auto gnc_acc = gnc_import_select_account (nullptr, nullptr, 1,
+    auto gnc_acc = gnc_import_select_account (nullptr, nullptr, true,
             parsed_text, nullptr, ACCT_TYPE_NONE, account, nullptr);
 
     if (gnc_acc) // We may have canceled
@@ -1592,28 +1589,9 @@ csv_tximp_acct_match_view_clicked_cb (GtkWidget *widget, GdkEventButton *event,
  *******************************************************/
 
 void
-csv_tximp_assist_start_page_prepare (GtkAssistant *assistant,
-        CsvImportTrans* info)
-{
-    auto num = gtk_assistant_get_current_page (assistant);
-    auto page = gtk_assistant_get_nth_page (assistant, num);
-
-    // Clear the treemodel list store
-    auto store = gtk_tree_view_get_model (GTK_TREE_VIEW(info->account_match_view));
-    gtk_list_store_clear (GTK_LIST_STORE(store));
-
-    /* Enable the Assistant Buttons */
-    gtk_assistant_set_page_complete (assistant, page, true);
-}
-
-
-void
 csv_tximp_assist_file_page_prepare (GtkAssistant *assistant,
         CsvImportTrans* info)
 {
-    auto num = gtk_assistant_get_current_page (assistant);
-    auto page = gtk_assistant_get_nth_page (assistant, num);
-
     info->previewing_errors = false; // We're looking at all the data.
     info->skip_errors = false; // Set skip_errors to False to start with.
 
@@ -1626,7 +1604,7 @@ csv_tximp_assist_file_page_prepare (GtkAssistant *assistant,
     }
 
     /* Disable the Forward Assistant Button */
-    gtk_assistant_set_page_complete (assistant, page, false);
+    gtk_assistant_set_page_complete (assistant, info->account_match_page, false);
 }
 
 
@@ -1670,17 +1648,9 @@ csv_tximp_assist_preview_page_prepare (GtkAssistant *assistant,
         gtk_widget_show (GTK_WIDGET(info->skip_errors_button));
         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->skip_errors_button), FALSE);
     }
-    else
-    {
-        // Load the account strings into the store
-        auto store = gtk_tree_view_get_model (GTK_TREE_VIEW(info->account_match_view));
-        gtk_list_store_clear (GTK_LIST_STORE(store)); // Clear list of accounts unless we are looking at errors
-    }
 
     /* Disable the Forward Assistant Button */
-    auto num = gtk_assistant_get_current_page (assistant);
-    auto page = gtk_assistant_get_nth_page (assistant, num);
-    gtk_assistant_set_page_complete (assistant, page, false);
+    gtk_assistant_set_page_complete (assistant, info->preview_page, false);
 
     /* Load the data into the treeview. */
     csv_tximp_preview_refresh_table (info);
@@ -1694,6 +1664,7 @@ csv_tximp_assist_account_match_page_prepare (GtkAssistant *assistant,
 {
     // Load the account strings into the store
     auto store = gtk_tree_view_get_model (GTK_TREE_VIEW(info->account_match_view));
+    gtk_list_store_clear (GTK_LIST_STORE(store));
     csv_tximp_acct_match_get_list_of_accounts (info, store);
 
     // Match the account strings to the mappings
@@ -1901,9 +1872,7 @@ csv_tximp_assist_prepare_cb (GtkAssistant *assistant, GtkWidget *page,
     // Reset callcount on every prepare
     info->callcount = 0;
 
-    if (page == info->start_page)
-        csv_tximp_assist_start_page_prepare (assistant, info);
-    else if (page == info->file_page)
+    if (page == info->file_page)
         csv_tximp_assist_file_page_prepare (assistant, info);
     else if (page == info->preview_page)
         csv_tximp_assist_preview_page_prepare (assistant, info);
@@ -1990,7 +1959,7 @@ csv_tximp_assist_create (CsvImportTrans *info)
     /* Enable buttons on all page. */
     gtk_assistant_set_page_complete (info->csv_imp_asst,
                                      GTK_WIDGET(gtk_builder_get_object (builder, "start_page")),
-                                     TRUE);
+                                     true);
     gtk_assistant_set_page_complete (info->csv_imp_asst,
                                      GTK_WIDGET(gtk_builder_get_object (builder, "file_page")),
                                      FALSE);

commit e4fc93ffe09fe309174997d781c112c9dad4410d
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Tue Dec 20 17:03:07 2016 +0100

    Cleanup round - move functions up and down to group related ones

diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index 163482f..48c1e69 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -173,22 +173,97 @@ void csv_tximp_assist_doc_page_prepare (GtkAssistant *assistant, CsvImportTrans*
 void csv_tximp_assist_match_page_prepare (GtkAssistant *assistant, CsvImportTrans* info);
 void csv_tximp_assist_summary_page_prepare (GtkAssistant *assistant, CsvImportTrans* info);
 
+void csv_tximp_preview_populate_settings_combo(GtkComboBox* combo);
+void csv_tximp_preview_handle_save_del_sensitivity (GtkComboBox* combo, CsvImportTrans *info);
+void csv_tximp_preview_row_sel_update (CsvImportTrans* info);
 void csv_tximp_preview_refresh_table (CsvImportTrans* info);
 void csv_tximp_preview_refresh (CsvImportTrans *info);
-void csv_tximp_validate_preview_settings (CsvImportTrans* info);
-bool get_list_of_accounts (CsvImportTrans* info, GtkTreeModel *store);
+void csv_tximp_preview_validate_settings (CsvImportTrans* info);
+bool csv_tximp_acct_match_get_list_of_accounts (CsvImportTrans* info, GtkTreeModel *store);
 
 /*************************************************************************/
 
 
+/**************************************************
+ * Code related to the file chooser page
+ **************************************************/
 
-/*******************************************************
- * populate_settings_combo
+/* csv_tximp_file_confirm_cb
  *
- * Set the available presets in the settings combo box
- *******************************************************/
+ * call back for ok button in file chooser widget
+ */
+void
+csv_tximp_file_confirm_cb (GtkWidget *button, CsvImportTrans *info)
+{
+    auto num = gtk_assistant_get_current_page (info->csv_imp_asst);
+    auto page = gtk_assistant_get_nth_page (info->csv_imp_asst, num);
+
+    gtk_assistant_set_page_complete (info->csv_imp_asst, page, false);
+
+    auto file_name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(info->file_chooser));
+    if (!file_name)
+        return;
+
+    auto filepath = gnc_uri_get_path (file_name);
+    auto starting_dir = g_path_get_dirname (filepath);
+
+    info->file_name = file_name;
+    gnc_set_default_directory (GNC_PREFS_GROUP, starting_dir);
+
+    DEBUG("file_name selected is %s", info->file_name.c_str());
+    DEBUG("starting directory is %s", starting_dir);
+
+    g_free (filepath);
+    g_free (file_name);
+    g_free (starting_dir);
+
+    /* Load the file into parse_data. */
+    auto parse_data = new GncTxImport;
+    /* Assume data is CSV. User can later override to Fixed Width if needed */
+    try
+    {
+        parse_data->file_format (GncImpFileFormat::CSV);
+        parse_data->load_file (info->file_name);
+        parse_data->tokenize (true);
+    }
+    catch (std::ifstream::failure& e)
+    {
+        /* File loading failed ... */
+        gnc_error_dialog (nullptr, "%s", e.what());
+            delete parse_data;
+            return;
+    }
+    catch (std::range_error &e)
+    {
+        /* Parsing failed ... */
+        gnc_error_dialog (nullptr, "%s", e.what());
+        delete parse_data;
+        return;
+    }
+
+    if (info->tx_imp) // Free parse_data if we have come back here
+        delete info->tx_imp;
+    info->tx_imp = parse_data;
+    info->previewing_errors = false; /* We're looking at all the data. */
+    info->skip_errors = false; // Set skip_errors to False
+    csv_tximp_preview_refresh (info);
+
+    /* Get settings store and populate */
+    csv_tximp_preview_populate_settings_combo(info->settings_combo);
+    gtk_combo_box_set_active (info->settings_combo, 0);
 
-static void populate_settings_combo(GtkComboBox* combo)
+    gtk_assistant_set_page_complete (info->csv_imp_asst, page, true);
+    gtk_assistant_set_current_page (info->csv_imp_asst, num + 1);
+}
+
+
+/**************************************************
+ * Code related to the preview page
+ **************************************************/
+
+/* Set the available presets in the settings combo box
+ */
+void csv_tximp_preview_populate_settings_combo(GtkComboBox* combo)
 {
     // Clear the list store
     auto model = gtk_combo_box_get_model (combo);
@@ -211,8 +286,10 @@ static void populate_settings_combo(GtkComboBox* combo)
     }
 }
 
-
-static void handle_save_del_sensitivity (GtkComboBox* combo, CsvImportTrans *info)
+/* Enable or disable the save and delete settings buttons
+ * depending on what is selected and entered as settings name
+ */
+void csv_tximp_preview_handle_save_del_sensitivity (GtkComboBox* combo, CsvImportTrans *info)
 {
     GtkTreeIter iter;
     auto can_delete = false;
@@ -243,11 +320,9 @@ static void handle_save_del_sensitivity (GtkComboBox* combo, CsvImportTrans *inf
 }
 
 
-/*******************************************************
- * csv_tximp_preview_settings_sel_changed_cb
- *
- * Triggered when a preset is selected in the settings combo.
- *******************************************************/
+/* Use selected preset to configure the import. Triggered when
+ * a preset is selected in the settings combo.
+ */
 void
 csv_tximp_preview_settings_sel_changed_cb (GtkComboBox *combo, CsvImportTrans *info)
 {
@@ -278,16 +353,13 @@ csv_tximp_preview_settings_sel_changed_cb (GtkComboBox *combo, CsvImportTrans *i
     }
 
     csv_tximp_preview_refresh (info);
-    handle_save_del_sensitivity (combo, info);
-    csv_tximp_validate_preview_settings (info);
+    csv_tximp_preview_handle_save_del_sensitivity (combo, info);
+    csv_tximp_preview_validate_settings (info);
 }
 
 
-/*******************************************************
- * csv_tximp_preview_settings_text_changed_cb
- *
- * Triggered while the user is editing text the settings combo.
- *******************************************************/
+/* Callback triggered while the user is editing text the settings combo.
+ */
 void
 csv_tximp_preview_settings_text_changed_cb (GtkEntry *entry, CsvImportTrans *info)
 {
@@ -296,15 +368,12 @@ csv_tximp_preview_settings_text_changed_cb (GtkEntry *entry, CsvImportTrans *inf
         info->tx_imp->settings_name(text);
 
     auto combo = gtk_widget_get_parent (GTK_WIDGET(entry));
-    handle_save_del_sensitivity (GTK_COMBO_BOX(combo), info);
+    csv_tximp_preview_handle_save_del_sensitivity (GTK_COMBO_BOX(combo), info);
 }
 
 
-/*******************************************************
- * csv_tximp_preview_del_settings_cb
- *
- * call back to delete a settings entry
- *******************************************************/
+/* Callback to delete a settings entry
+ */
 void
 csv_tximp_preview_del_settings_cb (GtkWidget *button, CsvImportTrans *info)
 {
@@ -330,17 +399,14 @@ csv_tximp_preview_del_settings_cb (GtkWidget *button, CsvImportTrans *info)
     if (response == GTK_RESPONSE_OK)
     {
         preset->remove();
-        populate_settings_combo(info->settings_combo);
+        csv_tximp_preview_populate_settings_combo(info->settings_combo);
         gtk_combo_box_set_active (info->settings_combo, 0); // Default
         csv_tximp_preview_refresh (info); // Reset the widgets
     }
 }
 
-/*******************************************************
- * csv_tximp_preview_save_settings_cb
- *
- * Save the settings to a Key File.
- *******************************************************/
+/* Callback to save the current settings to the gnucash state file.
+ */
 void
 csv_tximp_preview_save_settings_cb (GtkWidget *button, CsvImportTrans *info)
 {
@@ -395,7 +461,7 @@ csv_tximp_preview_save_settings_cb (GtkWidget *button, CsvImportTrans *info)
         gtk_widget_destroy (dialog);
 
         // Update the settings store
-        populate_settings_combo(info->settings_combo);
+        csv_tximp_preview_populate_settings_combo(info->settings_combo);
         auto model = gtk_combo_box_get_model (info->settings_combo);
 
         // Get the first entry in model
@@ -427,289 +493,98 @@ csv_tximp_preview_save_settings_cb (GtkWidget *button, CsvImportTrans *info)
         gtk_dialog_run (GTK_DIALOG (dialog));
         gtk_widget_destroy (dialog);
     }
-}
-
-
-/**************************************************
- * csv_tximp_file_confirm_cb
- *
- * call back for ok button in file chooser widget
- **************************************************/
-void
-csv_tximp_file_confirm_cb (GtkWidget *button, CsvImportTrans *info)
+}/* Callback triggered when user adjusts skip start lines
+ */
+void csv_tximp_preview_srow_cb (GtkSpinButton *spin, CsvImportTrans *info)
 {
-    auto num = gtk_assistant_get_current_page (info->csv_imp_asst);
-    auto page = gtk_assistant_get_nth_page (info->csv_imp_asst, num);
 
-    gtk_assistant_set_page_complete (info->csv_imp_asst, page, false);
-
-    auto file_name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(info->file_chooser));
-    if (!file_name)
-        return;
+    /* Get number of lines to skip at the beginning */
+    info->tx_imp->skip_start_lines (gtk_spin_button_get_value_as_int (spin));
 
-    auto filepath = gnc_uri_get_path (file_name);
-    auto starting_dir = g_path_get_dirname (filepath);
+    /* And adjust maximum number of lines that can be skipped at the end accordingly */
+    auto adj = gtk_spin_button_get_adjustment (info->end_row_spin);
+    gtk_adjustment_set_upper (adj, info->tx_imp->m_parsed_lines.size()
+            - info->tx_imp->skip_start_lines());
 
-    info->file_name = file_name;
-    gnc_set_default_directory (GNC_PREFS_GROUP, starting_dir);
+    csv_tximp_preview_row_sel_update (info);
+    csv_tximp_preview_validate_settings (info);
+}
 
-    DEBUG("file_name selected is %s", info->file_name.c_str());
-    DEBUG("starting directory is %s", starting_dir);
 
-    g_free (filepath);
-    g_free (file_name);
-    g_free (starting_dir);
+/* Callback triggered when user adjusts skip end lines
+ */
+void csv_tximp_preview_erow_cb (GtkSpinButton *spin, CsvImportTrans *info)
+{
+    /* Get number of lines to skip at the end */
+    info->tx_imp->skip_end_lines (gtk_spin_button_get_value_as_int (spin));
 
-    /* Load the file into parse_data. */
-    auto parse_data = new GncTxImport;
-    /* Assume data is CSV. User can later override to Fixed Width if needed */
-    try
-    {
-        parse_data->file_format (GncImpFileFormat::CSV);
-        parse_data->load_file (info->file_name);
-        parse_data->tokenize (true);
-    }
-    catch (std::ifstream::failure& e)
-    {
-        /* File loading failed ... */
-        gnc_error_dialog (nullptr, "%s", e.what());
-            delete parse_data;
-            return;
-    }
-    catch (std::range_error &e)
-    {
-        /* Parsing failed ... */
-        gnc_error_dialog (nullptr, "%s", e.what());
-        delete parse_data;
-        return;
-    }
+    /* And adjust maximum number of lines that can be skipped at the beginning accordingly */
+    auto adj = gtk_spin_button_get_adjustment (info->start_row_spin);
+    gtk_adjustment_set_upper (adj, info->tx_imp->m_parsed_lines.size()
+            - info->tx_imp->skip_end_lines());
 
-    if (info->tx_imp) // Free parse_data if we have come back here
-        delete info->tx_imp;
-    info->tx_imp = parse_data;
-    info->previewing_errors = false; /* We're looking at all the data. */
-    info->skip_errors = false; // Set skip_errors to False
-    csv_tximp_preview_refresh (info);
+    csv_tximp_preview_row_sel_update (info);
+    csv_tximp_preview_validate_settings (info);
+}
 
-    /* Get settings store and populate */
-    populate_settings_combo(info->settings_combo);
-    gtk_combo_box_set_active (info->settings_combo, 0);
 
-    gtk_assistant_set_page_complete (info->csv_imp_asst, page, true);
-    gtk_assistant_set_current_page (info->csv_imp_asst, num + 1);
+/* Callback triggered when user clicks the skip errors button
+ */
+void csv_tximp_preview_skiperrors_cb (GtkToggleButton *checkbox, CsvImportTrans *info)
+{
+    info->skip_errors = gtk_toggle_button_get_active (checkbox);
+    csv_tximp_preview_validate_settings (info);
 }
 
 
-/**************************************************
- * csv_tximp_row_sel_update
- *
- * refresh the start and end row highlighting
- **************************************************/
-static
-void csv_tximp_preview_row_sel_update (CsvImportTrans* info)
+/* Callback triggered when user clicks the multi split button
+ */
+void csv_tximp_preview_multisplit_cb (GtkToggleButton *checkbox, CsvImportTrans *info)
 {
-    GtkTreeIter iter;
-    auto store = GTK_LIST_STORE(gtk_tree_view_get_model (info->treeview));
+    info->tx_imp->multi_split (gtk_toggle_button_get_active (checkbox));
+    csv_tximp_preview_refresh_table (info);
+    csv_tximp_preview_validate_settings (info);
+}
 
-    /* Colorize rows that will be skipped */
-    for (uint i = 0; i < info->tx_imp->m_parsed_lines.size(); i++)
-    {
-        const char *color = nullptr;
-        if ((i < info->tx_imp->skip_start_lines()) ||             // start rows to skip
-            (i >= info->tx_imp->m_parsed_lines.size()
-                    - info->tx_imp->skip_end_lines()) ||          // end rows to skip
-            (((i - info->tx_imp->skip_start_lines()) % 2 == 1) && // skip every second row...
-             info->tx_imp->skip_alt_lines()))                     // ...if requested
-            color = "pink";
-        else
-            color = nullptr;                                          // all other rows
 
-        bool valid = gtk_tree_model_iter_nth_child (GTK_TREE_MODEL(store), &iter, nullptr, i);
-        if (valid)
-            gtk_list_store_set (store, &iter, 0, color, -1);
-    }
+/* Callback triggered when user clicks the skip alternating lines button
+ */
+void csv_tximp_preview_skiprows_cb (GtkToggleButton *checkbox, CsvImportTrans *info)
+{
+    info->tx_imp->skip_alt_lines (gtk_toggle_button_get_active (checkbox));
+    csv_tximp_preview_row_sel_update (info);
+    csv_tximp_preview_validate_settings (info);
 }
 
-/*******************************************************
- * csv_import_preview_refresh
- *
- * Update the preview page based on the current state of the importer.
- * Should be called when settings are changed.
- *******************************************************/
-void
-csv_tximp_preview_refresh (CsvImportTrans *info)
+
+/** Returns the cell renderer from a column in the preview's treeview.
+ * @param info The display of the data being imported
+ * @param col The number of the column whose cell renderer is being retrieved
+ * @return The cell renderer of column number col
+ */
+static GtkCellRenderer* gnc_csv_preview_get_cell_renderer (CsvImportTrans* info, int col)
 {
-    // Set start row
-    auto adj = gtk_spin_button_get_adjustment (info->start_row_spin);
-    gtk_adjustment_set_upper (adj, info->tx_imp->m_parsed_lines.size());
-    gtk_spin_button_set_value (info->start_row_spin,
-            info->tx_imp->skip_start_lines());
+    auto renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT(gtk_tree_view_get_column (info->treeview, col)));
+    auto cell = GTK_CELL_RENDERER(renderers->data);
+    g_list_free (renderers);
+    return cell;
+}
 
-    // Set end row
-    adj = gtk_spin_button_get_adjustment (info->end_row_spin);
-    gtk_adjustment_set_upper (adj, info->tx_imp->m_parsed_lines.size());
-    gtk_spin_button_set_value (info->end_row_spin,
-            info->tx_imp->skip_end_lines());
 
-    // Set Alternate rows
-    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->skip_alt_rows_button),
-            info->tx_imp->skip_alt_lines());
+/** Event handler for separator changes. This function is called
+ * whenever one of the widgets for configuring the separators (the
+ * separator checkbuttons or the custom separator entry) is
+ * changed.
+ * @param widget The widget that was changed
+ * @param info The data that is being configured
+ */
+void csv_tximp_preview_sep_button_cb (GtkWidget* widget, CsvImportTrans* info)
+{
 
-    // Set multi-split indicator
-    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->multi_split_cbutton),
-            info->tx_imp->multi_split());
-
-    // Set Import Format
-    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->csv_button),
-            (info->tx_imp->file_format() == GncImpFileFormat::CSV));
-    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->fixed_button),
-            (info->tx_imp->file_format() != GncImpFileFormat::CSV));
-
-    // This section deals with the combo's and character encoding
-    gtk_combo_box_set_active (GTK_COMBO_BOX(info->date_format_combo),
-            info->tx_imp->date_format());
-    gtk_combo_box_set_active (GTK_COMBO_BOX(info->currency_format_combo),
-            info->tx_imp->currency_format());
-    go_charmap_sel_set_encoding (info->encselector, info->tx_imp->encoding().c_str());
-
-    gnc_account_sel_set_account(GNC_ACCOUNT_SEL(info->acct_selector),
-            info->tx_imp->base_account() , false);
-
-    // Handle separator checkboxes and custom field, only relevant if the file format is csv
-    if (info->tx_imp->file_format() == GncImpFileFormat::CSV)
-    {
-        auto separators = info->tx_imp->separators();
-        const auto stock_sep_chars = std::string (" \t,:;-");
-        for (int i = 0; i < SEP_NUM_OF_TYPES; i++)
-            gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->sep_button[i]),
-                separators.find (stock_sep_chars[i]) != std::string::npos);
-
-        // If there are any other separators in the separators string,
-        // add them as custom separators
-        auto pos = separators.find_first_of (stock_sep_chars);
-        while (!separators.empty() && pos != std::string::npos)
-        {
-            separators.erase(pos);
-            pos = separators.find_first_of (stock_sep_chars);
-        }
-        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->custom_cbutton),
-                !separators.empty());
-        gtk_entry_set_text (GTK_ENTRY(info->custom_entry), separators.c_str());
-    }
-
-    // Repopulate the parsed data table
-    csv_tximp_preview_refresh_table (info);
-}
-
-
-
-/*******************************************************
- * csv_tximp_preview_srow_cb
- *
- * call back for import start row
- *******************************************************/
-void csv_tximp_preview_srow_cb (GtkSpinButton *spin, CsvImportTrans *info)
-{
-
-    /* Get number of lines to skip at the beginning */
-    info->tx_imp->skip_start_lines (gtk_spin_button_get_value_as_int (spin));
-
-    /* And adjust maximum number of lines that can be skipped at the end accordingly */
-    auto adj = gtk_spin_button_get_adjustment (info->end_row_spin);
-    gtk_adjustment_set_upper (adj, info->tx_imp->m_parsed_lines.size()
-            - info->tx_imp->skip_start_lines());
-
-    csv_tximp_preview_row_sel_update (info);
-    csv_tximp_validate_preview_settings (info);
-}
-
-
-/*******************************************************
- * csv_tximp_preview_erow_cb
- *
- * call back for import end row
- *******************************************************/
-void csv_tximp_preview_erow_cb (GtkSpinButton *spin, CsvImportTrans *info)
-{
-    /* Get number of lines to skip at the end */
-    info->tx_imp->skip_end_lines (gtk_spin_button_get_value_as_int (spin));
-
-    /* And adjust maximum number of lines that can be skipped at the beginning accordingly */
-    auto adj = gtk_spin_button_get_adjustment (info->start_row_spin);
-    gtk_adjustment_set_upper (adj, info->tx_imp->m_parsed_lines.size()
-            - info->tx_imp->skip_end_lines());
-
-    csv_tximp_preview_row_sel_update (info);
-    csv_tximp_validate_preview_settings (info);
-}
-
-
-/*******************************************************
- * csv_tximp_preview_skiperrors_cb
- *
- * call back for Skip Errors
- *******************************************************/
-void csv_tximp_preview_skiperrors_cb (GtkToggleButton *checkbox, CsvImportTrans *info)
-{
-    info->skip_errors = gtk_toggle_button_get_active (checkbox);
-    csv_tximp_validate_preview_settings (info);
-}
-
-
-/*******************************************************
- * csv_tximp_preview_multisplit_cb
- *
- * call back for import multi-split checkbox
- *******************************************************/
-void csv_tximp_preview_multisplit_cb (GtkToggleButton *checkbox, CsvImportTrans *info)
-{
-    info->tx_imp->multi_split (gtk_toggle_button_get_active (checkbox));
-    csv_tximp_preview_refresh_table (info);
-    csv_tximp_validate_preview_settings (info);
-}
-
-
-/*******************************************************
- * csv_tximp_preview_skiprows_cb
- *
- * call back for import skip rows checkbox
- *******************************************************/
-void csv_tximp_preview_skiprows_cb (GtkToggleButton *checkbox, CsvImportTrans *info)
-{
-    info->tx_imp->skip_alt_lines (gtk_toggle_button_get_active (checkbox));
-    csv_tximp_preview_row_sel_update (info);
-    csv_tximp_validate_preview_settings (info);
-}
-
-
-/** Returns the cell renderer from a column in the preview's treeview.
- * @param info The display of the data being imported
- * @param col The number of the column whose cell renderer is being retrieved
- * @return The cell renderer of column number col
- */
-static GtkCellRenderer* gnc_csv_preview_get_cell_renderer (CsvImportTrans* info, int col)
-{
-    auto renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT(gtk_tree_view_get_column (info->treeview, col)));
-    auto cell = GTK_CELL_RENDERER(renderers->data);
-    g_list_free (renderers);
-    return cell;
-}
-
-
-/** Event handler for separator changes. This function is called
- * whenever one of the widgets for configuring the separators (the
- * separator checkbuttons or the custom separator entry) is
- * changed.
- * @param widget The widget that was changed
- * @param info The data that is being configured
- */
-void csv_tximp_preview_sep_button_cb (GtkWidget* widget, CsvImportTrans* info)
-{
-
-    /* Only manipulate separator characters if the currently open file is
-     * csv separated. */
-    if (info->tx_imp->file_format() != GncImpFileFormat::CSV)
-        return;
+    /* Only manipulate separator characters if the currently open file is
+     * csv separated. */
+    if (info->tx_imp->file_format() != GncImpFileFormat::CSV)
+        return;
 
     /* Add the corresponding characters to checked_separators for each
      * button that is checked. */
@@ -739,7 +614,7 @@ void csv_tximp_preview_sep_button_cb (GtkWidget* widget, CsvImportTrans* info)
     {
         info->tx_imp->tokenize (false);
         csv_tximp_preview_refresh_table (info);
-        csv_tximp_validate_preview_settings (info);
+        csv_tximp_preview_validate_settings (info);
     }
     catch (std::range_error &e)
     {
@@ -762,13 +637,15 @@ void csv_tximp_preview_sep_button_cb (GtkWidget* widget, CsvImportTrans* info)
     }
 }
 
+/* Callback triggered when user selects an account in the account selection
+ */
 void csv_tximp_preview_acct_sel_cb (GtkWidget* widget, CsvImportTrans* info)
 {
 
     auto acct = gnc_account_sel_get_account( GNC_ACCOUNT_SEL(widget) );
     info->tx_imp->base_account(acct);
     csv_tximp_preview_refresh_table (info);
-    csv_tximp_validate_preview_settings (info);
+    csv_tximp_preview_validate_settings (info);
 }
 
 
@@ -789,7 +666,7 @@ void csv_tximp_preview_sep_fixed_sel_cb (GtkToggleButton* csv_button, CsvImportT
 
         info->tx_imp->tokenize (false);
         csv_tximp_preview_refresh_table (info);
-        csv_tximp_validate_preview_settings (info);
+        csv_tximp_preview_validate_settings (info);
     }
     catch (std::range_error &e)
     {
@@ -830,7 +707,7 @@ void csv_tximp_preview_enc_sel_cb (GOCharmapSel* selector, const char* encoding,
             info->tx_imp->tokenize (false);
 
             csv_tximp_preview_refresh_table (info);
-            csv_tximp_validate_preview_settings (info);
+            csv_tximp_preview_validate_settings (info);
 
             info->encoding_selected_called = false;
         }
@@ -856,7 +733,7 @@ void csv_tximp_preview_enc_sel_cb (GOCharmapSel* selector, const char* encoding,
 static void csv_tximp_preview_date_fmt_sel_cb (GtkComboBox* format_selector, CsvImportTrans* info)
 {
     info->tx_imp->date_format (gtk_combo_box_get_active (format_selector));
-    csv_tximp_validate_preview_settings (info);
+    csv_tximp_preview_validate_settings (info);
 }
 
 
@@ -867,10 +744,65 @@ static void csv_tximp_preview_date_fmt_sel_cb (GtkComboBox* format_selector, Csv
 static void csv_tximp_preview_currency_fmt_sel_cb (GtkComboBox* currency_selector, CsvImportTrans* info)
 {
     info->tx_imp->currency_format (gtk_combo_box_get_active (currency_selector));
-    csv_tximp_validate_preview_settings (info);
+    csv_tximp_preview_validate_settings (info);
+}
+
+
+/** Event handler for the data treeview being resized. When the data
+ * treeview is resized, the column types treeview's columns are also resized to
+ * match.
+ * @param widget The data treeview
+ * @param allocation The size of the data treeview
+ * @param info The display of the data being imported
+ */
+static void csv_tximp_preview_treeview_resized_cb (GtkWidget* widget, GtkAllocation* allocation, CsvImportTrans* info)
+{
+    /* Go through each column except for the last. (We don't want to set
+     * the width of the last column because the user won't be able to
+     * shrink the dialog back if it's expanded.) */
+    for (uint i = 0; i < info->tx_imp->column_types().size() - 1; i++)
+    {
+        /* Get the width. */
+        auto col_width = gtk_tree_view_column_get_width (gtk_tree_view_get_column (info->treeview, i));
+        /* Set the minimum width for a column so that drop down selector can be seen. */
+        if (col_width < MIN_COL_WIDTH)
+            col_width = MIN_COL_WIDTH;
+        auto pcol = gtk_tree_view_get_column (info->treeview, i);
+        gtk_tree_view_column_set_min_width (pcol, col_width);
+        /* Set ccol's width the same. */
+        auto ccol = gtk_tree_view_get_column (info->ctreeview, i);
+        gtk_tree_view_column_set_min_width (ccol, col_width);
+        gtk_tree_view_column_set_max_width (ccol, col_width);
+    }
 }
 
 
+/** Event handler for the user selecting a new column type. When the
+ * user selects a new column type, that column's text must be changed
+ * to that selection, and any other columns containing that selection
+ * must be changed to "None" because we don't allow duplicates.
+ * @param renderer The renderer of the column the user changed
+ * @param path There is only 1 row in info->ctreeview, so this is always 0.
+ * @param new_text The text the user selected
+ * @param info The display of the data being imported
+ */
+static void csv_tximp_preview_col_type_changed_cb (GtkCellRenderer* renderer, gchar* path,
+                                GtkTreeIter* new_text_iter, CsvImportTrans* info)
+{
+    /* Get the new text */
+    auto col_num = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT(renderer), "col-num"));
+    GtkTreeModel* model;
+    g_object_get (renderer, "model", &model, nullptr);
+    auto new_col_type = GncTransPropType::NONE;
+    gtk_tree_model_get (model, new_text_iter,
+            1, &new_col_type, // Invisible column in the combobox' model containing the column type
+            -1);
+
+    info->tx_imp->set_column_type (col_num, new_col_type);
+    csv_tximp_preview_refresh_table (info);
+    csv_tximp_preview_validate_settings (info);
+}
+
 /*======================================================================*/
 /*================== Beginning of Gnumeric Code ========================*/
 
@@ -992,7 +924,7 @@ fixed_context_menu_handler (GnumericPopupMenuElement const *element,
         return false;
     }
     csv_tximp_preview_refresh_table (info);
-    csv_tximp_validate_preview_settings (info);
+    csv_tximp_preview_validate_settings (info);
     return true;
 }
 static void
@@ -1035,64 +967,8 @@ fixed_context_menu (CsvImportTrans* info, GdkEventButton *event,
 /*===================== End of Gnumeric Code ===========================*/
 /*======================================================================*/
 
-
-/** Event handler for the data treeview being resized. When the data
- * treeview is resized, the column types treeview's columns are also resized to
- * match.
- * @param widget The data treeview
- * @param allocation The size of the data treeview
- * @param info The display of the data being imported
- */
-static void csv_tximp_preview_treeview_resized_cb (GtkWidget* widget, GtkAllocation* allocation, CsvImportTrans* info)
-{
-    /* Go through each column except for the last. (We don't want to set
-     * the width of the last column because the user won't be able to
-     * shrink the dialog back if it's expanded.) */
-    for (uint i = 0; i < info->tx_imp->column_types().size() - 1; i++)
-    {
-        /* Get the width. */
-        auto col_width = gtk_tree_view_column_get_width (gtk_tree_view_get_column (info->treeview, i));
-        /* Set the minimum width for a column so that drop down selector can be seen. */
-        if (col_width < MIN_COL_WIDTH)
-            col_width = MIN_COL_WIDTH;
-        auto pcol = gtk_tree_view_get_column (info->treeview, i);
-        gtk_tree_view_column_set_min_width (pcol, col_width);
-        /* Set ccol's width the same. */
-        auto ccol = gtk_tree_view_get_column (info->ctreeview, i);
-        gtk_tree_view_column_set_min_width (ccol, col_width);
-        gtk_tree_view_column_set_max_width (ccol, col_width);
-    }
-}
-
-
-/** Event handler for the user selecting a new column type. When the
- * user selects a new column type, that column's text must be changed
- * to that selection, and any other columns containing that selection
- * must be changed to "None" because we don't allow duplicates.
- * @param renderer The renderer of the column the user changed
- * @param path There is only 1 row in info->ctreeview, so this is always 0.
- * @param new_text The text the user selected
- * @param info The display of the data being imported
- */
-static void csv_tximp_preview_col_type_changed_cb (GtkCellRenderer* renderer, gchar* path,
-                                GtkTreeIter* new_text_iter, CsvImportTrans* info)
-{
-    /* Get the new text */
-    auto col_num = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT(renderer), "col-num"));
-    GtkTreeModel* model;
-    g_object_get (renderer, "model", &model, nullptr);
-    auto new_col_type = GncTransPropType::NONE;
-    gtk_tree_model_get (model, new_text_iter,
-            1, &new_col_type, // Invisible column in the combobox' model containing the column type
-            -1);
-
-    info->tx_imp->set_column_type (col_num, new_col_type);
-    csv_tximp_preview_refresh_table (info);
-    csv_tximp_validate_preview_settings (info);
-}
-
 static void
-split_column (CsvImportTrans* info, int col, int dx)
+csv_tximp_preview_split_column (CsvImportTrans* info, int col, int dx)
 {
     auto rel_pos = get_new_col_rel_pos (info, col, dx);
     auto fwtok = dynamic_cast<GncFwTokenizer*>(info->tx_imp->m_tokenizer.get());
@@ -1107,7 +983,7 @@ split_column (CsvImportTrans* info, int col, int dx)
         return;
     }
     csv_tximp_preview_refresh_table (info);
-    csv_tximp_validate_preview_settings (info);
+    csv_tximp_preview_validate_settings (info);
 }
 
 
@@ -1118,7 +994,8 @@ split_column (CsvImportTrans* info, int col, int dx)
  * @param event The event that happened (where the user clicked)
  * @param info The data being configured
  */
-static void header_button_press_handler (GtkWidget* button, GdkEventButton* event,
+static void
+csv_tximp_preview_header_clicked_cb (GtkWidget* button, GdkEventButton* event,
                                         CsvImportTrans* info)
 {
     /* Find the column that was clicked. */
@@ -1134,106 +1011,37 @@ static void header_button_press_handler (GtkWidget* button, GdkEventButton* even
     auto offset = alloc.x - alloc.x;
     if (event->type == GDK_2BUTTON_PRESS && event->button == 1)
         /* Double clicks can split columns. */
-        split_column (info, col, (int)event->x - offset);
+        csv_tximp_preview_split_column (info, col, (int)event->x - offset);
     else if (event->type == GDK_BUTTON_PRESS && event->button == 3)
         /* Right clicking brings up a context menu. */
         fixed_context_menu (info, event, col, (int)event->x - offset);
 }
 
 
-/* Test for the string being in the liststore
- * Returns true if it is or false if not.
- *
- * @param liststore The data being reviewed
- *
- * @param string to check for
+/* visualize skipped lines
  */
-static bool
-check_for_duplicates (GtkListStore *liststore, const gchar *string)
+void csv_tximp_preview_row_sel_update (CsvImportTrans* info)
 {
     GtkTreeIter iter;
-    auto valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL(liststore), &iter);
-    while (valid)
-    {
-        gchar *text;
-        // Walk through the list, reading each row of column string
-        gtk_tree_model_get (GTK_TREE_MODEL(liststore), &iter, MAPPING_STRING, &text, -1);
-
-        if(!(g_strcmp0 (text, string)))
-        {
-            g_free (text);
-            return true;
-        }
-        g_free (text);
-
-        valid = gtk_tree_model_iter_next (GTK_TREE_MODEL(liststore), &iter);
-    }
-    return false;
-}
-
-
-/* Get the list of accounts
- * Returns true if we have any accounts
- *
- * @param info The data being previewed
- *
- * @param store for the account match page
- */
-bool get_list_of_accounts (CsvImportTrans* info, GtkTreeModel *store)
-{
-    /* ctstore contains the actual strings appearing in the column types treeview. */
-    auto ctstore = gtk_tree_view_get_model (info->ctreeview);
-    GtkTreeIter ct_iter;
-    gtk_tree_model_get_iter_first (ctstore, &ct_iter);
+    auto store = GTK_LIST_STORE(gtk_tree_view_get_model (info->treeview));
 
-    /* datastore contains the actual strings appearing in the preview treeview. */
-    auto datastore = gtk_tree_view_get_model (info->treeview);
-    auto have_accounts = false;
-    for (uint j = info->tx_imp->skip_start_lines();
-            j < info->tx_imp->m_parsed_lines.size() - info->tx_imp->skip_end_lines() - 1;
-            j++)
+    /* Colorize rows that will be skipped */
+    for (uint i = 0; i < info->tx_imp->m_parsed_lines.size(); i++)
     {
-        /* Go through each of the columns. */
-        for (uint i = 0; i < info->tx_imp->column_types().size(); i++)
-        {
-            auto col_type = GncTransPropType::NONE;
-
-            /* Get the column type. Store is arranged so that every two
-             * columns is a pair of
-             * - the column type as a user visible (translated) string
-             * - the internal type for this column
-             * So store looks like:
-             * col_type_str 0, col_type, col_type_str 1, col_type 1, ..., col_type_str ncols, col_type ncols. */
-            gtk_tree_model_get (ctstore, &ct_iter, 2 * i + 1, &col_type, -1);
-
-            /* We're only interested in columns of type ACCOUNT and OACCOUNT. */
-            if ((col_type == GncTransPropType::ACCOUNT) || (col_type == GncTransPropType::TACCOUNT))
-            {
-                /* Get an iterator for the row in the data store. */
-                GtkTreeIter data_iter;
-                if (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL(datastore), &data_iter, nullptr, j))
-                {
-                    gchar* accstr = nullptr;   /* The string in this column from datastore. */
-                    gtk_tree_model_get (datastore, &data_iter, i + 1, &accstr, -1);
-                    if (!accstr || *accstr == '\0')
-                        continue;
+        const char *color = nullptr;
+        if ((i < info->tx_imp->skip_start_lines()) ||             // start rows to skip
+            (i >= info->tx_imp->m_parsed_lines.size()
+                    - info->tx_imp->skip_end_lines()) ||          // end rows to skip
+            (((i - info->tx_imp->skip_start_lines()) % 2 == 1) && // skip every second row...
+             info->tx_imp->skip_alt_lines()))                     // ...if requested
+            color = "pink";
+        else
+            color = nullptr;                                          // all other rows
 
-                    // Append the entry
-                    if (!check_for_duplicates (GTK_LIST_STORE(store), accstr))
-                    {
-                        GtkTreeIter acct_iter;
-                        gtk_list_store_append (GTK_LIST_STORE(store), &acct_iter);
-                        gtk_list_store_set (GTK_LIST_STORE(store), &acct_iter, MAPPING_STRING, accstr,
-                                            MAPPING_FULLPATH, _("No Linked Account"), MAPPING_ACCOUNT, nullptr, -1);
-                        have_accounts = true;
-                    }
-                    g_free (accstr);
-                }
-            }
-        }
+        bool valid = gtk_tree_model_iter_nth_child (GTK_TREE_MODEL(store), &iter, nullptr, i);
+        if (valid)
+            gtk_list_store_set (store, &iter, 0, color, -1);
     }
-
-    return have_accounts;
 }
 
 
@@ -1386,7 +1194,7 @@ void csv_tximp_preview_refresh_table (CsvImportTrans* info)
         g_object_set (G_OBJECT(col), "clickable", TRUE, nullptr);
         auto button = gtk_tree_view_column_get_widget(col);
         g_signal_connect (G_OBJECT(button), "button_press_event",
-                         G_CALLBACK(header_button_press_handler), (gpointer)info);
+                         G_CALLBACK(csv_tximp_preview_header_clicked_cb), (gpointer)info);
         /* Store the column number in the button to know which one was clicked later on */
         g_object_set_data (G_OBJECT(button), "col-num",GINT_TO_POINTER(i));
     }
@@ -1425,8 +1233,77 @@ void csv_tximp_preview_refresh_table (CsvImportTrans* info)
 
 }
 
+/* Update the preview page based on the current state of the importer.
+ * Should be called when settings are changed.
+ */
+void
+csv_tximp_preview_refresh (CsvImportTrans *info)
+{
+    // Set start row
+    auto adj = gtk_spin_button_get_adjustment (info->start_row_spin);
+    gtk_adjustment_set_upper (adj, info->tx_imp->m_parsed_lines.size());
+    gtk_spin_button_set_value (info->start_row_spin,
+            info->tx_imp->skip_start_lines());
+
+    // Set end row
+    adj = gtk_spin_button_get_adjustment (info->end_row_spin);
+    gtk_adjustment_set_upper (adj, info->tx_imp->m_parsed_lines.size());
+    gtk_spin_button_set_value (info->end_row_spin,
+            info->tx_imp->skip_end_lines());
+
+    // Set Alternate rows
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->skip_alt_rows_button),
+            info->tx_imp->skip_alt_lines());
+
+    // Set multi-split indicator
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->multi_split_cbutton),
+            info->tx_imp->multi_split());
+
+    // Set Import Format
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->csv_button),
+            (info->tx_imp->file_format() == GncImpFileFormat::CSV));
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->fixed_button),
+            (info->tx_imp->file_format() != GncImpFileFormat::CSV));
+
+    // This section deals with the combo's and character encoding
+    gtk_combo_box_set_active (GTK_COMBO_BOX(info->date_format_combo),
+            info->tx_imp->date_format());
+    gtk_combo_box_set_active (GTK_COMBO_BOX(info->currency_format_combo),
+            info->tx_imp->currency_format());
+    go_charmap_sel_set_encoding (info->encselector, info->tx_imp->encoding().c_str());
+
+    gnc_account_sel_set_account(GNC_ACCOUNT_SEL(info->acct_selector),
+            info->tx_imp->base_account() , false);
+
+    // Handle separator checkboxes and custom field, only relevant if the file format is csv
+    if (info->tx_imp->file_format() == GncImpFileFormat::CSV)
+    {
+        auto separators = info->tx_imp->separators();
+        const auto stock_sep_chars = std::string (" \t,:;-");
+        for (int i = 0; i < SEP_NUM_OF_TYPES; i++)
+            gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->sep_button[i]),
+                separators.find (stock_sep_chars[i]) != std::string::npos);
+
+        // If there are any other separators in the separators string,
+        // add them as custom separators
+        auto pos = separators.find_first_of (stock_sep_chars);
+        while (!separators.empty() && pos != std::string::npos)
+        {
+            separators.erase(pos);
+            pos = separators.find_first_of (stock_sep_chars);
+        }
+        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->custom_cbutton),
+                !separators.empty());
+        gtk_entry_set_text (GTK_ENTRY(info->custom_entry), separators.c_str());
+    }
+
+    // Repopulate the parsed data table
+    csv_tximp_preview_refresh_table (info);
+}
 
-void csv_tximp_validate_preview_settings (CsvImportTrans* info)
+/* Check if all selected data can be parsed sufficiently to continue
+ */
+void csv_tximp_preview_validate_settings (CsvImportTrans* info)
 {
     /* Allow the user to proceed only if there are no inconsistencies in the settings */
     auto error_msg = info->tx_imp->verify();
@@ -1435,112 +1312,107 @@ void csv_tximp_validate_preview_settings (CsvImportTrans* info)
     gtk_widget_set_visible (GTK_WIDGET(info->instructions_image), !error_msg.empty());
 }
 
-/*======================================================================*/
-/*======================================================================*/
-
-/*******************************************************
- * Assistant page prepare functions
- *******************************************************/
-void
-csv_tximp_assist_start_page_prepare (GtkAssistant *assistant,
-        CsvImportTrans* info)
-{
-    auto num = gtk_assistant_get_current_page (assistant);
-    auto page = gtk_assistant_get_nth_page (assistant, num);
-
-    // Clear the treemodel list store
-    auto store = gtk_tree_view_get_model (GTK_TREE_VIEW(info->account_match_view));
-    gtk_list_store_clear (GTK_LIST_STORE(store));
-
-    /* Enable the Assistant Buttons */
-    gtk_assistant_set_page_complete (assistant, page, true);
-}
 
+/**************************************************
+ * Code related to the account match page
+ **************************************************/
 
-void
-csv_tximp_assist_file_page_prepare (GtkAssistant *assistant,
-        CsvImportTrans* info)
+/* Test for the string being in the liststore
+ * Returns true if it is or false if not.
+ *
+ * @param liststore The data being reviewed
+ * @param string to check for
+ */
+static bool
+csv_tximp_acct_match_check_for_duplicates (GtkListStore *liststore, const gchar *string)
 {
-    auto num = gtk_assistant_get_current_page (assistant);
-    auto page = gtk_assistant_get_nth_page (assistant, num);
+    GtkTreeIter iter;
+    auto valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL(liststore), &iter);
+    while (valid)
+    {
+        gchar *text;
+        // Walk through the list, reading each row of column string
+        gtk_tree_model_get (GTK_TREE_MODEL(liststore), &iter, MAPPING_STRING, &text, -1);
 
-    info->previewing_errors = false; // We're looking at all the data.
-    info->skip_errors = false; // Set skip_errors to False to start with.
+        if(!(g_strcmp0 (text, string)))
+        {
+            g_free (text);
+            return true;
+        }
+        g_free (text);
 
-    /* Set the default directory */
-    auto starting_dir = gnc_get_default_directory (GNC_PREFS_GROUP);
-    if (starting_dir)
-    {
-        gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER(info->file_chooser), starting_dir);
-        g_free (starting_dir);
+        valid = gtk_tree_model_iter_next (GTK_TREE_MODEL(liststore), &iter);
     }
-
-    /* Disable the Forward Assistant Button */
-    gtk_assistant_set_page_complete (assistant, page, false);
+    return false;
 }
 
 
-void
-csv_tximp_assist_preview_page_prepare (GtkAssistant *assistant,
-        CsvImportTrans* info)
+/* Get the list of accounts
+ * Returns true if we have any accounts
+ *
+ * @param info The data being previewed
+ * @param store for the account match page
+ */
+bool csv_tximp_acct_match_get_list_of_accounts (CsvImportTrans* info, GtkTreeModel *store)
 {
-    g_signal_connect (G_OBJECT(info->treeview), "size-allocate",
-                     G_CALLBACK(csv_tximp_preview_treeview_resized_cb), (gpointer)info);
-
-    // Hide the skip errors check button
-    gtk_widget_hide (GTK_WIDGET(info->skip_errors_button));
+    /* ctstore contains the actual strings appearing in the column types treeview. */
+    auto ctstore = gtk_tree_view_get_model (info->ctreeview);
+    GtkTreeIter ct_iter;
+    gtk_tree_model_get_iter_first (ctstore, &ct_iter);
 
-    if (info->previewing_errors) // We are looking at errors to display
+    /* datastore contains the actual strings appearing in the preview treeview. */
+    auto datastore = gtk_tree_view_get_model (info->treeview);
+    auto have_accounts = false;
+    for (uint j = info->tx_imp->skip_start_lines();
+            j < info->tx_imp->m_parsed_lines.size() - info->tx_imp->skip_end_lines() - 1;
+            j++)
     {
-        /* Block going back */
-        gtk_assistant_commit (info->csv_imp_asst);
-
-        gchar* name;
-        GtkIconSize size;
-        gtk_image_get_stock (info->instructions_image, &name, &size);
-        gtk_image_set_from_stock (info->instructions_image, GTK_STOCK_DIALOG_ERROR, size);
-        gtk_label_set_text (info->instructions_label,
-                           _("The rows displayed below had errors which are in the last column. You can attempt to correct them by changing the configuration."));
-        gtk_widget_show (GTK_WIDGET(info->instructions_image));
-        gtk_widget_show (GTK_WIDGET(info->instructions_label));
+        /* Go through each of the columns. */
+        for (uint i = 0; i < info->tx_imp->column_types().size(); i++)
+        {
+            auto col_type = GncTransPropType::NONE;
 
-        /* Reset start and end row */
-        gtk_spin_button_set_value (info->start_row_spin, 0);
-        gtk_spin_button_set_value (info->end_row_spin, 0);
+            /* Get the column type. Store is arranged so that every two
+             * columns is a pair of
+             * - the column type as a user visible (translated) string
+             * - the internal type for this column
+             * So store looks like:
+             * col_type_str 0, col_type, col_type_str 1, col_type 1, ..., col_type_str ncols, col_type ncols. */
+            gtk_tree_model_get (ctstore, &ct_iter, 2 * i + 1, &col_type, -1);
 
-        /* Set spin buttons and settings combo hbox not sensitive */
-        gtk_widget_set_sensitive (info->combo_hbox, FALSE);
-        gtk_widget_set_sensitive (GTK_WIDGET(info->start_row_spin), FALSE);
-        gtk_widget_set_sensitive (GTK_WIDGET(info->end_row_spin), FALSE);
-        gtk_widget_set_sensitive (info->skip_alt_rows_button, FALSE);
-        gtk_widget_set_sensitive (info->multi_split_cbutton, FALSE);
-        info->tx_imp->skip_alt_lines (false);
+            /* We're only interested in columns of type ACCOUNT and OACCOUNT. */
+            if ((col_type == GncTransPropType::ACCOUNT) || (col_type == GncTransPropType::TACCOUNT))
+            {
+                /* Get an iterator for the row in the data store. */
+                GtkTreeIter data_iter;
+                if (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL(datastore), &data_iter, nullptr, j))
+                {
+                    gchar* accstr = nullptr;   /* The string in this column from datastore. */
+                    gtk_tree_model_get (datastore, &data_iter, i + 1, &accstr, -1);
+                    if (!accstr || *accstr == '\0')
+                        continue;
 
-        /* Show the skip errors check button */
-        gtk_widget_show (GTK_WIDGET(info->skip_errors_button));
-        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->skip_errors_button), FALSE);
-    }
-    else
-    {
-        // Load the account strings into the store
-        auto store = gtk_tree_view_get_model (GTK_TREE_VIEW(info->account_match_view));
-        gtk_list_store_clear (GTK_LIST_STORE(store)); // Clear list of accounts unless we are looking at errors
+                    // Append the entry
+                    if (!csv_tximp_acct_match_check_for_duplicates (GTK_LIST_STORE(store), accstr))
+                    {
+                        GtkTreeIter acct_iter;
+                        gtk_list_store_append (GTK_LIST_STORE(store), &acct_iter);
+                        gtk_list_store_set (GTK_LIST_STORE(store), &acct_iter, MAPPING_STRING, accstr,
+                                            MAPPING_FULLPATH, _("No Linked Account"), MAPPING_ACCOUNT, nullptr, -1);
+                        have_accounts = true;
+                    }
+                    g_free (accstr);
+                }
+            }
+        }
     }
 
-    /* Disable the Forward Assistant Button */
-    auto num = gtk_assistant_get_current_page (assistant);
-    auto page = gtk_assistant_get_nth_page (assistant, num);
-    gtk_assistant_set_page_complete (assistant, page, false);
-
-    /* Load the data into the treeview. */
-    csv_tximp_preview_refresh_table (info);
-    csv_tximp_validate_preview_settings (info);
-
+    return have_accounts;
 }
 
 
 static bool
-import_account_check_all (GtkTreeModel *model)
+csv_tximp_acct_match_check_all (GtkTreeModel *model)
 {
     // Set iter to first entry of store
     GtkTreeIter iter;
@@ -1564,15 +1436,14 @@ import_account_check_all (GtkTreeModel *model)
 }
 
 
-/*****************************************************************
- * Parse the text splitting into a path and the last_part based on
+/* Parse the text splitting into a path and the last_part based on
  * account separator. If the path is valid, add the last_part and
  * return this. If the path is invalid, use the new separator path
  * with the last_part and return that so there is only one new
  * account dialog.
- *****************************************************************/
+ */
 static gchar *
-import_account_text_parse (gchar *text)
+csv_tximp_acct_match_text_parse (gchar *text)
 {
     // Split the incoming string by current separator
     auto sep = gnc_get_account_separator_string ();
@@ -1639,7 +1510,8 @@ import_account_text_parse (gchar *text)
     return ret_string;
 }
 
-static void import_account_select_internal(CsvImportTrans* info, GtkTreeModel *model, GtkTreeIter& iter)
+static void
+csv_tximp_acct_match_select_internal(CsvImportTrans* info, GtkTreeModel *model, GtkTreeIter& iter)
 {
     // Get the the String
     gchar *text = nullptr;
@@ -1649,7 +1521,7 @@ static void import_account_select_internal(CsvImportTrans* info, GtkTreeModel *m
     Account *account = nullptr;
     gtk_tree_model_get (model, &iter, MAPPING_ACCOUNT, &account, -1);
 
-    auto parsed_text = import_account_text_parse (text);
+    auto parsed_text = csv_tximp_acct_match_text_parse (text);
     auto gnc_acc = gnc_import_select_account (nullptr, nullptr, 1,
             parsed_text, nullptr, ACCT_TYPE_NONE, account, nullptr);
 
@@ -1668,7 +1540,7 @@ static void import_account_select_internal(CsvImportTrans* info, GtkTreeModel *m
     g_free (text);
     g_free (parsed_text);
 
-    if (import_account_check_all (model))
+    if (csv_tximp_acct_match_check_all (model))
         gtk_assistant_set_page_complete (info->csv_imp_asst, info->account_match_page, true);
 
 }
@@ -1681,7 +1553,7 @@ csv_tximp_acct_match_button_clicked_cb (GtkWidget *widget, CsvImportTrans* info)
 
     GtkTreeIter iter;
     if (gtk_tree_selection_get_selected (selection, &model, &iter))
-        import_account_select_internal (info, model, iter);
+        csv_tximp_acct_match_select_internal (info, model, iter);
 }
 
 
@@ -1706,7 +1578,7 @@ csv_tximp_acct_match_view_clicked_cb (GtkWidget *widget, GdkEventButton *event,
             auto model = gtk_tree_view_get_model (GTK_TREE_VIEW(info->account_match_view));
             GtkTreeIter iter;
             if (gtk_tree_model_get_iter (model, &iter, path))
-                import_account_select_internal (info, model, iter);
+                csv_tximp_acct_match_select_internal (info, model, iter);
             gtk_tree_path_free (path);
         }
         return true;
@@ -1715,13 +1587,114 @@ csv_tximp_acct_match_view_clicked_cb (GtkWidget *widget, GdkEventButton *event,
 }
 
 
+/*******************************************************
+ * Assistant page prepare functions
+ *******************************************************/
+
+void
+csv_tximp_assist_start_page_prepare (GtkAssistant *assistant,
+        CsvImportTrans* info)
+{
+    auto num = gtk_assistant_get_current_page (assistant);
+    auto page = gtk_assistant_get_nth_page (assistant, num);
+
+    // Clear the treemodel list store
+    auto store = gtk_tree_view_get_model (GTK_TREE_VIEW(info->account_match_view));
+    gtk_list_store_clear (GTK_LIST_STORE(store));
+
+    /* Enable the Assistant Buttons */
+    gtk_assistant_set_page_complete (assistant, page, true);
+}
+
+
+void
+csv_tximp_assist_file_page_prepare (GtkAssistant *assistant,
+        CsvImportTrans* info)
+{
+    auto num = gtk_assistant_get_current_page (assistant);
+    auto page = gtk_assistant_get_nth_page (assistant, num);
+
+    info->previewing_errors = false; // We're looking at all the data.
+    info->skip_errors = false; // Set skip_errors to False to start with.
+
+    /* Set the default directory */
+    auto starting_dir = gnc_get_default_directory (GNC_PREFS_GROUP);
+    if (starting_dir)
+    {
+        gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER(info->file_chooser), starting_dir);
+        g_free (starting_dir);
+    }
+
+    /* Disable the Forward Assistant Button */
+    gtk_assistant_set_page_complete (assistant, page, false);
+}
+
+
+void
+csv_tximp_assist_preview_page_prepare (GtkAssistant *assistant,
+        CsvImportTrans* info)
+{
+    g_signal_connect (G_OBJECT(info->treeview), "size-allocate",
+                     G_CALLBACK(csv_tximp_preview_treeview_resized_cb), (gpointer)info);
+
+    // Hide the skip errors check button
+    gtk_widget_hide (GTK_WIDGET(info->skip_errors_button));
+
+    if (info->previewing_errors) // We are looking at errors to display
+    {
+        /* Block going back */
+        gtk_assistant_commit (info->csv_imp_asst);
+
+        gchar* name;
+        GtkIconSize size;
+        gtk_image_get_stock (info->instructions_image, &name, &size);
+        gtk_image_set_from_stock (info->instructions_image, GTK_STOCK_DIALOG_ERROR, size);
+        gtk_label_set_text (info->instructions_label,
+                           _("The rows displayed below had errors which are in the last column. You can attempt to correct them by changing the configuration."));
+        gtk_widget_show (GTK_WIDGET(info->instructions_image));
+        gtk_widget_show (GTK_WIDGET(info->instructions_label));
+
+        /* Reset start and end row */
+        gtk_spin_button_set_value (info->start_row_spin, 0);
+        gtk_spin_button_set_value (info->end_row_spin, 0);
+
+        /* Set spin buttons and settings combo hbox not sensitive */
+        gtk_widget_set_sensitive (info->combo_hbox, FALSE);
+        gtk_widget_set_sensitive (GTK_WIDGET(info->start_row_spin), FALSE);
+        gtk_widget_set_sensitive (GTK_WIDGET(info->end_row_spin), FALSE);
+        gtk_widget_set_sensitive (info->skip_alt_rows_button, FALSE);
+        gtk_widget_set_sensitive (info->multi_split_cbutton, FALSE);
+        info->tx_imp->skip_alt_lines (false);
+
+        /* Show the skip errors check button */
+        gtk_widget_show (GTK_WIDGET(info->skip_errors_button));
+        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->skip_errors_button), FALSE);
+    }
+    else
+    {
+        // Load the account strings into the store
+        auto store = gtk_tree_view_get_model (GTK_TREE_VIEW(info->account_match_view));
+        gtk_list_store_clear (GTK_LIST_STORE(store)); // Clear list of accounts unless we are looking at errors
+    }
+
+    /* Disable the Forward Assistant Button */
+    auto num = gtk_assistant_get_current_page (assistant);
+    auto page = gtk_assistant_get_nth_page (assistant, num);
+    gtk_assistant_set_page_complete (assistant, page, false);
+
+    /* Load the data into the treeview. */
+    csv_tximp_preview_refresh_table (info);
+    csv_tximp_preview_validate_settings (info);
+
+}
+
 void
 csv_tximp_assist_account_match_page_prepare (GtkAssistant *assistant,
         CsvImportTrans* info)
 {
     // Load the account strings into the store
     auto store = gtk_tree_view_get_model (GTK_TREE_VIEW(info->account_match_view));
-    get_list_of_accounts (info, store);
+    csv_tximp_acct_match_get_list_of_accounts (info, store);
 
     // Match the account strings to the mappings
     gnc_csv_account_map_load_mappings (store);
@@ -1737,7 +1710,7 @@ csv_tximp_assist_account_match_page_prepare (GtkAssistant *assistant,
 
     /* Enable the Forward Assistant Button */
        gtk_assistant_set_page_complete (assistant, info->account_match_page,
-               import_account_check_all (store));
+               csv_tximp_acct_match_check_all (store));
 }
 
 

commit 0d721b795c20a9691ca89951899d55af2dc26b51
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sun Dec 18 18:12:57 2016 +0100

    Cleanup round
    
    - m_ prefix all member variables
    - delay variable declarations until actually needed
    - use auto where possible for variable declarations
    - use standard c(++) types and constants where possible
    - rename variables and functions to be more consistent
    - set gui callbacks as much as possible in the glade file
    - drop unused parameters

diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index 45105ce..163482f 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -72,32 +72,29 @@ static QofLogModule log_module = GNC_MOD_ASSISTANT;
 struct  CsvImportTrans
 {
 
-    GtkWidget       *window;
+    GtkAssistant    *csv_imp_asst;
 
     GtkWidget       *start_page;                    /**< Assistant start page widget */
 
     GtkWidget       *file_page;                     /**< Assistant file page widget */
     GtkWidget       *file_chooser;                  /**< The widget for the file chooser */
     std::string      file_name;                     /**< The import file name */
-    std::string      error_text;                    /**< Error Text */
 
     GtkWidget       *preview_page;                  /**< Assistant preview page widget */
-    GtkWidget       *settings_combo;                /**< The Settings Combo */
+    GtkComboBox     *settings_combo;                /**< The Settings Combo */
     GtkWidget       *save_button;                   /**< The Save Settings button */
     GtkWidget       *del_button;                    /**< The Delete Settings button */
     GtkWidget       *acct_selector;                 /**< The Account selector */
     GtkWidget       *combo_hbox;                    /**< The Settings Combo hbox */
-    GtkWidget       *check_butt;                    /**< The widget for the check label button */
-    GtkWidget       *start_row_spin;                /**< The widget for the start row spinner */
-    GtkWidget       *end_row_spin;                  /**< The widget for the end row spinner */
-    GtkWidget       *skip_rows;                     /**< The widget for Skip alternate rows from start row */
+    GtkWidget       *skip_errors_button;            /**< The widget for the check label button */
+    GtkSpinButton   *start_row_spin;                /**< The widget for the start row spinner */
+    GtkSpinButton   *end_row_spin;                  /**< The widget for the end row spinner */
+    GtkWidget       *skip_alt_rows_button;          /**< The widget for Skip alternate rows from start row */
     GtkWidget       *csv_button;                    /**< The widget for the CSV button */
     GtkWidget       *fixed_button;                  /**< The widget for the Fixed Width button */
     GtkWidget       *multi_split_cbutton;           /**< The widget for Multi-split */
-
-    GncTxImport     *parse_data;                    /**< The actual data we are previewing */
     GOCharmapSel    *encselector;                   /**< The widget for selecting the encoding */
-    GtkCheckButton  *sep_buttons[SEP_NUM_OF_TYPES]; /**< Checkbuttons for common separators */
+    GtkCheckButton  *sep_button[SEP_NUM_OF_TYPES];  /**< Checkbuttons for common separators */
     GtkCheckButton  *custom_cbutton;                /**< The checkbutton for a custom separator */
     GtkEntry        *custom_entry;                  /**< The entry for custom separators */
     GtkComboBoxText *date_format_combo;             /**< The Combo Text widget for selecting the date format */
@@ -110,10 +107,6 @@ struct  CsvImportTrans
                                                        * (See description of encoding_selected.) */
     bool             previewing_errors;             /**< true if the dialog is displaying
                                                        * error lines, instead of all the file data. */
-    int              code_encoding_calls;           /**< Normally this is 0. If the computer
-                                                       * changes encselector, this is set to
-                                                       * 2. encoding_selected is called twice,
-                                                       * each time decrementing this by 1. */
     bool             skip_errors;                   /**< This is false until the user checks the skip errors. */
     int              fixed_context_col;             /**< The number of the column whose the user has clicked */
     int              fixed_context_dx;              /**< The horizontal coordinate of the pixel in the header of the column
@@ -126,9 +119,9 @@ struct  CsvImportTrans
 
     GtkWidget            *doc_page;                 /**< Assistant doc page widget */
 
-    GNCImportMainMatcher *gnc_csv_importer_gui;     /**< The GNCImportMainMatcher structure */
     GtkWidget            *match_page;               /**< Assistant match page widget, to be packed with the transaction matcher */
     GtkWidget            *match_label;              /**< The match label at the bottom of the page */
+    GNCImportMainMatcher *gnc_csv_importer_gui;     /**< The GNCImportMainMatcher structure */
     GtkWidget            *help_button;              /**< The widget for the help button on the matcher page */
     GtkWidget            *cancel_button;            /**< The widget for the new cancel button when going back is blocked */
     bool                  match_parse_run = false;  /**< This is set after the first run */
@@ -137,6 +130,7 @@ struct  CsvImportTrans
     GtkWidget            *summary_label;            /**< The summary text */
 
     bool                  new_book;                 /**< Are we importing into a new book?; if yes, call book options */
+    GncTxImport          *tx_imp;                   /**< The actual data we are previewing */
     int                   callcount;                /**< Number of times the assistant page forward function called */
     int                   next_page;                /**< The saved assistant next page number */
 
@@ -147,37 +141,41 @@ struct  CsvImportTrans
 
 extern "C"
 {
-void csv_import_trans_assistant_prepare (GtkAssistant  *assistant, GtkWidget *page, gpointer user_data);
-void csv_import_trans_assistant_finish (GtkAssistant *gtkassistant, gpointer user_data);
-void csv_import_trans_assistant_cancel (GtkAssistant *gtkassistant, gpointer user_data);
-void csv_import_trans_assistant_close (GtkAssistant *gtkassistant, gpointer user_data);
-void csv_import_trans_srow_cb (GtkWidget *spin, gpointer user_data);
-void csv_import_trans_erow_cb (GtkWidget *spin, gpointer user_data);
-void csv_import_trans_skip_errors_cb (GtkToggleButton *checkbox, gpointer user_data);
-void csv_import_trans_skiprows_cb (GtkToggleButton *checkbox, gpointer user_data);
-void csv_import_trans_multisplit_cb (GtkToggleButton *checkbox, gpointer user_data);
-void csv_import_trans_auto_cb (GtkWidget *cb, gpointer user_data);
-void csv_import_trans_file_chooser_confirm_cb (GtkWidget *button, CsvImportTrans *info);
-
-void csv_import_trans_delete_settings_cb (GtkWidget *button, CsvImportTrans *info);
-void csv_import_trans_save_settings_cb (GtkWidget *button, CsvImportTrans *info);
-void csv_import_trans_changed_settings_cb (GtkWidget *combo, CsvImportTrans *info);
-void csv_import_trans_changed_settings_text_cb (GtkWidget *entry, CsvImportTrans *info);
-void sep_button_clicked (GtkWidget* widget, CsvImportTrans* info);
-void account_selected_cb (GtkWidget* widget, CsvImportTrans* info);
+void csv_tximp_assist_prepare_cb (GtkAssistant  *assistant, GtkWidget *page, CsvImportTrans* info);
+void csv_tximp_assist_finish_cb (GtkAssistant *gtkassistant, CsvImportTrans* info);
+void csv_tximp_assist_cancel_cb (GtkAssistant *gtkassistant, CsvImportTrans* info);
+void csv_tximp_assist_close_cb (GtkAssistant *gtkassistant, CsvImportTrans* info);
+void csv_tximp_assist_destroy_cb (GtkWidget *object, CsvImportTrans* info);
+void csv_tximp_file_confirm_cb (GtkWidget *button, CsvImportTrans *info);
+void csv_tximp_preview_srow_cb (GtkSpinButton *spin, CsvImportTrans *info);
+void csv_tximp_preview_erow_cb (GtkSpinButton *spin, CsvImportTrans *info);
+void csv_tximp_preview_skiperrors_cb (GtkToggleButton *checkbox, CsvImportTrans *info);
+void csv_tximp_preview_skiprows_cb (GtkToggleButton *checkbox, CsvImportTrans *info);
+void csv_tximp_preview_multisplit_cb (GtkToggleButton *checkbox, CsvImportTrans *info);
+void csv_tximp_preview_del_settings_cb (GtkWidget *button, CsvImportTrans *info);
+void csv_tximp_preview_save_settings_cb (GtkWidget *button, CsvImportTrans *info);
+void csv_tximp_preview_settings_sel_changed_cb (GtkComboBox *combo, CsvImportTrans *info);
+void csv_tximp_preview_settings_text_changed_cb (GtkEntry *entry, CsvImportTrans *info);
+void csv_tximp_preview_sep_button_cb (GtkWidget* widget, CsvImportTrans* info);
+void csv_tximp_preview_sep_fixed_sel_cb (GtkToggleButton* csv_button, CsvImportTrans* info);
+void csv_tximp_preview_acct_sel_cb (GtkWidget* widget, CsvImportTrans* info);
+void csv_tximp_preview_enc_sel_cb (GOCharmapSel* selector, const char* encoding,
+                              CsvImportTrans* info);
+void csv_tximp_acct_match_button_clicked_cb (GtkWidget *widget, CsvImportTrans* info);
+bool csv_tximp_acct_match_view_clicked_cb (GtkWidget *widget, GdkEventButton *event, CsvImportTrans* info);
 }
 
-void csv_import_trans_assistant_start_page_prepare (GtkAssistant *gtkassistant, gpointer user_data);
-void csv_import_trans_assistant_file_page_prepare (GtkAssistant *assistant, gpointer user_data);
-void csv_import_trans_assistant_preview_page_prepare (GtkAssistant *gtkassistant, gpointer user_data);
-void csv_import_trans_assistant_account_match_page_prepare (GtkAssistant *assistant, gpointer user_data);
-void csv_import_trans_assistant_doc_page_prepare (GtkAssistant *assistant, gpointer user_data);
-void csv_import_trans_assistant_match_page_prepare (GtkAssistant *assistant, gpointer user_data);
-void csv_import_trans_assistant_summary_page_prepare (GtkAssistant *assistant, gpointer user_data);
-
-void csv_import_preview_refresh_table (CsvImportTrans* info);
-void csv_import_preview_refresh (CsvImportTrans *info);
-void csv_import_validate_preview_settings (CsvImportTrans* info);
+void csv_tximp_assist_start_page_prepare (GtkAssistant *gtkassistant, CsvImportTrans* info);
+void csv_tximp_assist_file_page_prepare (GtkAssistant *assistant, CsvImportTrans* info);
+void csv_tximp_assist_preview_page_prepare (GtkAssistant *gtkassistant, CsvImportTrans* info);
+void csv_tximp_assist_account_match_page_prepare (GtkAssistant *assistant, CsvImportTrans* info);
+void csv_tximp_assist_doc_page_prepare (GtkAssistant *assistant, CsvImportTrans* info);
+void csv_tximp_assist_match_page_prepare (GtkAssistant *assistant, CsvImportTrans* info);
+void csv_tximp_assist_summary_page_prepare (GtkAssistant *assistant, CsvImportTrans* info);
+
+void csv_tximp_preview_refresh_table (CsvImportTrans* info);
+void csv_tximp_preview_refresh (CsvImportTrans *info);
+void csv_tximp_validate_preview_settings (CsvImportTrans* info);
 bool get_list_of_accounts (CsvImportTrans* info, GtkTreeModel *store);
 
 /*************************************************************************/
@@ -193,7 +191,7 @@ bool get_list_of_accounts (CsvImportTrans* info, GtkTreeModel *store);
 static void populate_settings_combo(GtkComboBox* combo)
 {
     // Clear the list store
-    GtkTreeModel *model = gtk_combo_box_get_model (combo);
+    auto model = gtk_combo_box_get_model (combo);
     gtk_list_store_clear (GTK_LIST_STORE(model));
 
     // Append the default entry
@@ -209,27 +207,26 @@ static void populate_settings_combo(GtkComboBox* combo)
          * For now this is safe, because the shared pointers in this case are
          * long-lived, but this may need refactoring.
          */
-        gtk_list_store_set (GTK_LIST_STORE(model), &iter, SET_GROUP, preset.get(), SET_NAME, preset->name.c_str(), -1);
+        gtk_list_store_set (GTK_LIST_STORE(model), &iter, SET_GROUP, preset.get(), SET_NAME, preset->m_name.c_str(), -1);
     }
 }
 
 
-static void handle_save_del_sensitivity (GtkWidget* combo, CsvImportTrans *info)
+static void handle_save_del_sensitivity (GtkComboBox* combo, CsvImportTrans *info)
 {
     GtkTreeIter iter;
     auto can_delete = false;
     auto can_save = false;
     auto entry = gtk_bin_get_child (GTK_BIN(combo));
     auto entry_text = gtk_entry_get_text (GTK_ENTRY(entry));
-    /* Handle sensitivity of the delete button and save button */
-    auto got_iter = gtk_combo_box_get_active_iter (GTK_COMBO_BOX(combo), &iter);
-    if (got_iter)
+    /* Handle sensitivity of the delete and save button */
+    if (gtk_combo_box_get_active_iter (combo, &iter))
     {
         CsvTransSettings *preset;
-        GtkTreeModel *model = gtk_combo_box_get_model (GTK_COMBO_BOX(combo));
+        GtkTreeModel *model = gtk_combo_box_get_model (combo);
         gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1);
 
-        if (preset && !trans_preset_is_reserved_name (preset->name))
+        if (preset && !trans_preset_is_reserved_name (preset->m_name))
         {
             /* Current preset is not read_only, so buttons can be enabled */
             can_delete = true;
@@ -247,87 +244,84 @@ static void handle_save_del_sensitivity (GtkWidget* combo, CsvImportTrans *info)
 
 
 /*******************************************************
- * csv_import_trans_changed_settings_cb
+ * csv_tximp_preview_settings_sel_changed_cb
  *
  * Triggered when a preset is selected in the settings combo.
  *******************************************************/
 void
-csv_import_trans_changed_settings_cb (GtkWidget *combo, CsvImportTrans *info)
+csv_tximp_preview_settings_sel_changed_cb (GtkComboBox *combo, CsvImportTrans *info)
 {
     // Get the Active Selection
     GtkTreeIter iter;
-    if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX(info->settings_combo), &iter))
+    if (!gtk_combo_box_get_active_iter (combo, &iter))
         return;
 
     CsvTransSettings *preset = nullptr;
-    GtkTreeModel *model = gtk_combo_box_get_model (GTK_COMBO_BOX(info->settings_combo));
+    auto model = gtk_combo_box_get_model (combo);
     gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1);
 
     if (!preset)
         return;
 
-    info->parse_data->settings (*preset);
-    if (preset->load_error)
+    info->tx_imp->settings (*preset);
+    if (preset->m_load_error)
     {
-        const gchar *title = _("Load the Import Settings.");
-        GtkWidget *dialog = gtk_message_dialog_new (GTK_WINDOW(info->window),
+        auto dialog = gtk_message_dialog_new (GTK_WINDOW(info->csv_imp_asst),
                                     (GtkDialogFlags) 0,
                                     GTK_MESSAGE_ERROR,
                                     GTK_BUTTONS_OK,
-                                    "%s", title);
+                                    "%s", _("Load the Import Settings."));
         gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
             "%s", _("There were problems reading some saved settings, continuing to load.\n Please review and save again."));
         gtk_dialog_run (GTK_DIALOG (dialog));
         gtk_widget_destroy (dialog);
     }
 
-    csv_import_preview_refresh (info);
+    csv_tximp_preview_refresh (info);
     handle_save_del_sensitivity (combo, info);
-    csv_import_validate_preview_settings (info);
+    csv_tximp_validate_preview_settings (info);
 }
 
 
 /*******************************************************
- * csv_import_trans_changed_settings_text_cb
+ * csv_tximp_preview_settings_text_changed_cb
  *
  * Triggered while the user is editing text the settings combo.
  *******************************************************/
 void
-csv_import_trans_changed_settings_text_cb (GtkWidget *entry, CsvImportTrans *info)
+csv_tximp_preview_settings_text_changed_cb (GtkEntry *entry, CsvImportTrans *info)
 {
-    auto text = gtk_entry_get_text (GTK_ENTRY(entry));
+    auto text = gtk_entry_get_text (entry);
     if (text)
-        info->parse_data->settings_name(text);
+        info->tx_imp->settings_name(text);
 
-    auto combo = gtk_widget_get_parent (entry);
-    handle_save_del_sensitivity (combo, info);
+    auto combo = gtk_widget_get_parent (GTK_WIDGET(entry));
+    handle_save_del_sensitivity (GTK_COMBO_BOX(combo), info);
 }
 
 
 /*******************************************************
- * csv_import_trans_delete_settings_cb
+ * csv_tximp_preview_del_settings_cb
  *
  * call back to delete a settings entry
  *******************************************************/
 void
-csv_import_trans_delete_settings_cb (GtkWidget *button, CsvImportTrans *info)
+csv_tximp_preview_del_settings_cb (GtkWidget *button, CsvImportTrans *info)
 {
-    const gchar  *title = _("Delete the Import Settings.");
-
     // Get the Active Selection
     GtkTreeIter iter;
-    if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX(info->settings_combo), &iter))
+    if (!gtk_combo_box_get_active_iter (info->settings_combo, &iter))
         return;
 
     CsvTransSettings *preset = nullptr;
-    auto model = gtk_combo_box_get_model (GTK_COMBO_BOX(info->settings_combo));
+    auto model = gtk_combo_box_get_model (info->settings_combo);
     gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1);
 
-    auto dialog = gtk_message_dialog_new (GTK_WINDOW(info->window),
+    auto dialog = gtk_message_dialog_new (GTK_WINDOW(info->csv_imp_asst),
                                 (GtkDialogFlags) 0,
                                 GTK_MESSAGE_QUESTION,
                                 GTK_BUTTONS_OK_CANCEL,
-                                "%s", title);
+                                "%s", _("Delete the Import Settings."));
     gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
         "%s", _("Do you really want to delete the selection?"));
     auto response = gtk_dialog_run (GTK_DIALOG (dialog));
@@ -336,29 +330,29 @@ csv_import_trans_delete_settings_cb (GtkWidget *button, CsvImportTrans *info)
     if (response == GTK_RESPONSE_OK)
     {
         preset->remove();
-        populate_settings_combo(GTK_COMBO_BOX(info->settings_combo));
-        gtk_combo_box_set_active (GTK_COMBO_BOX(info->settings_combo), 0); // Default
-        csv_import_preview_refresh (info); // Reset the widgets
+        populate_settings_combo(info->settings_combo);
+        gtk_combo_box_set_active (info->settings_combo, 0); // Default
+        csv_tximp_preview_refresh (info); // Reset the widgets
     }
 }
 
 /*******************************************************
- * csv_import_trans_save_settings_cb
+ * csv_tximp_preview_save_settings_cb
  *
  * Save the settings to a Key File.
  *******************************************************/
 void
-csv_import_trans_save_settings_cb (GtkWidget *button, CsvImportTrans *info)
+csv_tximp_preview_save_settings_cb (GtkWidget *button, CsvImportTrans *info)
 {
     auto title = _("Save the Import Settings.");
-    auto new_name = info->parse_data->settings_name();
+    auto new_name = info->tx_imp->settings_name();
 
     /* Check if the entry text matches an already existing preset */
     GtkTreeIter iter;
-    if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX(info->settings_combo), &iter))
+    if (!gtk_combo_box_get_active_iter (info->settings_combo, &iter))
     {
 
-        GtkTreeModel *model = gtk_combo_box_get_model (GTK_COMBO_BOX(info->settings_combo));
+        auto model = gtk_combo_box_get_model (info->settings_combo);
         bool valid = gtk_tree_model_get_iter_first (model, &iter);
         while (valid)
         {
@@ -366,9 +360,9 @@ csv_import_trans_save_settings_cb (GtkWidget *button, CsvImportTrans *info)
             CsvTransSettings *preset;
             gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1);
 
-            if (preset && (preset->name == std::string(new_name)))
+            if (preset && (preset->m_name == std::string(new_name)))
             {
-                auto dialog = gtk_message_dialog_new (GTK_WINDOW(info->window),
+                auto dialog = gtk_message_dialog_new (GTK_WINDOW(info->csv_imp_asst),
                                         (GtkDialogFlags) 0,
                                         GTK_MESSAGE_QUESTION,
                                         GTK_BUTTONS_OK_CANCEL,
@@ -388,9 +382,9 @@ csv_import_trans_save_settings_cb (GtkWidget *button, CsvImportTrans *info)
     }
 
     /* All checks passed, let's save this preset */
-    if (!info->parse_data->save_settings())
+    if (!info->tx_imp->save_settings())
     {
-        auto dialog = gtk_message_dialog_new (GTK_WINDOW(info->window),
+        auto dialog = gtk_message_dialog_new (GTK_WINDOW(info->csv_imp_asst),
                                     (GtkDialogFlags) 0,
                                     GTK_MESSAGE_INFO,
                                     GTK_BUTTONS_OK,
@@ -401,21 +395,20 @@ csv_import_trans_save_settings_cb (GtkWidget *button, CsvImportTrans *info)
         gtk_widget_destroy (dialog);
 
         // Update the settings store
-        populate_settings_combo(GTK_COMBO_BOX(info->settings_combo));
-        auto model = gtk_combo_box_get_model (GTK_COMBO_BOX(info->settings_combo));
+        populate_settings_combo(info->settings_combo);
+        auto model = gtk_combo_box_get_model (info->settings_combo);
 
         // Get the first entry in model
         GtkTreeIter   iter;
         bool valid = gtk_tree_model_get_iter_first (model, &iter);
         while (valid)
         {
-            gchar *name = nullptr;
-
             // Walk through the list, reading each row
+            gchar *name = nullptr;
             gtk_tree_model_get (model, &iter, SET_NAME, &name, -1);
 
             if (g_strcmp0 (name, new_name.c_str()) == 0) // Set Active, the one Saved.
-                gtk_combo_box_set_active_iter (GTK_COMBO_BOX(info->settings_combo), &iter);
+                gtk_combo_box_set_active_iter (info->settings_combo, &iter);
 
             g_free (name);
 
@@ -424,7 +417,7 @@ csv_import_trans_save_settings_cb (GtkWidget *button, CsvImportTrans *info)
     }
     else
     {
-        auto dialog = gtk_message_dialog_new (GTK_WINDOW(info->window),
+        auto dialog = gtk_message_dialog_new (GTK_WINDOW(info->csv_imp_asst),
                                     (GtkDialogFlags) 0,
                                     GTK_MESSAGE_ERROR,
                                     GTK_BUTTONS_OK,
@@ -438,24 +431,23 @@ csv_import_trans_save_settings_cb (GtkWidget *button, CsvImportTrans *info)
 
 
 /**************************************************
- * csv_import_trans_file_chooser_confirm_cb
+ * csv_tximp_file_confirm_cb
  *
  * call back for ok button in file chooser widget
  **************************************************/
 void
-csv_import_trans_file_chooser_confirm_cb (GtkWidget *button, CsvImportTrans *info)
+csv_tximp_file_confirm_cb (GtkWidget *button, CsvImportTrans *info)
 {
-    GtkAssistant *assistant = GTK_ASSISTANT(info->window);
-    gint num = gtk_assistant_get_current_page (assistant);
-    GtkWidget *page = gtk_assistant_get_nth_page (assistant, num);
+    auto num = gtk_assistant_get_current_page (info->csv_imp_asst);
+    auto page = gtk_assistant_get_nth_page (info->csv_imp_asst, num);
 
-    gtk_assistant_set_page_complete (assistant, page, FALSE);
+    gtk_assistant_set_page_complete (info->csv_imp_asst, page, false);
 
     auto file_name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(info->file_chooser));
     if (!file_name)
         return;
 
-    gchar *filepath = gnc_uri_get_path (file_name);
+    auto filepath = gnc_uri_get_path (file_name);
     auto starting_dir = g_path_get_dirname (filepath);
 
     info->file_name = file_name;
@@ -480,59 +472,59 @@ csv_import_trans_file_chooser_confirm_cb (GtkWidget *button, CsvImportTrans *inf
     catch (std::ifstream::failure& e)
     {
         /* File loading failed ... */
-        gnc_error_dialog (NULL, "%s", e.what());
+        gnc_error_dialog (nullptr, "%s", e.what());
             delete parse_data;
             return;
     }
     catch (std::range_error &e)
     {
         /* Parsing failed ... */
-        gnc_error_dialog (NULL, "%s", e.what());
+        gnc_error_dialog (nullptr, "%s", e.what());
         delete parse_data;
         return;
     }
 
-    if (info->parse_data) // Free parse_data if we have come back here
-        delete info->parse_data;
-    info->parse_data = parse_data;
+    if (info->tx_imp) // Free parse_data if we have come back here
+        delete info->tx_imp;
+    info->tx_imp = parse_data;
     info->previewing_errors = false; /* We're looking at all the data. */
     info->skip_errors = false; // Set skip_errors to False
-    csv_import_preview_refresh (info);
+    csv_tximp_preview_refresh (info);
 
     /* Get settings store and populate */
-    populate_settings_combo(GTK_COMBO_BOX(info->settings_combo));
-    gtk_combo_box_set_active (GTK_COMBO_BOX(info->settings_combo), 0);
+    populate_settings_combo(info->settings_combo);
+    gtk_combo_box_set_active (info->settings_combo, 0);
 
-    gtk_assistant_set_page_complete (assistant, page, TRUE);
-    gtk_assistant_set_current_page (assistant, num + 1);
+    gtk_assistant_set_page_complete (info->csv_imp_asst, page, true);
+    gtk_assistant_set_current_page (info->csv_imp_asst, num + 1);
 }
 
 
 /**************************************************
- * row_selection_update
+ * csv_tximp_row_sel_update
  *
  * refresh the start and end row highlighting
  **************************************************/
 static
-void row_selection_update (CsvImportTrans* info)
+void csv_tximp_preview_row_sel_update (CsvImportTrans* info)
 {
     GtkTreeIter iter;
     auto store = GTK_LIST_STORE(gtk_tree_view_get_model (info->treeview));
 
     /* Colorize rows that will be skipped */
-    for (uint i = 0; i < info->parse_data->parsed_lines.size(); i++)
+    for (uint i = 0; i < info->tx_imp->m_parsed_lines.size(); i++)
     {
-        const char *color = NULL;
-        if ((i < info->parse_data->skip_start_lines()) ||             // start rows to skip
-            (i >= info->parse_data->parsed_lines.size()
-                    - info->parse_data->skip_end_lines()) ||          // end rows to skip
-            (((i - info->parse_data->skip_start_lines()) % 2 == 1) && // skip every second row...
-             info->parse_data->skip_alt_lines()))                     // ...if requested
+        const char *color = nullptr;
+        if ((i < info->tx_imp->skip_start_lines()) ||             // start rows to skip
+            (i >= info->tx_imp->m_parsed_lines.size()
+                    - info->tx_imp->skip_end_lines()) ||          // end rows to skip
+            (((i - info->tx_imp->skip_start_lines()) % 2 == 1) && // skip every second row...
+             info->tx_imp->skip_alt_lines()))                     // ...if requested
             color = "pink";
         else
-            color = NULL;                                           // all other rows
+            color = nullptr;                                          // all other rows
 
-        bool valid = gtk_tree_model_iter_nth_child (GTK_TREE_MODEL(store), &iter, NULL, i);
+        bool valid = gtk_tree_model_iter_nth_child (GTK_TREE_MODEL(store), &iter, nullptr, i);
         if (valid)
             gtk_list_store_set (store, &iter, 0, color, -1);
     }
@@ -545,49 +537,51 @@ void row_selection_update (CsvImportTrans* info)
  * Should be called when settings are changed.
  *******************************************************/
 void
-csv_import_preview_refresh (CsvImportTrans *info)
+csv_tximp_preview_refresh (CsvImportTrans *info)
 {
-    GtkTreeIter   iter;
-
     // Set start row
-    GtkAdjustment *adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(info->start_row_spin));
-    gtk_adjustment_set_upper (adj, info->parse_data->parsed_lines.size());
-    gtk_spin_button_set_value (GTK_SPIN_BUTTON(info->start_row_spin),
-            info->parse_data->skip_start_lines());
+    auto adj = gtk_spin_button_get_adjustment (info->start_row_spin);
+    gtk_adjustment_set_upper (adj, info->tx_imp->m_parsed_lines.size());
+    gtk_spin_button_set_value (info->start_row_spin,
+            info->tx_imp->skip_start_lines());
 
     // Set end row
-    adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(info->end_row_spin));
-    gtk_adjustment_set_upper (adj, info->parse_data->parsed_lines.size());
-    gtk_spin_button_set_value (GTK_SPIN_BUTTON(info->end_row_spin),
-            info->parse_data->skip_end_lines());
+    adj = gtk_spin_button_get_adjustment (info->end_row_spin);
+    gtk_adjustment_set_upper (adj, info->tx_imp->m_parsed_lines.size());
+    gtk_spin_button_set_value (info->end_row_spin,
+            info->tx_imp->skip_end_lines());
 
     // Set Alternate rows
-    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->skip_rows), info->parse_data->skip_alt_lines());
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->skip_alt_rows_button),
+            info->tx_imp->skip_alt_lines());
 
     // Set multi-split indicator
-    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->multi_split_cbutton), info->parse_data->multi_split());
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->multi_split_cbutton),
+            info->tx_imp->multi_split());
 
     // Set Import Format
     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->csv_button),
-            (info->parse_data->file_format() == GncImpFileFormat::CSV));
+            (info->tx_imp->file_format() == GncImpFileFormat::CSV));
     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->fixed_button),
-            (info->parse_data->file_format() != GncImpFileFormat::CSV));
+            (info->tx_imp->file_format() != GncImpFileFormat::CSV));
 
     // This section deals with the combo's and character encoding
-    gtk_combo_box_set_active (GTK_COMBO_BOX(info->date_format_combo), info->parse_data->date_format());
-    gtk_combo_box_set_active (GTK_COMBO_BOX(info->currency_format_combo), info->parse_data->currency_format());
-    go_charmap_sel_set_encoding (info->encselector, info->parse_data->encoding().c_str());
+    gtk_combo_box_set_active (GTK_COMBO_BOX(info->date_format_combo),
+            info->tx_imp->date_format());
+    gtk_combo_box_set_active (GTK_COMBO_BOX(info->currency_format_combo),
+            info->tx_imp->currency_format());
+    go_charmap_sel_set_encoding (info->encselector, info->tx_imp->encoding().c_str());
 
     gnc_account_sel_set_account(GNC_ACCOUNT_SEL(info->acct_selector),
-            info->parse_data->base_account() , false);
+            info->tx_imp->base_account() , false);
 
     // Handle separator checkboxes and custom field, only relevant if the file format is csv
-    if (info->parse_data->file_format() == GncImpFileFormat::CSV)
+    if (info->tx_imp->file_format() == GncImpFileFormat::CSV)
     {
-        auto separators = info->parse_data->separators();
+        auto separators = info->tx_imp->separators();
         const auto stock_sep_chars = std::string (" \t,:;-");
         for (int i = 0; i < SEP_NUM_OF_TYPES; i++)
-            gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->sep_buttons[i]),
+            gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->sep_button[i]),
                 separators.find (stock_sep_chars[i]) != std::string::npos);
 
         // If there are any other separators in the separators string,
@@ -596,7 +590,7 @@ csv_import_preview_refresh (CsvImportTrans *info)
         while (!separators.empty() && pos != std::string::npos)
         {
             separators.erase(pos);
-            pos = separators.find_first_of (" \t,:;-");
+            pos = separators.find_first_of (stock_sep_chars);
         }
         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->custom_cbutton),
                 !separators.empty());
@@ -604,102 +598,87 @@ csv_import_preview_refresh (CsvImportTrans *info)
     }
 
     // Repopulate the parsed data table
-    csv_import_preview_refresh_table (info);
+    csv_tximp_preview_refresh_table (info);
 }
 
 
 
 /*******************************************************
- * csv_import_trans_srow_cb
+ * csv_tximp_preview_srow_cb
  *
  * call back for import start row
  *******************************************************/
-void csv_import_trans_srow_cb (GtkWidget *spin, gpointer user_data)
+void csv_tximp_preview_srow_cb (GtkSpinButton *spin, CsvImportTrans *info)
 {
-    CsvImportTrans *info = (CsvImportTrans*) user_data;
-    GtkAdjustment *adj;
 
     /* Get number of lines to skip at the beginning */
-    info->parse_data->skip_start_lines (gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON(spin)));
+    info->tx_imp->skip_start_lines (gtk_spin_button_get_value_as_int (spin));
 
     /* And adjust maximum number of lines that can be skipped at the end accordingly */
-    adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(info->end_row_spin));
-    gtk_adjustment_set_upper (adj, info->parse_data->parsed_lines.size()
-            - info->parse_data->skip_start_lines());
-
-    /* Refresh the row highlighting */
-    row_selection_update (info);
+    auto adj = gtk_spin_button_get_adjustment (info->end_row_spin);
+    gtk_adjustment_set_upper (adj, info->tx_imp->m_parsed_lines.size()
+            - info->tx_imp->skip_start_lines());
 
-    csv_import_validate_preview_settings (info);
+    csv_tximp_preview_row_sel_update (info);
+    csv_tximp_validate_preview_settings (info);
 }
 
 
 /*******************************************************
- * csv_import_trans_erow_cb
+ * csv_tximp_preview_erow_cb
  *
  * call back for import end row
  *******************************************************/
-void csv_import_trans_erow_cb (GtkWidget *spin, gpointer user_data)
+void csv_tximp_preview_erow_cb (GtkSpinButton *spin, CsvImportTrans *info)
 {
-    CsvImportTrans *info = (CsvImportTrans*) user_data;
-    GtkAdjustment *adj;
-
     /* Get number of lines to skip at the end */
-    info->parse_data->skip_end_lines (gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON(spin)));
+    info->tx_imp->skip_end_lines (gtk_spin_button_get_value_as_int (spin));
 
     /* And adjust maximum number of lines that can be skipped at the beginning accordingly */
-    adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(info->start_row_spin));
-    gtk_adjustment_set_upper (adj, info->parse_data->parsed_lines.size()
-            - info->parse_data->skip_end_lines());
-
-    /* Refresh the row highlighting */
-    row_selection_update (info);
+    auto adj = gtk_spin_button_get_adjustment (info->start_row_spin);
+    gtk_adjustment_set_upper (adj, info->tx_imp->m_parsed_lines.size()
+            - info->tx_imp->skip_end_lines());
 
-    csv_import_validate_preview_settings (info);
+    csv_tximp_preview_row_sel_update (info);
+    csv_tximp_validate_preview_settings (info);
 }
 
 
 /*******************************************************
- * csv_import_trans_skip_errors_cb
+ * csv_tximp_preview_skiperrors_cb
  *
  * call back for Skip Errors
  *******************************************************/
-void csv_import_trans_skip_errors_cb (GtkToggleButton *checkbox, gpointer user_data)
+void csv_tximp_preview_skiperrors_cb (GtkToggleButton *checkbox, CsvImportTrans *info)
 {
-    CsvImportTrans *info = (CsvImportTrans*) user_data;
-
     info->skip_errors = gtk_toggle_button_get_active (checkbox);
-    csv_import_validate_preview_settings (info);
+    csv_tximp_validate_preview_settings (info);
 }
 
 
 /*******************************************************
- * csv_import_trans_multisplit_cb
+ * csv_tximp_preview_multisplit_cb
  *
  * call back for import multi-split checkbox
  *******************************************************/
-void csv_import_trans_multisplit_cb (GtkToggleButton *checkbox, gpointer user_data)
+void csv_tximp_preview_multisplit_cb (GtkToggleButton *checkbox, CsvImportTrans *info)
 {
-    CsvImportTrans *info = (CsvImportTrans*) user_data;
-
-    info->parse_data->multi_split (gtk_toggle_button_get_active (checkbox));
-    csv_import_preview_refresh_table (info);
-    csv_import_validate_preview_settings (info);
+    info->tx_imp->multi_split (gtk_toggle_button_get_active (checkbox));
+    csv_tximp_preview_refresh_table (info);
+    csv_tximp_validate_preview_settings (info);
 }
 
 
 /*******************************************************
- * csv_import_trans_skiprows_cb
+ * csv_tximp_preview_skiprows_cb
  *
  * call back for import skip rows checkbox
  *******************************************************/
-void csv_import_trans_skiprows_cb (GtkToggleButton *checkbox, gpointer user_data)
+void csv_tximp_preview_skiprows_cb (GtkToggleButton *checkbox, CsvImportTrans *info)
 {
-    CsvImportTrans *info = (CsvImportTrans*) user_data;
-
-    info->parse_data->skip_alt_lines (gtk_toggle_button_get_active (checkbox));
-    row_selection_update (info);
-    csv_import_validate_preview_settings (info);
+    info->tx_imp->skip_alt_lines (gtk_toggle_button_get_active (checkbox));
+    csv_tximp_preview_row_sel_update (info);
+    csv_tximp_validate_preview_settings (info);
 }
 
 
@@ -710,9 +689,8 @@ void csv_import_trans_skiprows_cb (GtkToggleButton *checkbox, gpointer user_data
  */
 static GtkCellRenderer* gnc_csv_preview_get_cell_renderer (CsvImportTrans* info, int col)
 {
-    GList* renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT(gtk_tree_view_get_column (info->treeview, col)));
-
-    GtkCellRenderer* cell = GTK_CELL_RENDERER(renderers->data);
+    auto renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT(gtk_tree_view_get_column (info->treeview, col)));
+    auto cell = GTK_CELL_RENDERER(renderers->data);
     g_list_free (renderers);
     return cell;
 }
@@ -725,50 +703,50 @@ static GtkCellRenderer* gnc_csv_preview_get_cell_renderer (CsvImportTrans* info,
  * @param widget The widget that was changed
  * @param info The data that is being configured
  */
-void sep_button_clicked (GtkWidget* widget, CsvImportTrans* info)
+void csv_tximp_preview_sep_button_cb (GtkWidget* widget, CsvImportTrans* info)
 {
 
     /* Only manipulate separator characters if the currently open file is
      * csv separated. */
-    if (info->parse_data->file_format() != GncImpFileFormat::CSV)
+    if (info->tx_imp->file_format() != GncImpFileFormat::CSV)
         return;
 
     /* Add the corresponding characters to checked_separators for each
      * button that is checked. */
-    std::string checked_separators;
+    auto checked_separators = std::string();
     const auto stock_sep_chars = std::string (" \t,:;-");
     for (int i = 0; i < SEP_NUM_OF_TYPES; i++)
     {
-        if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(info->sep_buttons[i])))
+        if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(info->sep_button[i])))
             checked_separators += stock_sep_chars[i];
     }
 
     /* Add the custom separator if the user checked its button. */
     if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(info->custom_cbutton)))
     {
-        char* custom_sep = (char*)gtk_entry_get_text (info->custom_entry);
+        auto custom_sep = gtk_entry_get_text (info->custom_entry);
         if (custom_sep[0] != '\0') /* Don't add a blank separator (bad things will happen!). */
             checked_separators += custom_sep;
     }
 
     /* Set the parse options using the checked_separators list. */
-    info->parse_data->separators (checked_separators);
+    info->tx_imp->separators (checked_separators);
 
     /* Parse the data using the new options. We don't want to reguess
      * the column types because we want to leave the user's
      * configurations intact. */
     try
     {
-        info->parse_data->tokenize (false);
-        csv_import_preview_refresh_table (info);
-        csv_import_validate_preview_settings (info);
+        info->tx_imp->tokenize (false);
+        csv_tximp_preview_refresh_table (info);
+        csv_tximp_validate_preview_settings (info);
     }
     catch (std::range_error &e)
     {
         /* Warn the user there was a problem and try to undo what caused
          * the error. (This will cause a reparsing and ideally a usable
          * configuration.) */
-        gnc_error_dialog (NULL, "Error in parsing");
+        gnc_error_dialog (nullptr, "Error in parsing");
         /* If we're here because the user changed the file format, we should just wait for the user
          * to update the configuration */
         if (!widget)
@@ -784,13 +762,13 @@ void sep_button_clicked (GtkWidget* widget, CsvImportTrans* info)
     }
 }
 
-void account_selected_cb (GtkWidget* widget, CsvImportTrans* info)
+void csv_tximp_preview_acct_sel_cb (GtkWidget* widget, CsvImportTrans* info)
 {
 
     auto acct = gnc_account_sel_get_account( GNC_ACCOUNT_SEL(widget) );
-    info->parse_data->base_account(acct);
-    csv_import_preview_refresh_table (info);
-    csv_import_validate_preview_settings (info);
+    info->tx_imp->base_account(acct);
+    csv_tximp_preview_refresh_table (info);
+    csv_tximp_validate_preview_settings (info);
 }
 
 
@@ -799,24 +777,24 @@ void account_selected_cb (GtkWidget* widget, CsvImportTrans* info)
  * @param csv_button The "Separated" radio button
  * @param info The display of the data being imported
  */
-static void separated_or_fixed_selected (GtkToggleButton* csv_button, CsvImportTrans* info)
+void csv_tximp_preview_sep_fixed_sel_cb (GtkToggleButton* csv_button, CsvImportTrans* info)
 {
     /* Set the parsing type correctly. */
     try
     {
         if (gtk_toggle_button_get_active (csv_button))
-            info->parse_data->file_format (GncImpFileFormat::CSV);
+            info->tx_imp->file_format (GncImpFileFormat::CSV);
         else
-            info->parse_data->file_format (GncImpFileFormat::FIXED_WIDTH);
+            info->tx_imp->file_format (GncImpFileFormat::FIXED_WIDTH);
 
-        info->parse_data->tokenize (false);
-        csv_import_preview_refresh_table (info);
-        csv_import_validate_preview_settings (info);
+        info->tx_imp->tokenize (false);
+        csv_tximp_preview_refresh_table (info);
+        csv_tximp_validate_preview_settings (info);
     }
     catch (std::range_error &e)
     {
         /* Parsing failed ... */
-        gnc_error_dialog (NULL, "%s", e.what());
+        gnc_error_dialog (nullptr, "%s", e.what());
         return;
     }
     catch (...)
@@ -834,39 +812,32 @@ static void separated_or_fixed_selected (GtkToggleButton* csv_button, CsvImportT
  * @param encoding The encoding that the user selected
  * @param info The display of the data being imported
  */
-static void encoding_selected (GOCharmapSel* selector, const char* encoding,
+void csv_tximp_preview_enc_sel_cb (GOCharmapSel* selector, const char* encoding,
                               CsvImportTrans* info)
 {
     /* This gets called twice every time a new encoding is selected. The
      * second call actually passes the correct data; thus, we only do
      * something the second time this is called. */
 
-    /* Prevent code-caused calls of this function from having an impact. */
-    if (info->code_encoding_calls > 0)
-    {
-        info->code_encoding_calls--;
-        return;
-    }
-
     /* If this is the second time the function is called ... */
     if (info->encoding_selected_called)
     {
-        std::string previous_encoding = info->parse_data->tokenizer->encoding();
+        std::string previous_encoding = info->tx_imp->m_tokenizer->encoding();
         /* Try converting the new encoding and reparsing. */
         try
         {
-            info->parse_data->encoding (encoding);
-            info->parse_data->tokenize (false);
+            info->tx_imp->encoding (encoding);
+            info->tx_imp->tokenize (false);
 
-            csv_import_preview_refresh_table (info);
-            csv_import_validate_preview_settings (info);
+            csv_tximp_preview_refresh_table (info);
+            csv_tximp_validate_preview_settings (info);
 
             info->encoding_selected_called = false;
         }
         catch (...)
         {
             /* If it fails, change back to the old encoding. */
-            gnc_error_dialog (NULL, "%s", _("Invalid encoding selected"));
+            gnc_error_dialog (nullptr, "%s", _("Invalid encoding selected"));
             info->encoding_selected_called = false;
             go_charmap_sel_set_encoding (selector, previous_encoding.c_str());
         }
@@ -882,10 +853,10 @@ static void encoding_selected (GOCharmapSel* selector, const char* encoding,
  * @param format_selector The combo box for selecting date formats
  * @param info The display of the data being imported
  */
-static void date_format_selected (GtkComboBox* format_selector, CsvImportTrans* info)
+static void csv_tximp_preview_date_fmt_sel_cb (GtkComboBox* format_selector, CsvImportTrans* info)
 {
-    info->parse_data->date_format (gtk_combo_box_get_active (format_selector));
-    csv_import_validate_preview_settings (info);
+    info->tx_imp->date_format (gtk_combo_box_get_active (format_selector));
+    csv_tximp_validate_preview_settings (info);
 }
 
 
@@ -893,10 +864,10 @@ static void date_format_selected (GtkComboBox* format_selector, CsvImportTrans*
  * @param currency_selector The combo box for selecting currency formats
  * @param info The display of the data being imported
  */
-static void currency_format_selected (GtkComboBox* currency_selector, CsvImportTrans* info)
+static void csv_tximp_preview_currency_fmt_sel_cb (GtkComboBox* currency_selector, CsvImportTrans* info)
 {
-    info->parse_data->currency_format (gtk_combo_box_get_active (currency_selector));
-    csv_import_validate_preview_settings (info);
+    info->tx_imp->currency_format (gtk_combo_box_get_active (currency_selector));
+    csv_tximp_validate_preview_settings (info);
 }
 
 
@@ -946,12 +917,12 @@ static GnumericPopupMenuElement const popup_elements[] =
         N_("Merge with column on _right"), GTK_STOCK_REMOVE,
         0, 1 << CONTEXT_STF_IMPORT_MERGE_RIGHT, CONTEXT_STF_IMPORT_MERGE_RIGHT
     },
-    { "", NULL, 0, 0, 0 },
+    { "", nullptr, 0, 0, 0 },
     {
-        N_("_Split this column"), NULL,
+        N_("_Split this column"), nullptr,
         0, 1 << CONTEXT_STF_IMPORT_SPLIT, CONTEXT_STF_IMPORT_SPLIT
     },
-    { "", NULL, 0, 0, 0 },
+    { "", nullptr, 0, 0, 0 },
     {
         N_("_Widen this column"), GTK_STOCK_GO_FORWARD,
         0, 1 << CONTEXT_STF_IMPORT_WIDEN, CONTEXT_STF_IMPORT_WIDEN
@@ -960,22 +931,21 @@ static GnumericPopupMenuElement const popup_elements[] =
         N_("_Narrow this column"), GTK_STOCK_GO_BACK,
         0, 1 << CONTEXT_STF_IMPORT_NARROW, CONTEXT_STF_IMPORT_NARROW
     },
-    { NULL, NULL, 0, 0, 0 },
+    { nullptr, nullptr, 0, 0, 0 },
 };
 
 static uint get_new_col_rel_pos (CsvImportTrans* info, int col, int dx)
 {
+    auto cell = gnc_csv_preview_get_cell_renderer (info, col);
     PangoFontDescription *font_desc;
-    int width;
-    uint charindex;
-    GtkCellRenderer *cell = gnc_csv_preview_get_cell_renderer (info, col);
-    g_object_get (G_OBJECT(cell), "font_desc", &font_desc, NULL);
+    g_object_get (G_OBJECT(cell), "font_desc", &font_desc, nullptr);
 
     PangoLayout *layout = gtk_widget_create_pango_layout (GTK_WIDGET(info->treeview), "x");
     pango_layout_set_font_description (layout, font_desc);
-    pango_layout_get_pixel_size (layout, &width, NULL);
+    int width;
+    pango_layout_get_pixel_size (layout, &width, nullptr);
     if (width < 1) width = 1;
-    charindex = (dx + width / 2) / width;
+    uint charindex = (dx + width / 2) / width;
     g_object_unref (layout);
     pango_font_description_free (font_desc);
 
@@ -984,12 +954,12 @@ static uint get_new_col_rel_pos (CsvImportTrans* info, int col, int dx)
 
 static gboolean
 fixed_context_menu_handler (GnumericPopupMenuElement const *element,
-                            gpointer user_data)
+        gpointer userdata)
 {
-    CsvImportTrans *info = (CsvImportTrans*) user_data;
-    int col = info->fixed_context_col;
-    uint rel_pos = get_new_col_rel_pos (info, col, info->fixed_context_dx);
-    GncFwTokenizer *fwtok = dynamic_cast<GncFwTokenizer*>(info->parse_data->tokenizer.get());
+    auto info = (CsvImportTrans*)userdata;
+    auto col = info->fixed_context_col;
+    auto rel_pos = get_new_col_rel_pos (info, col, info->fixed_context_dx);
+    auto fwtok = dynamic_cast<GncFwTokenizer*>(info->tx_imp->m_tokenizer.get());
 
     switch (element->index)
     {
@@ -1014,40 +984,37 @@ fixed_context_menu_handler (GnumericPopupMenuElement const *element,
 
     try
     {
-        info->parse_data->tokenize (false);
+        info->tx_imp->tokenize (false);
     }
     catch(std::range_error& e)
     {
-        gnc_error_dialog (NULL, "%s", e.what());
-        return FALSE;
+        gnc_error_dialog (nullptr, "%s", e.what());
+        return false;
     }
-    csv_import_preview_refresh_table (info);
-    csv_import_validate_preview_settings (info);
-    return TRUE;
+    csv_tximp_preview_refresh_table (info);
+    csv_tximp_validate_preview_settings (info);
+    return true;
 }
 static void
 select_column (CsvImportTrans* info, int col)
 {
-    GtkTreeViewColumn *column;
-
     if (col < 0)
         return;
 
-    column = gtk_tree_view_get_column (info->treeview, col);
-    gtk_widget_grab_focus (column->button);
+    auto column = gtk_tree_view_get_column (info->treeview, col);
+    gtk_widget_grab_focus (gtk_tree_view_column_get_widget(column));
 }
 
 static void
 fixed_context_menu (CsvImportTrans* info, GdkEventButton *event,
                     int col, int dx)
 {
-    int sensitivity_filter = 0;
-
-    GncFwTokenizer *fwtok = dynamic_cast<GncFwTokenizer*>(info->parse_data->tokenizer.get());
+    auto fwtok = dynamic_cast<GncFwTokenizer*>(info->tx_imp->m_tokenizer.get());
     info->fixed_context_col = col;
     info->fixed_context_dx = dx;
     uint rel_pos = get_new_col_rel_pos (info, col, dx);
 
+    int sensitivity_filter = 0;
     if (!fwtok->col_can_delete (col - 1))
         sensitivity_filter |= (1 << CONTEXT_STF_IMPORT_MERGE_LEFT);
     if (!fwtok->col_can_delete (col))
@@ -1076,28 +1043,22 @@ fixed_context_menu (CsvImportTrans* info, GdkEventButton *event,
  * @param allocation The size of the data treeview
  * @param info The display of the data being imported
  */
-static void treeview_resized (GtkWidget* widget, GtkAllocation* allocation, CsvImportTrans* info)
+static void csv_tximp_preview_treeview_resized_cb (GtkWidget* widget, GtkAllocation* allocation, CsvImportTrans* info)
 {
     /* Go through each column except for the last. (We don't want to set
      * the width of the last column because the user won't be able to
      * shrink the dialog back if it's expanded.) */
-    for (uint i = 0; i < info->parse_data->column_types().size() - 1; i++)
+    for (uint i = 0; i < info->tx_imp->column_types().size() - 1; i++)
     {
-        gint col_width; /* The width of the column in info->treeview. */
-        GtkTreeViewColumn* pcol;
-        GtkTreeViewColumn* ccol; /* The corresponding column in info->ctreeview. */
-
         /* Get the width. */
-        col_width = gtk_tree_view_column_get_width (gtk_tree_view_get_column (info->treeview, i));
+        auto col_width = gtk_tree_view_column_get_width (gtk_tree_view_get_column (info->treeview, i));
         /* Set the minimum width for a column so that drop down selector can be seen. */
         if (col_width < MIN_COL_WIDTH)
-        {
             col_width = MIN_COL_WIDTH;
-        }
-        pcol = gtk_tree_view_get_column (info->treeview, i);
+        auto pcol = gtk_tree_view_get_column (info->treeview, i);
         gtk_tree_view_column_set_min_width (pcol, col_width);
         /* Set ccol's width the same. */
-        ccol = gtk_tree_view_get_column (info->ctreeview, i);
+        auto ccol = gtk_tree_view_get_column (info->ctreeview, i);
         gtk_tree_view_column_set_min_width (ccol, col_width);
         gtk_tree_view_column_set_max_width (ccol, col_width);
     }
@@ -1113,43 +1074,40 @@ static void treeview_resized (GtkWidget* widget, GtkAllocation* allocation, CsvI
  * @param new_text The text the user selected
  * @param info The display of the data being imported
  */
-static void column_type_changed (GtkCellRenderer* renderer, gchar* path,
+static void csv_tximp_preview_col_type_changed_cb (GtkCellRenderer* renderer, gchar* path,
                                 GtkTreeIter* new_text_iter, CsvImportTrans* info)
 {
-    GtkTreeModel* model;
-    gchar* new_text;
-    auto new_col_type = GncTransPropType::NONE;
-
     /* Get the new text */
     auto col_num = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT(renderer), "col-num"));
-    g_object_get (renderer, "model", &model, NULL);
+    GtkTreeModel* model;
+    g_object_get (renderer, "model", &model, nullptr);
+    auto new_col_type = GncTransPropType::NONE;
     gtk_tree_model_get (model, new_text_iter,
             1, &new_col_type, // Invisible column in the combobox' model containing the column type
             -1);
 
-    info->parse_data->set_column_type (col_num, new_col_type);
-    csv_import_preview_refresh_table (info);
-    csv_import_validate_preview_settings (info);
+    info->tx_imp->set_column_type (col_num, new_col_type);
+    csv_tximp_preview_refresh_table (info);
+    csv_tximp_validate_preview_settings (info);
 }
 
 static void
 split_column (CsvImportTrans* info, int col, int dx)
 {
-    uint rel_pos = get_new_col_rel_pos (info, col, dx);
-
-    GncFwTokenizer *fwtok = dynamic_cast<GncFwTokenizer*>(info->parse_data->tokenizer.get());
+    auto rel_pos = get_new_col_rel_pos (info, col, dx);
+    auto fwtok = dynamic_cast<GncFwTokenizer*>(info->tx_imp->m_tokenizer.get());
     fwtok->col_split (col, rel_pos);
     try
     {
-        info->parse_data->tokenize (false);
+        info->tx_imp->tokenize (false);
     }
     catch (std::range_error& e)
     {
-        gnc_error_dialog (NULL, "%s", e.what());
+        gnc_error_dialog (nullptr, "%s", e.what());
         return;
     }
-    csv_import_preview_refresh_table (info);
-    csv_import_validate_preview_settings (info);
+    csv_tximp_preview_refresh_table (info);
+    csv_tximp_validate_preview_settings (info);
 }
 
 
@@ -1167,23 +1125,19 @@ static void header_button_press_handler (GtkWidget* button, GdkEventButton* even
     auto col = GPOINTER_TO_UINT(g_object_get_data (G_OBJECT(button), "col-num"));
 
     /* Don't let the user affect the last column if it has error messages. */
-    if (col == info->parse_data->column_types().size())
+    if (col == info->tx_imp->column_types().size())
         return;
 
     /*  calculate offset to compensate for the button indentation. */
     GtkAllocation alloc;
     gtk_widget_get_allocation (gtk_bin_get_child (GTK_BIN(button)), &alloc);
     auto offset = alloc.x - alloc.x;
-    /* Double clicks can split columns. */
     if (event->type == GDK_2BUTTON_PRESS && event->button == 1)
-    {
+        /* Double clicks can split columns. */
         split_column (info, col, (int)event->x - offset);
-    }
-    /* Right clicking brings up a context menu. */
     else if (event->type == GDK_BUTTON_PRESS && event->button == 3)
-    {
+        /* Right clicking brings up a context menu. */
         fixed_context_menu (info, event, col, (int)event->x - offset);
-    }
 }
 
 
@@ -1198,9 +1152,7 @@ static bool
 check_for_duplicates (GtkListStore *liststore, const gchar *string)
 {
     GtkTreeIter iter;
-    bool valid;
-
-    valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL(liststore), &iter);
+    auto valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL(liststore), &iter);
     while (valid)
     {
         gchar *text;
@@ -1229,26 +1181,21 @@ check_for_duplicates (GtkListStore *liststore, const gchar *string)
  */
 bool get_list_of_accounts (CsvImportTrans* info, GtkTreeModel *store)
 {
-    bool     have_accounts = false;
-
     /* ctstore contains the actual strings appearing in the column types treeview. */
-    GtkTreeModel* ctstore = gtk_tree_view_get_model (info->ctreeview);
-    /* datastore contains the actual strings appearing in the preview treeview. */
-    GtkTreeModel* datastore = gtk_tree_view_get_model (info->treeview);
-
-    GtkTreeIter iter1, iter2, iter3;
+    auto ctstore = gtk_tree_view_get_model (info->ctreeview);
+    GtkTreeIter ct_iter;
+    gtk_tree_model_get_iter_first (ctstore, &ct_iter);
 
-    /* Get an iterator for the first (and only) row of the column store. */
-    gtk_tree_model_get_iter_first (ctstore, &iter1);
-
-    for (uint j = info->parse_data->skip_start_lines();
-            j < info->parse_data->parsed_lines.size() - info->parse_data->skip_end_lines() - 1;
+    /* datastore contains the actual strings appearing in the preview treeview. */
+    auto datastore = gtk_tree_view_get_model (info->treeview);
+    auto have_accounts = false;
+    for (uint j = info->tx_imp->skip_start_lines();
+            j < info->tx_imp->m_parsed_lines.size() - info->tx_imp->skip_end_lines() - 1;
             j++)
     {
         /* Go through each of the columns. */
-        for (uint i = 0; i < info->parse_data->column_types().size(); i++)
+        for (uint i = 0; i < info->tx_imp->column_types().size(); i++)
         {
-            gchar* accstr = NULL;   /* The string in this column from datastore. */
             auto col_type = GncTransPropType::NONE;
 
             /* Get the column type. Store is arranged so that every two
@@ -1257,24 +1204,27 @@ bool get_list_of_accounts (CsvImportTrans* info, GtkTreeModel *store)
              * - the internal type for this column
              * So store looks like:
              * col_type_str 0, col_type, col_type_str 1, col_type 1, ..., col_type_str ncols, col_type ncols. */
-            gtk_tree_model_get (ctstore, &iter1, 2 * i + 1, &col_type, -1);
+            gtk_tree_model_get (ctstore, &ct_iter, 2 * i + 1, &col_type, -1);
 
             /* We're only interested in columns of type ACCOUNT and OACCOUNT. */
             if ((col_type == GncTransPropType::ACCOUNT) || (col_type == GncTransPropType::TACCOUNT))
             {
                 /* Get an iterator for the row in the data store. */
-                if (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL(datastore), &iter2, NULL, j))
+                GtkTreeIter data_iter;
+                if (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL(datastore), &data_iter, nullptr, j))
                 {
-                    gtk_tree_model_get (datastore, &iter2, i + 1, &accstr, -1);
+                    gchar* accstr = nullptr;   /* The string in this column from datastore. */
+                    gtk_tree_model_get (datastore, &data_iter, i + 1, &accstr, -1);
                     if (!accstr || *accstr == '\0')
                         continue;
 
                     // Append the entry
                     if (!check_for_duplicates (GTK_LIST_STORE(store), accstr))
                     {
-                        gtk_list_store_append (GTK_LIST_STORE(store), &iter3);
-                        gtk_list_store_set (GTK_LIST_STORE(store), &iter3, MAPPING_STRING, accstr,
-                                            MAPPING_FULLPATH, _("No Linked Account"), MAPPING_ACCOUNT, NULL, -1);
+                        GtkTreeIter acct_iter;
+                        gtk_list_store_append (GTK_LIST_STORE(store), &acct_iter);
+                        gtk_list_store_set (GTK_LIST_STORE(store), &acct_iter, MAPPING_STRING, accstr,
+                                            MAPPING_FULLPATH, _("No Linked Account"), MAPPING_ACCOUNT, nullptr, -1);
                         have_accounts = true;
                     }
                     g_free (accstr);
@@ -1291,10 +1241,10 @@ bool get_list_of_accounts (CsvImportTrans* info, GtkTreeModel *store)
  *
  * @param info The data being previewed
  */
-void csv_import_preview_refresh_table (CsvImportTrans* info)
+void csv_tximp_preview_refresh_table (CsvImportTrans* info)
 {
     /* ncols is the number of columns in the file data. */
-    auto column_types = info->parse_data->column_types();
+    auto column_types = info->tx_imp->column_types();
     auto ncols = column_types.size();
 
     // Set up the header liststore
@@ -1309,7 +1259,7 @@ void csv_import_preview_refresh_table (CsvImportTrans* info)
      * So store looks like:
      * col_type_str 0, col_type, col_type_str 1, col_type 1, ..., col_type_str ncols, col_type ncols.
      * And then a final column is added for the Error column (which doesn't require a col_type) */
-    GType* headertypes = g_new (GType, 2 * ncols + 1);
+    auto headertypes = g_new (GType, 2 * ncols + 1);
     for (guint i = 0; i < 2 * ncols; i += 2)
     {
         headertypes[i] = G_TYPE_STRING;
@@ -1337,14 +1287,14 @@ void csv_import_preview_refresh_table (CsvImportTrans* info)
 
     /* store is a liststore to hold the data from the file being imported.
        it contains only strings. */
-    GType* bodytypes = g_new (GType, ncols + 2);
+    auto bodytypes = g_new (GType, ncols + 2);
     for (guint i = 0; i <  ncols + 2; i++)
         bodytypes[i] = G_TYPE_STRING;
     auto store = gtk_list_store_newv (ncols + 2, bodytypes);
     g_free (bodytypes);
 
     /* Fill the data liststore with data from the file. */
-    for (auto parse_line : info->parse_data->parsed_lines)
+    for (auto parse_line : info->tx_imp->m_parsed_lines)
     {
         // When previewing errors skip all lines that don't have errors
         if (info->previewing_errors && std::get<1>(parse_line).empty())
@@ -1354,7 +1304,7 @@ void csv_import_preview_refresh_table (CsvImportTrans* info)
         gtk_list_store_append (store, &iter);
 
         /* Row Color column */
-        gtk_list_store_set (store, &iter, 0, NULL, -1);
+        gtk_list_store_set (store, &iter, 0, nullptr, -1);
 
         for (auto cell_str_it = std::get<0>(parse_line).cbegin(); cell_str_it != std::get<0>(parse_line).cend(); cell_str_it++)
         {
@@ -1391,7 +1341,7 @@ void csv_import_preview_refresh_table (CsvImportTrans* info)
         /* Only add column types that make sense in
          * the chosen import mode (multi-split vs two-split).
          */
-        if (sanitize_trans_prop(col_type.first, info->parse_data->multi_split()) == col_type.first)
+        if (sanitize_trans_prop(col_type.first, info->tx_imp->multi_split()) == col_type.first)
         {
             GtkTreeIter iter;
             gtk_list_store_append (combostore, &iter);
@@ -1402,7 +1352,7 @@ void csv_import_preview_refresh_table (CsvImportTrans* info)
     }
 
     /* Insert columns into the data and column type treeviews. */
-    for (guint i = 0; i < ncols ; i++)
+    for (uint i = 0; i < ncols ; i++)
     {
         /* The header cells are combobox entries. They all use the same
          * common model for the dropdown list while their text value
@@ -1410,34 +1360,35 @@ void csv_import_preview_refresh_table (CsvImportTrans* info)
         auto crenderer = gtk_cell_renderer_combo_new();
         /* Set the properties for the dropdown list */
         g_object_set (G_OBJECT(crenderer), "model", combostore, "text-column", 0,
-                     "editable", TRUE, "has-entry", FALSE, NULL);
+                     "editable", TRUE, "has-entry", FALSE, nullptr);
         g_object_set_data (G_OBJECT(crenderer),
                            "col-num", GUINT_TO_POINTER(i));
         g_signal_connect (G_OBJECT(crenderer), "changed",
-                         G_CALLBACK(column_type_changed), (gpointer)info);
+                         G_CALLBACK(csv_tximp_preview_col_type_changed_cb), (gpointer)info);
         /* Insert the column */
         gtk_tree_view_insert_column_with_attributes (info->ctreeview,
-                -1, "", crenderer, "text", 2 * i, NULL);
+                -1, "", crenderer, "text", 2 * i, nullptr);
 
         /* The file data treeview cells are simple text cells. */
         auto renderer = gtk_cell_renderer_text_new();
         /* We want a monospace font for the data in case of fixed-width data. */
-        g_object_set (G_OBJECT(renderer), "family", "monospace", NULL);
+        g_object_set (G_OBJECT(renderer), "family", "monospace", nullptr);
 
         /* Add a single column for the treeview. */
         auto col = gtk_tree_view_column_new_with_attributes ("", renderer, "background", 0,
-                                                             "text", i + 1, NULL);
+                                                             "text", i + 1, nullptr);
         gtk_tree_view_insert_column (info->treeview, col, -1);
         /* Enable resizing of the columns. */
         gtk_tree_view_column_set_resizable (col, TRUE);
 
         /* We need to allow clicking on the file data's column headers for
          * fixed-width column splitting and merging. */
-        g_object_set (G_OBJECT(col), "clickable", TRUE, NULL);
-        g_signal_connect (G_OBJECT(col->button), "button_press_event",
+        g_object_set (G_OBJECT(col), "clickable", TRUE, nullptr);
+        auto button = gtk_tree_view_column_get_widget(col);
+        g_signal_connect (G_OBJECT(button), "button_press_event",
                          G_CALLBACK(header_button_press_handler), (gpointer)info);
         /* Store the column number in the button to know which one was clicked later on */
-        g_object_set_data (G_OBJECT(col->button), "col-num",GINT_TO_POINTER(i));
+        g_object_set_data (G_OBJECT(button), "col-num",GINT_TO_POINTER(i));
     }
 
     /* Add a column for the error messages when reviewing errors */
@@ -1445,11 +1396,11 @@ void csv_import_preview_refresh_table (CsvImportTrans* info)
     {
         auto crenderer = gtk_cell_renderer_text_new();
         gtk_tree_view_insert_column_with_attributes (info->ctreeview,
-                -1, "", crenderer, "text", 2 * ncols, NULL);
+                -1, "", crenderer, "text", 2 * ncols, nullptr);
 
         auto renderer = gtk_cell_renderer_text_new();
         auto col = gtk_tree_view_column_new_with_attributes ("", renderer, "background", 0,
-                                                             "text", ncols + 1, NULL);
+                                                             "text", ncols + 1, nullptr);
         gtk_tree_view_insert_column (info->treeview, col, -1);
         /* Enable resizing of the columns. */
         gtk_tree_view_column_set_resizable (col, TRUE);
@@ -1461,25 +1412,25 @@ void csv_import_preview_refresh_table (CsvImportTrans* info)
     gtk_tree_selection_select_iter (selection, &iter);
 
     /* Release our reference for the stores to allow proper memory management. */
-    g_object_unref (GTK_TREE_MODEL(store));
-    g_object_unref (GTK_TREE_MODEL(ctstore));
-    g_object_unref (GTK_TREE_MODEL(combostore));
+    g_object_unref (store);
+    g_object_unref (ctstore);
+    g_object_unref (combostore);
 
     /* Make the things actually appear. */
     gtk_widget_show_all (GTK_WIDGET(info->treeview));
     gtk_widget_show_all (GTK_WIDGET(info->ctreeview));
 
     /* Update the row selection highlight */
-    row_selection_update (info);
+    csv_tximp_preview_row_sel_update (info);
 
 }
 
 
-void csv_import_validate_preview_settings (CsvImportTrans* info)
+void csv_tximp_validate_preview_settings (CsvImportTrans* info)
 {
     /* Allow the user to proceed only if there are no inconsistencies in the settings */
-    auto error_msg = info->parse_data->verify();
-    gtk_assistant_set_page_complete (GTK_ASSISTANT(info->window), info->preview_page, error_msg.empty());
+    auto error_msg = info->tx_imp->verify();
+    gtk_assistant_set_page_complete (info->csv_imp_asst, info->preview_page, error_msg.empty());
     gtk_label_set_text(GTK_LABEL(info->instructions_label), error_msg.c_str());
     gtk_widget_set_visible (GTK_WIDGET(info->instructions_image), !error_msg.empty());
 }
@@ -1491,31 +1442,27 @@ void csv_import_validate_preview_settings (CsvImportTrans* info)
  * Assistant page prepare functions
  *******************************************************/
 void
-csv_import_trans_assistant_start_page_prepare (GtkAssistant *assistant,
-        gpointer user_data)
+csv_tximp_assist_start_page_prepare (GtkAssistant *assistant,
+        CsvImportTrans* info)
 {
-    CsvImportTrans *info = (CsvImportTrans*) user_data;
-    GtkTreeModel   *store;
-
-    gint num = gtk_assistant_get_current_page (assistant);
-    GtkWidget *page = gtk_assistant_get_nth_page (assistant, num);
+    auto num = gtk_assistant_get_current_page (assistant);
+    auto page = gtk_assistant_get_nth_page (assistant, num);
 
     // Clear the treemodel list store
-    store = gtk_tree_view_get_model (GTK_TREE_VIEW(info->account_match_view));
+    auto store = gtk_tree_view_get_model (GTK_TREE_VIEW(info->account_match_view));
     gtk_list_store_clear (GTK_LIST_STORE(store));
 
     /* Enable the Assistant Buttons */
-    gtk_assistant_set_page_complete (assistant, page, TRUE);
+    gtk_assistant_set_page_complete (assistant, page, true);
 }
 
 
 void
-csv_import_trans_assistant_file_page_prepare (GtkAssistant *assistant,
-        gpointer user_data)
+csv_tximp_assist_file_page_prepare (GtkAssistant *assistant,
+        CsvImportTrans* info)
 {
-    CsvImportTrans *info = (CsvImportTrans*) user_data;
-    gint            num = gtk_assistant_get_current_page (assistant);
-    GtkWidget      *page = gtk_assistant_get_nth_page (assistant, num);
+    auto num = gtk_assistant_get_current_page (assistant);
+    auto page = gtk_assistant_get_nth_page (assistant, num);
 
     info->previewing_errors = false; // We're looking at all the data.
     info->skip_errors = false; // Set skip_errors to False to start with.
@@ -1529,30 +1476,27 @@ csv_import_trans_assistant_file_page_prepare (GtkAssistant *assistant,
     }
 
     /* Disable the Forward Assistant Button */
-    gtk_assistant_set_page_complete (assistant, page, FALSE);
+    gtk_assistant_set_page_complete (assistant, page, false);
 }
 
 
 void
-csv_import_trans_assistant_preview_page_prepare (GtkAssistant *assistant,
-        gpointer user_data)
+csv_tximp_assist_preview_page_prepare (GtkAssistant *assistant,
+        CsvImportTrans* info)
 {
-    CsvImportTrans *info = (CsvImportTrans*) user_data;
-
     g_signal_connect (G_OBJECT(info->treeview), "size-allocate",
-                     G_CALLBACK(treeview_resized), (gpointer)info);
+                     G_CALLBACK(csv_tximp_preview_treeview_resized_cb), (gpointer)info);
 
     // Hide the skip errors check button
-    gtk_widget_hide (GTK_WIDGET(info->check_butt));
+    gtk_widget_hide (GTK_WIDGET(info->skip_errors_button));
 
     if (info->previewing_errors) // We are looking at errors to display
     {
-        gchar* name;
-        GtkIconSize size;
-
         /* Block going back */
-        gtk_assistant_commit (GTK_ASSISTANT(info->window));
+        gtk_assistant_commit (info->csv_imp_asst);
 
+        gchar* name;
+        GtkIconSize size;
         gtk_image_get_stock (info->instructions_image, &name, &size);
         gtk_image_set_from_stock (info->instructions_image, GTK_STOCK_DIALOG_ERROR, size);
         gtk_label_set_text (info->instructions_label,
@@ -1561,27 +1505,25 @@ csv_import_trans_assistant_preview_page_prepare (GtkAssistant *assistant,
         gtk_widget_show (GTK_WIDGET(info->instructions_label));
 
         /* Reset start and end row */
-        gtk_spin_button_set_value (GTK_SPIN_BUTTON(info->start_row_spin), 0);
-        gtk_spin_button_set_value (GTK_SPIN_BUTTON(info->end_row_spin), 0);
+        gtk_spin_button_set_value (info->start_row_spin, 0);
+        gtk_spin_button_set_value (info->end_row_spin, 0);
 
         /* Set spin buttons and settings combo hbox not sensitive */
         gtk_widget_set_sensitive (info->combo_hbox, FALSE);
-        gtk_widget_set_sensitive (info->start_row_spin, FALSE);
-        gtk_widget_set_sensitive (info->end_row_spin, FALSE);
-        gtk_widget_set_sensitive (info->skip_rows, FALSE);
+        gtk_widget_set_sensitive (GTK_WIDGET(info->start_row_spin), FALSE);
+        gtk_widget_set_sensitive (GTK_WIDGET(info->end_row_spin), FALSE);
+        gtk_widget_set_sensitive (info->skip_alt_rows_button, FALSE);
         gtk_widget_set_sensitive (info->multi_split_cbutton, FALSE);
-        info->parse_data->skip_alt_lines (false);
+        info->tx_imp->skip_alt_lines (false);
 
         /* Show the skip errors check button */
-        gtk_widget_show (GTK_WIDGET(info->check_butt));
-        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->check_butt), FALSE);
+        gtk_widget_show (GTK_WIDGET(info->skip_errors_button));
+        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->skip_errors_button), FALSE);
     }
     else
     {
-        GtkTreeModel *store;
-
         // Load the account strings into the store
-        store = gtk_tree_view_get_model (GTK_TREE_VIEW(info->account_match_view));
+        auto store = gtk_tree_view_get_model (GTK_TREE_VIEW(info->account_match_view));
         gtk_list_store_clear (GTK_LIST_STORE(store)); // Clear list of accounts unless we are looking at errors
     }
 
@@ -1591,8 +1533,8 @@ csv_import_trans_assistant_preview_page_prepare (GtkAssistant *assistant,
     gtk_assistant_set_page_complete (assistant, page, false);
 
     /* Load the data into the treeview. */
-    csv_import_preview_refresh_table (info);
-    csv_import_validate_preview_settings (info);
+    csv_tximp_preview_refresh_table (info);
+    csv_tximp_validate_preview_settings (info);
 
 }
 
@@ -1600,13 +1542,12 @@ csv_import_trans_assistant_preview_page_prepare (GtkAssistant *assistant,
 static bool
 import_account_check_all (GtkTreeModel *model)
 {
-    GtkTreeIter iter;
-    bool valid, ret = true;
-
     // Set iter to first entry of store
-    valid = gtk_tree_model_get_iter_first (model, &iter);
+    GtkTreeIter iter;
+    auto valid = gtk_tree_model_get_iter_first (model, &iter);
 
-    // Walk through the store looking for Null accounts
+    // Walk through the store looking for nullptr accounts
+    auto ret = true;
     while (valid)
     {
         Account *account;
@@ -1614,7 +1555,7 @@ import_account_check_all (GtkTreeModel *model)
         // Walk through the list, reading each row
         gtk_tree_model_get (model, &iter, MAPPING_ACCOUNT, &account, -1);
 
-        if (account == NULL)
+        if (account == nullptr)
             ret = false;
 
         valid = gtk_tree_model_iter_next (model, &iter);
@@ -1633,52 +1574,43 @@ import_account_check_all (GtkTreeModel *model)
 static gchar *
 import_account_text_parse (gchar *text)
 {
-    QofBook     *book;
-    const gchar *sep;
-    const gchar *newsep;
-    gchar      **names;
-    gchar      **ptr = NULL;
-    gint         i, count = 0;
-    gchar       *ret_string, *last_part = NULL;;
-
-    // Get the book
-    book = gnc_get_current_book ();
-    sep = gnc_get_account_separator_string ();
-
-    // Setup an alternative separator
-    if (g_strcmp0 (sep,":") == 0)
-        newsep = "-";
-    else
-        newsep = ":";
-
     // Split the incoming string by current separator
-    names = g_strsplit (text, sep, -1);
+    auto sep = gnc_get_account_separator_string ();
+    auto names = g_strsplit (text, sep, -1);
 
     /* find the last_part and count parts */
-    for (ptr = names; *ptr; ptr++)
+    int count = 0;
+    gchar *last_part = nullptr;
+    for (auto ptr = names; *ptr; ptr++)
     {
         if (g_strcmp0 (*ptr,"") != 0) //separator is last in string
         {
-            g_free (last_part);
+            if (last_part)
+                g_free (last_part);
             last_part = g_strdup (*ptr);
             count = count + 1;
         }
     }
 
     // If count is 1 we have no path
+    gchar *ret_string = nullptr;
     if (count == 1)
         ret_string = g_strdup (last_part);
     else
     {
-        Account   *account;
-        gchar     *sep_path, *newsep_path;
-
         // Start to create two paths based on current and possibly new separator
-        sep_path = g_strdup (names[0]);
-        newsep_path = g_strdup (names[0]);
+        auto sep_path = g_strdup (names[0]);
+        auto newsep_path = g_strdup (names[0]);
+
+        // Setup an alternative separator
+        const gchar *alt_sep;
+        if (g_strcmp0 (sep,":") == 0)
+            alt_sep = "-";
+        else
+            alt_sep = ":";
 
         // Join the parts together apart from last_part which could be account name
-        for (i = 1; i < count - 1; i++)
+        for (int i = 1; i < count - 1; i++)
         {
             gchar *temp_sep_path, *temp_newsep_path;
 
@@ -1686,17 +1618,18 @@ import_account_text_parse (gchar *text)
             temp_newsep_path = g_strdup (newsep_path);
             g_free (sep_path);
             g_free (newsep_path);
-            sep_path = g_strconcat (temp_sep_path, sep, names[i], NULL);
-            newsep_path = g_strconcat (temp_newsep_path, newsep, names[i], NULL);
+            sep_path = g_strconcat (temp_sep_path, sep, names[i], nullptr);
+            newsep_path = g_strconcat (temp_newsep_path, alt_sep, names[i], nullptr);
             g_free (temp_sep_path);
             g_free (temp_newsep_path);
         }
-        account = gnc_account_lookup_by_full_name (gnc_book_get_root_account (book), sep_path);
+        auto book = gnc_get_current_book ();
+        auto account = gnc_account_lookup_by_full_name (gnc_book_get_root_account (book), sep_path);
 
-        if (account == NULL) // path not found
-            ret_string = g_strconcat (newsep_path, newsep, last_part, NULL);
+        if (!account) // path not found
+            ret_string = g_strconcat (newsep_path, alt_sep, last_part, nullptr);
         else
-            ret_string = g_strconcat (sep_path, sep, last_part, NULL);
+            ret_string = g_strconcat (sep_path, sep, last_part, nullptr);
 
         g_free (sep_path);
         g_free (newsep_path);
@@ -1706,121 +1639,76 @@ import_account_text_parse (gchar *text)
     return ret_string;
 }
 
-
-static void
-import_account_select_cb (GtkWidget *widget, gpointer user_data)
+static void import_account_select_internal(CsvImportTrans* info, GtkTreeModel *model, GtkTreeIter& iter)
 {
-    CsvImportTrans *info = (CsvImportTrans*) user_data;
-    GtkTreeSelection *selection;
-    GtkTreeModel *model;
-    GtkTreeIter iter;
+    // Get the the String
+    gchar *text = nullptr;
+    gtk_tree_model_get (model, &iter, MAPPING_STRING, &text, -1);
 
-    model = gtk_tree_view_get_model (GTK_TREE_VIEW(info->account_match_view));
+    // Get the pointer to the Account
+    Account *account = nullptr;
+    gtk_tree_model_get (model, &iter, MAPPING_ACCOUNT, &account, -1);
 
-    selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(info->account_match_view));
+    auto parsed_text = import_account_text_parse (text);
+    auto gnc_acc = gnc_import_select_account (nullptr, nullptr, 1,
+            parsed_text, nullptr, ACCT_TYPE_NONE, account, nullptr);
 
-    if (gtk_tree_selection_get_selected (selection, &model, &iter))
+    if (gnc_acc) // We may have canceled
     {
-        Account *gnc_acc = NULL, *account = NULL;
-        gchar *text = NULL;
-        gchar *parsed_text = NULL;
-
-        // Get the the String
-        gtk_tree_model_get (model, &iter, MAPPING_STRING, &text, -1);
-
-        // Get the pointer to the Account
-        gtk_tree_model_get (model, &iter, MAPPING_ACCOUNT, &account, -1);
+        auto fullpath = gnc_account_get_full_name (gnc_acc);
+        gtk_list_store_set (GTK_LIST_STORE(model), &iter,
+                MAPPING_ACCOUNT, gnc_acc,
+                MAPPING_FULLPATH, fullpath, -1);
 
-        parsed_text = import_account_text_parse (text);
+        // Update the account kvp mappings
+        gnc_csv_account_map_change_mappings (account, gnc_acc, text);
 
-        gnc_acc = gnc_import_select_account (NULL, NULL, 1, parsed_text, NULL, ACCT_TYPE_NONE, account, NULL);
-
-        if (gnc_acc != NULL) // We may of canceled
-        {
-            gchar *fullpath = NULL;
+        g_free (fullpath);
+    }
+    g_free (text);
+    g_free (parsed_text);
 
-            gtk_list_store_set (GTK_LIST_STORE(model), &iter, MAPPING_ACCOUNT, gnc_acc, -1);
+    if (import_account_check_all (model))
+        gtk_assistant_set_page_complete (info->csv_imp_asst, info->account_match_page, true);
 
-            fullpath = gnc_account_get_full_name (gnc_acc);
-            gtk_list_store_set (GTK_LIST_STORE(model), &iter, MAPPING_FULLPATH, fullpath, -1);
+}
 
-            // Update the account kvp mappings
-            gnc_csv_account_map_change_mappings (account, gnc_acc, text);
+void
+csv_tximp_acct_match_button_clicked_cb (GtkWidget *widget, CsvImportTrans* info)
+{
+    auto model = gtk_tree_view_get_model (GTK_TREE_VIEW(info->account_match_view));
+    auto selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(info->account_match_view));
 
-            g_free (fullpath);
-        }
-        g_free (text);
-        g_free (parsed_text);
-    }
-    if (import_account_check_all (model))
-        gtk_assistant_set_page_complete (GTK_ASSISTANT(info->window), info->account_match_page, TRUE);
+    GtkTreeIter iter;
+    if (gtk_tree_selection_get_selected (selection, &model, &iter))
+        import_account_select_internal (info, model, iter);
 }
 
 
 /* This is the callback for the mouse click */
-static bool
-import_account_button_cb (GtkWidget *widget, GdkEventButton *event, gpointer user_data)
+bool
+csv_tximp_acct_match_view_clicked_cb (GtkWidget *widget, GdkEventButton *event, CsvImportTrans* info)
 {
-    CsvImportTrans     *info = (CsvImportTrans*) user_data;
-    GtkTreeModel       *model;
-    GtkTreeIter         iter;
-    GtkTreePath        *path;
-    GtkTreeViewColumn  *col;
-
-    model = gtk_tree_view_get_model (GTK_TREE_VIEW(info->account_match_view));
-
     /* This is for a double click */
     if (event->button == 1 && event->type == GDK_2BUTTON_PRESS)
     {
-        GdkWindow *window = gtk_tree_view_get_bin_window (GTK_TREE_VIEW (info->account_match_view));
-
+        auto window = gtk_tree_view_get_bin_window (GTK_TREE_VIEW (info->account_match_view));
         if (event->window != window)
             return false;
 
         /* Get tree path for row that was clicked, true if row exists */
+        GtkTreePath *path;
         if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (info->account_match_view), (gint) event->x, (gint) event->y,
-                                             &path, &col, NULL, NULL))
+                                             &path, nullptr, nullptr, nullptr))
         {
             DEBUG("event->x is %d and event->y is %d", (gint)event->x, (gint)event->y);
 
-            if (gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path))
-            {
-                Account *gnc_acc = NULL, *account = NULL;
-                gchar *text = NULL;
-                gchar *parsed_text = NULL;
-
-                // Get the the String
-                gtk_tree_model_get (model, &iter, MAPPING_STRING, &text, -1);
-
-                // Get the pointer to the Account
-                gtk_tree_model_get (model, &iter, MAPPING_ACCOUNT, &account, -1);
-
-                parsed_text = import_account_text_parse (text);
-
-                gnc_acc = gnc_import_select_account (NULL, NULL, 1, parsed_text, NULL, ACCT_TYPE_NONE, account, NULL);
-
-                if (gnc_acc != NULL) // We may of canceled
-                {
-                    gchar *fullpath = NULL;
-
-                    gtk_list_store_set (GTK_LIST_STORE(model), &iter, MAPPING_ACCOUNT, gnc_acc, -1);
-
-                    fullpath = gnc_account_get_full_name (gnc_acc);
-                    gtk_list_store_set (GTK_LIST_STORE(model), &iter, MAPPING_FULLPATH, fullpath, -1);
-
-                    // Update the account kvp mappings
-                    gnc_csv_account_map_change_mappings (account, gnc_acc, text);
-
-                    g_free (fullpath);
-                }
-                g_free (text);
-                g_free (parsed_text);
-            }
+            auto model = gtk_tree_view_get_model (GTK_TREE_VIEW(info->account_match_view));
+            GtkTreeIter iter;
+            if (gtk_tree_model_get_iter (model, &iter, path))
+                import_account_select_internal (info, model, iter);
             gtk_tree_path_free (path);
         }
-        if (import_account_check_all (model))
-            gtk_assistant_set_page_complete (GTK_ASSISTANT(info->window), info->account_match_page, TRUE);
-
         return true;
     }
     return false;
@@ -1828,52 +1716,42 @@ import_account_button_cb (GtkWidget *widget, GdkEventButton *event, gpointer use
 
 
 void
-csv_import_trans_assistant_account_match_page_prepare (GtkAssistant *assistant,
-        gpointer user_data)
+csv_tximp_assist_account_match_page_prepare (GtkAssistant *assistant,
+        CsvImportTrans* info)
 {
-    CsvImportTrans *info = (CsvImportTrans*) user_data;
-    gint            num = gtk_assistant_get_current_page (assistant);
-    gchar          *text, *mtext;
-
-    auto store = gtk_tree_view_get_model (GTK_TREE_VIEW(info->account_match_view));
-
     // Load the account strings into the store
+    auto store = gtk_tree_view_get_model (GTK_TREE_VIEW(info->account_match_view));
     get_list_of_accounts (info, store);
 
     // Match the account strings to the mappings
     gnc_csv_account_map_load_mappings (store);
 
-    text = g_strdup_printf (gettext ("To change mapping, Double Click on a row or select a row and press the button..."));
-    mtext = g_strdup_printf ("<span size=\"medium\" color=\"red\"><b>%s</b></span>", text);
-    gtk_label_set_markup (GTK_LABEL(info->account_match_label), mtext);
-    g_free (mtext);
-    g_free (text);
+    auto text = std::string ("<span size=\"medium\" color=\"red\"><b>");
+    text += _("To change mapping, double click on a row or select a row and press the button...");
+    text += "</b></span>";
+    gtk_label_set_markup (GTK_LABEL(info->account_match_label), text.c_str());
 
     // Enable the view, possibly after an error
-    gtk_widget_set_sensitive (info->account_match_view, TRUE);
-    gtk_widget_set_sensitive (info->account_match_btn, TRUE);
+    gtk_widget_set_sensitive (info->account_match_view, true);
+    gtk_widget_set_sensitive (info->account_match_btn, true);
 
     /* Enable the Forward Assistant Button */
-    if (import_account_check_all (store))
-       gtk_assistant_set_page_complete (assistant, info->account_match_page, TRUE);
-    else
-        gtk_assistant_set_page_complete (assistant, info->account_match_page, FALSE);
+       gtk_assistant_set_page_complete (assistant, info->account_match_page,
+               import_account_check_all (store));
 }
 
 
 void
-csv_import_trans_assistant_doc_page_prepare (GtkAssistant *assistant,
-        gpointer user_data)
+csv_tximp_assist_doc_page_prepare (GtkAssistant *assistant,
+        CsvImportTrans* info)
 {
-    CsvImportTrans *info = (CsvImportTrans*) user_data;
-
     /* Block going back */
-    gtk_assistant_commit (GTK_ASSISTANT(info->window));
+    gtk_assistant_commit (info->csv_imp_asst);
 
     /* Before creating transactions, if this is a new book, let user specify
      * book options, since they affect how transactions are created */
     if (info->new_book)
-        info->new_book = gnc_new_book_option_display (info->window);
+        info->new_book = gnc_new_book_option_display (GTK_WIDGET(info->csv_imp_asst));
 
     if (!info->match_parse_run)
     {
@@ -1881,7 +1759,7 @@ csv_import_trans_assistant_doc_page_prepare (GtkAssistant *assistant,
         info->cancel_button = gtk_button_new_with_mnemonic (_("_Cancel"));
         gtk_assistant_add_action_widget (assistant, info->cancel_button);
         g_signal_connect (info->cancel_button, "clicked",
-                         G_CALLBACK(csv_import_trans_assistant_cancel), info);
+                         G_CALLBACK(csv_tximp_assist_cancel_cb), info);
         gtk_widget_show (GTK_WIDGET(info->cancel_button));
     }
     info->match_parse_run = true;
@@ -1889,28 +1767,23 @@ csv_import_trans_assistant_doc_page_prepare (GtkAssistant *assistant,
 
 
 void
-csv_import_trans_assistant_match_page_prepare (GtkAssistant *assistant,
-        gpointer user_data)
+csv_tximp_assist_match_page_prepare (GtkAssistant *assistant,
+        CsvImportTrans* info)
 {
-    CsvImportTrans *info = (CsvImportTrans*) user_data;
-    gint num = gtk_assistant_get_current_page (assistant);
-    GtkWidget *page = gtk_assistant_get_nth_page (assistant, num);
-    gchar *text, *mtext;
-
     /* Block going back */
-    gtk_assistant_commit (GTK_ASSISTANT(info->window));
+    gtk_assistant_commit (info->csv_imp_asst);
 
-    if (!info->parse_data->parse_errors || info->skip_errors)
+    if (!info->tx_imp->m_parse_errors || info->skip_errors)
     {
-        text =  _("Double click on rows to change, then click on Apply to Import");
-        mtext = g_strdup_printf ("<span size=\"medium\" color=\"red\"><b>%s</b></span>", text);
-        gtk_label_set_markup (GTK_LABEL(info->match_label), mtext);
-        g_free (mtext);
+        auto text = std::string( "<span size=\"medium\" color=\"red\"><b>");
+        text += _("Double click on rows to change, then click on Apply to Import");
+        text += "</b></span>";
+        gtk_label_set_markup (GTK_LABEL(info->match_label), text.c_str());
 
-        if (info->gnc_csv_importer_gui == NULL)
+        if (!info->gnc_csv_importer_gui)
         {
             /* Create the generic transaction importer GUI. */
-            info->gnc_csv_importer_gui = gnc_gen_trans_assist_new (info->match_page, NULL, FALSE, 42);
+            info->gnc_csv_importer_gui = gnc_gen_trans_assist_new (info->match_page, nullptr, false, 42);
 
             /* Add the help button for the matcher */
             info->help_button = gtk_button_new_with_mnemonic (_("_Help"));
@@ -1920,7 +1793,7 @@ csv_import_trans_assistant_match_page_prepare (GtkAssistant *assistant,
             gtk_widget_show (GTK_WIDGET(info->help_button));
 
             /* Copy all of the transactions to the importer GUI. */
-            for (auto trans_it : info->parse_data->transactions)
+            for (auto trans_it : info->tx_imp->m_transactions)
             {
                 auto draft_trans = trans_it.second;
                 if (draft_trans->trans)
@@ -1932,41 +1805,36 @@ csv_import_trans_assistant_match_page_prepare (GtkAssistant *assistant,
         }
     }
     /* Enable the Forward Assistant Button */
-    gtk_assistant_set_page_complete (assistant, page, TRUE);
+    gtk_assistant_set_page_complete (assistant, info->match_page, true);
 }
 
 
 void
-csv_import_trans_assistant_summary_page_prepare (GtkAssistant *assistant,
-        gpointer user_data)
+csv_tximp_assist_summary_page_prepare (GtkAssistant *assistant,
+        CsvImportTrans* info)
 {
-    CsvImportTrans *info = (CsvImportTrans*) user_data;
-    gchar *text, *mtext;
-
-    /* Remove the added button */
+    /* Remove the added buttons */
     gtk_assistant_remove_action_widget (assistant, info->help_button);
     gtk_assistant_remove_action_widget (assistant, info->cancel_button);
 
-    text = g_strdup_printf (gettext ("The transactions were imported from the file '%s'."), info->file_name.c_str());
-    mtext = g_strdup_printf ("<span size=\"medium\"><b>%s</b></span>", text);
-    gtk_label_set_markup (GTK_LABEL(info->summary_label), mtext);
-    g_free (text);
-    g_free (mtext);
+    auto text = std::string("<span size=\"medium\"><b>");
+    text += _("The transactions were imported from the file '") + info->file_name + "'.";
+    text += "</b></span>";
+    gtk_label_set_markup (GTK_LABEL(info->summary_label), text.c_str());
 }
 
 
 static gint
-csv_import_trans_forward_page_func (gint current_page, gpointer user_data)
+csv_tximp_forward_page_func (gint current_page, gpointer user_data)
 {
-    CsvImportTrans *info = (CsvImportTrans*) user_data;
-    gint next_page = 0;
+    auto info = (CsvImportTrans*)user_data;
 
     /* Note: This function gets called multiple times by the GtkAssistant code and so
        as we need to run tests only once I have used a counter that gets incremented on
        every call but the tests only get run when the counter is at 1 */
-
     info->callcount = info->callcount + 1;
 
+    auto next_page = info->next_page;
     switch (current_page)
     {
 	case 0: //from start page
@@ -1975,8 +1843,6 @@ csv_import_trans_forward_page_func (gint current_page, gpointer user_data)
                 next_page = 1;
                 info->next_page = next_page;
             }
-            else
-                next_page = info->next_page;
             break;
 
         case 1: //from file page
@@ -1985,8 +1851,6 @@ csv_import_trans_forward_page_func (gint current_page, gpointer user_data)
                 next_page = 2;
                 info->next_page = next_page;
             }
-            else
-                next_page = info->next_page;
             break;
 
         case 2: //from preview page
@@ -1996,16 +1860,14 @@ csv_import_trans_forward_page_func (gint current_page, gpointer user_data)
                 if (info->skip_errors)
                     next_page = 4;
                 // Check to see if we have an account / other account columns
-                else if (info->parse_data->check_for_column_type (GncTransPropType::ACCOUNT) ||
-                        info->parse_data->check_for_column_type (GncTransPropType::TACCOUNT))
+                else if (info->tx_imp->check_for_column_type (GncTransPropType::ACCOUNT) ||
+                        info->tx_imp->check_for_column_type (GncTransPropType::TACCOUNT))
                     next_page = 3;
                 else
                     next_page = 4;
 
                 info->next_page = next_page;
             }
-            else
-                next_page = info->next_page;
             break;
 
         case 3: //from account match page
@@ -2014,8 +1876,6 @@ csv_import_trans_forward_page_func (gint current_page, gpointer user_data)
                 next_page = 4;
                 info->next_page = next_page;
             }
-            else
-                next_page = info->next_page;
             break;
 
         case 4: //from doc page
@@ -2023,10 +1883,10 @@ csv_import_trans_forward_page_func (gint current_page, gpointer user_data)
             {
                 /* Create transactions from the parsed data, first time with false
                    Subsequent times with true */
-                    info->parse_data->create_transactions (info->match_parse_run);
+                info->tx_imp->create_transactions (info->match_parse_run);
 
                 /* if there are errors, we jump back to preview to correct */
-                if (info->parse_data->parse_errors && !info->skip_errors)
+                if (info->tx_imp->m_parse_errors && !info->skip_errors)
                 {
                     info->previewing_errors = true; /* We're looking at errors. */
                     next_page = 2;
@@ -2036,8 +1896,6 @@ csv_import_trans_forward_page_func (gint current_page, gpointer user_data)
 
                 info->next_page = next_page;
             }
-            else
-                next_page = info->next_page;
             break;
 
         case 5: //from match page
@@ -2046,8 +1904,6 @@ csv_import_trans_forward_page_func (gint current_page, gpointer user_data)
                 next_page = 6;
                 info->next_page = next_page;
             }
-            else
-                next_page = info->next_page;
             break;
 
         case 6: //from summary page
@@ -2056,8 +1912,6 @@ csv_import_trans_forward_page_func (gint current_page, gpointer user_data)
                 next_page = 7;
                 info->next_page = next_page;
             }
-            else
-                next_page = info->next_page;
             break;
 
         default:
@@ -2068,28 +1922,26 @@ csv_import_trans_forward_page_func (gint current_page, gpointer user_data)
 
 
 void
-csv_import_trans_assistant_prepare (GtkAssistant *assistant, GtkWidget *page,
-                                    gpointer user_data)
+csv_tximp_assist_prepare_cb (GtkAssistant *assistant, GtkWidget *page,
+        CsvImportTrans* info)
 {
-    CsvImportTrans *info = (CsvImportTrans*) user_data;
-
     // Reset callcount on every prepare
     info->callcount = 0;
 
     if (page == info->start_page)
-        csv_import_trans_assistant_start_page_prepare (assistant, user_data);
+        csv_tximp_assist_start_page_prepare (assistant, info);
     else if (page == info->file_page)
-        csv_import_trans_assistant_file_page_prepare (assistant, user_data);
+        csv_tximp_assist_file_page_prepare (assistant, info);
     else if (page == info->preview_page)
-        csv_import_trans_assistant_preview_page_prepare (assistant, user_data);
+        csv_tximp_assist_preview_page_prepare (assistant, info);
     else if (page == info->account_match_page)
-        csv_import_trans_assistant_account_match_page_prepare (assistant, user_data);
+        csv_tximp_assist_account_match_page_prepare (assistant, info);
     else if (page == info->doc_page)
-        csv_import_trans_assistant_doc_page_prepare (assistant, user_data);
+        csv_tximp_assist_doc_page_prepare (assistant, info);
     else if (page == info->match_page)
-        csv_import_trans_assistant_match_page_prepare (assistant, user_data);
+        csv_tximp_assist_match_page_prepare (assistant, info);
     else if (page == info->summary_page)
-        csv_import_trans_assistant_summary_page_prepare (assistant, user_data);
+        csv_tximp_assist_summary_page_prepare (assistant, info);
     else
         g_assert_not_reached();
 }
@@ -2098,102 +1950,90 @@ csv_import_trans_assistant_prepare (GtkAssistant *assistant, GtkWidget *page,
 /*******************************************************
  * Assistant call back functions
  *******************************************************/
-static void
-csv_import_trans_assistant_destroy_cb (GtkWidget *object, gpointer user_data)
+void
+csv_tximp_assist_destroy_cb (GtkWidget *object, CsvImportTrans* info)
 {
-    CsvImportTrans *info = (CsvImportTrans*) user_data;
     gnc_unregister_gui_component_by_data (ASSISTANT_CSV_IMPORT_TRANS_CM_CLASS, info);
     g_free (info);
 }
 
 void
-csv_import_trans_assistant_cancel (GtkAssistant *assistant, gpointer user_data)
+csv_tximp_assist_cancel_cb (GtkAssistant *assistant, CsvImportTrans* info)
 {
-    CsvImportTrans *info = (CsvImportTrans*) user_data;
-
-    if (!(info->gnc_csv_importer_gui == NULL))
+    if (info->gnc_csv_importer_gui)
         gnc_gen_trans_list_delete (info->gnc_csv_importer_gui);
 
     gnc_close_gui_component_by_data (ASSISTANT_CSV_IMPORT_TRANS_CM_CLASS, info);
 }
 
 void
-csv_import_trans_assistant_close (GtkAssistant *assistant, gpointer user_data)
+csv_tximp_assist_close_cb (GtkAssistant *assistant, CsvImportTrans* info)
 {
-    CsvImportTrans *info = (CsvImportTrans*) user_data;
     gnc_close_gui_component_by_data (ASSISTANT_CSV_IMPORT_TRANS_CM_CLASS, info);
 }
 
 void
-csv_import_trans_assistant_finish (GtkAssistant *assistant, gpointer user_data)
+csv_tximp_assist_finish_cb (GtkAssistant *assistant, CsvImportTrans* info)
 {
-    CsvImportTrans *info = (CsvImportTrans*) user_data;
-
     /* Start the import */
-    if (!info->parse_data->transactions.empty())
+    if (!info->tx_imp->m_transactions.empty())
         gnc_gen_trans_assist_start (info->gnc_csv_importer_gui);
     else
         gnc_gen_trans_list_delete (info->gnc_csv_importer_gui);
 }
 
 static void
-csv_import_trans_close_handler (gpointer user_data)
+csv_tximp_close_handler (gpointer user_data)
 {
-    CsvImportTrans *info = (CsvImportTrans*) user_data;
+    auto info = (CsvImportTrans*)user_data;
 
     /* Free the memory we allocated. */
-    if (!(info->parse_data == NULL))
-        delete info->parse_data;
+    if (info->tx_imp)
+        delete info->tx_imp;
 
-    if (!(info->gnc_csv_importer_gui == NULL))
-        info->gnc_csv_importer_gui = NULL;
+    if (info->gnc_csv_importer_gui)
+        info->gnc_csv_importer_gui = nullptr;
 
-    gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(info->window));
-    gtk_widget_destroy (info->window);
+    gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(info->csv_imp_asst));
+    gtk_widget_destroy (GTK_WIDGET(info->csv_imp_asst));
 }
 
 /*******************************************************
  * Create the Assistant
  *******************************************************/
-static GtkWidget *
-csv_import_trans_assistant_create (CsvImportTrans *info)
+static void
+csv_tximp_assist_create (CsvImportTrans *info)
 {
-    GtkBuilder *builder;
-    GtkWidget *window;
-    GtkWidget *box;
-    GtkWidget *button, *h_box;
-
-    builder = gtk_builder_new();
+    auto builder = gtk_builder_new();
     gnc_builder_add_from_file  (builder , "assistant-csv-trans-import.glade", "start_row_adj");
     gnc_builder_add_from_file  (builder , "assistant-csv-trans-import.glade", "end_row_adj");
     gnc_builder_add_from_file  (builder , "assistant-csv-trans-import.glade", "account_match_store");
     gnc_builder_add_from_file  (builder , "assistant-csv-trans-import.glade", "CSV Transaction Assistant");
-    window = GTK_WIDGET(gtk_builder_get_object (builder, "CSV Transaction Assistant"));
-    info->window = window;
+    info->csv_imp_asst = GTK_ASSISTANT(gtk_builder_get_object (builder, "CSV Transaction Assistant"));
 
     /* Set the forward function */
-    gtk_assistant_set_forward_page_func (GTK_ASSISTANT(window), csv_import_trans_forward_page_func, info, NULL);
+    gtk_assistant_set_forward_page_func (info->csv_imp_asst, csv_tximp_forward_page_func, info, nullptr);
 
     /* Enable buttons on all page. */
-    gtk_assistant_set_page_complete (GTK_ASSISTANT(window),
+    gtk_assistant_set_page_complete (info->csv_imp_asst,
                                      GTK_WIDGET(gtk_builder_get_object (builder, "start_page")),
                                      TRUE);
-    gtk_assistant_set_page_complete (GTK_ASSISTANT(window),
+    gtk_assistant_set_page_complete (info->csv_imp_asst,
                                      GTK_WIDGET(gtk_builder_get_object (builder, "file_page")),
                                      FALSE);
-    gtk_assistant_set_page_complete (GTK_ASSISTANT(window),
+    gtk_assistant_set_page_complete (info->csv_imp_asst,
                                      GTK_WIDGET(gtk_builder_get_object (builder, "preview_page")),
                                      FALSE);
-    gtk_assistant_set_page_complete (GTK_ASSISTANT(window),
+    gtk_assistant_set_page_complete (info->csv_imp_asst,
                                      GTK_WIDGET(gtk_builder_get_object (builder, "account_match_page")),
                                      FALSE);
-    gtk_assistant_set_page_complete (GTK_ASSISTANT(window),
+    gtk_assistant_set_page_complete (info->csv_imp_asst,
                                      GTK_WIDGET(gtk_builder_get_object (builder, "doc_page")),
                                      TRUE);
-    gtk_assistant_set_page_complete (GTK_ASSISTANT(window),
+    gtk_assistant_set_page_complete (info->csv_imp_asst,
                                      GTK_WIDGET(gtk_builder_get_object (builder, "match_page")),
                                      FALSE);
-    gtk_assistant_set_page_complete (GTK_ASSISTANT(window),
+    gtk_assistant_set_page_complete (info->csv_imp_asst,
                                      GTK_WIDGET(gtk_builder_get_object (builder, "summary_page")),
                                      TRUE);
 
@@ -2204,17 +2044,17 @@ csv_import_trans_assistant_create (CsvImportTrans *info)
     info->file_page = GTK_WIDGET(gtk_builder_get_object (builder, "file_page"));
     info->file_chooser = gtk_file_chooser_widget_new (GTK_FILE_CHOOSER_ACTION_OPEN);
     g_signal_connect (G_OBJECT(info->file_chooser), "file-activated",
-                      G_CALLBACK(csv_import_trans_file_chooser_confirm_cb), info);
-    button = gtk_button_new_from_stock (GTK_STOCK_OK);
+                      G_CALLBACK(csv_tximp_file_confirm_cb), info);
+    auto button = gtk_button_new_from_stock (GTK_STOCK_OK);
     gtk_widget_set_size_request (button, 100, -1);
     gtk_widget_show (button);
-    h_box = gtk_hbox_new (TRUE, 0);
+    auto h_box = gtk_hbox_new (TRUE, 0);
     gtk_box_pack_start (GTK_BOX(h_box), button, FALSE, FALSE, 0);
     gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER(info->file_chooser), h_box);
     g_signal_connect (G_OBJECT(button), "clicked",
-                      G_CALLBACK(csv_import_trans_file_chooser_confirm_cb), info);
+                      G_CALLBACK(csv_tximp_file_confirm_cb), info);
 
-    box = GTK_WIDGET(gtk_builder_get_object (builder, "file_page"));
+    auto box = GTK_WIDGET(gtk_builder_get_object (builder, "file_page"));
     gtk_box_pack_start (GTK_BOX(box), info->file_chooser, TRUE, TRUE, 6);
     gtk_widget_show (info->file_chooser);
 
@@ -2224,21 +2064,21 @@ csv_import_trans_assistant_create (CsvImportTrans *info)
 
         // Add Settings combo
         auto settings_store = gtk_list_store_new (2, G_TYPE_POINTER, G_TYPE_STRING);
-        info->settings_combo = gtk_combo_box_new_with_model_and_entry (GTK_TREE_MODEL(settings_store));
+        info->settings_combo = GTK_COMBO_BOX(gtk_combo_box_new_with_model_and_entry (GTK_TREE_MODEL(settings_store)));
         gtk_combo_box_set_entry_text_column (GTK_COMBO_BOX(info->settings_combo), SET_NAME);
         gtk_combo_box_set_active (GTK_COMBO_BOX(info->settings_combo), 0);
 
         info->combo_hbox = GTK_WIDGET(gtk_builder_get_object (builder, "combo_hbox"));
-        gtk_box_pack_start (GTK_BOX(info->combo_hbox), info->settings_combo, true, true, 6);
-        gtk_widget_show (info->settings_combo);
+        gtk_box_pack_start (GTK_BOX(info->combo_hbox), GTK_WIDGET(info->settings_combo), true, true, 6);
+        gtk_widget_show (GTK_WIDGET(info->settings_combo));
 
         g_signal_connect (G_OBJECT(info->settings_combo), "changed",
-                         G_CALLBACK(csv_import_trans_changed_settings_cb), (gpointer)info);
+                         G_CALLBACK(csv_tximp_preview_settings_sel_changed_cb), (gpointer)info);
 
         // Additionally connect to the changed signal of the embedded GtkEntry
         auto emb_entry = gtk_bin_get_child (GTK_BIN (info->settings_combo));
         g_signal_connect (G_OBJECT(emb_entry), "changed",
-                         G_CALLBACK(csv_import_trans_changed_settings_text_cb), (gpointer)info);
+                         G_CALLBACK(csv_tximp_preview_settings_text_changed_cb), (gpointer)info);
 
         // Add Save Settings button
         info->save_button = GTK_WIDGET(gtk_builder_get_object (builder, "save_settings"));
@@ -2246,15 +2086,11 @@ csv_import_trans_assistant_create (CsvImportTrans *info)
         // Add Delete Settings button
         info->del_button = GTK_WIDGET(gtk_builder_get_object (builder, "delete_settings"));
 
-
-        g_signal_connect (G_OBJECT(info->del_button), "clicked",
-                         G_CALLBACK(csv_import_trans_delete_settings_cb), (gpointer)info);
-
         /* The table containing the separator configuration widgets */
-        info->start_row_spin = GTK_WIDGET(gtk_builder_get_object (builder, "start_row"));
-        info->end_row_spin = GTK_WIDGET(gtk_builder_get_object (builder, "end_row"));
-        info->skip_rows = GTK_WIDGET(gtk_builder_get_object (builder, "skip_rows"));
-        info->check_butt = GTK_WIDGET(gtk_builder_get_object (builder, "check_butt"));
+        info->start_row_spin = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "start_row"));
+        info->end_row_spin = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "end_row"));
+        info->skip_alt_rows_button = GTK_WIDGET(gtk_builder_get_object (builder, "skip_rows"));
+        info->skip_errors_button = GTK_WIDGET(gtk_builder_get_object (builder, "skip_errors_button"));
         info->multi_split_cbutton = GTK_WIDGET(gtk_builder_get_object (builder, "multi_split_button"));
 
         /* Load the separator buttons from the glade builder file into the
@@ -2268,26 +2104,17 @@ csv_import_trans_assistant_create (CsvImportTrans *info)
                 "hyphen_cbutton"
             };
         for (int i = 0; i < SEP_NUM_OF_TYPES; i++)
-        {
-            info->sep_buttons[i]
+            info->sep_button[i]
                 = (GtkCheckButton*)GTK_WIDGET(gtk_builder_get_object (builder, sep_button_names[i]));
-            /* Connect them to the sep_button_clicked event handler. */
-            g_signal_connect (G_OBJECT(info->sep_buttons[i]), "toggled",
-                             G_CALLBACK(sep_button_clicked), (gpointer)info);
-        }
 
         /* Load and connect the custom separator checkbutton in the same way
          * as the other separator buttons. */
         info->custom_cbutton
             = (GtkCheckButton*)GTK_WIDGET(gtk_builder_get_object (builder, "custom_cbutton"));
-        g_signal_connect (G_OBJECT(info->custom_cbutton), "clicked",
-                         G_CALLBACK(sep_button_clicked), (gpointer)info);
 
         /* Load the entry for the custom separator entry. Connect it to the
          * sep_button_clicked event handler as well. */
         info->custom_entry = (GtkEntry*)GTK_WIDGET(gtk_builder_get_object (builder, "custom_entry"));
-        g_signal_connect (G_OBJECT(info->custom_entry), "changed",
-                         G_CALLBACK(sep_button_clicked), (gpointer)info);
 
         /* Add account selection widget */
         info->acct_selector = gnc_account_sel_new();
@@ -2295,16 +2122,15 @@ csv_import_trans_assistant_create (CsvImportTrans *info)
         gtk_box_pack_start (GTK_BOX(account_hbox), info->acct_selector, TRUE, TRUE, 6);
         gtk_widget_show (info->acct_selector);
 
-
         g_signal_connect(G_OBJECT(info->acct_selector), "account_sel_changed",
-                         G_CALLBACK(account_selected_cb), (gpointer)info);
+                         G_CALLBACK(csv_tximp_preview_acct_sel_cb), (gpointer)info);
 
 
         /* Create the encoding selector widget and add it to the assistant */
         info->encselector = GO_CHARMAP_SEL(go_charmap_sel_new(GO_CHARMAP_SEL_TO_UTF8));
         /* Connect the selector to the encoding_selected event handler. */
         g_signal_connect (G_OBJECT(info->encselector), "charmap_changed",
-                         G_CALLBACK(encoding_selected), (gpointer)info);
+                         G_CALLBACK(csv_tximp_preview_enc_sel_cb), (gpointer)info);
 
         auto encoding_container = GTK_CONTAINER(gtk_builder_get_object (builder, "encoding_container"));
         gtk_container_add (encoding_container, GTK_WIDGET(info->encselector));
@@ -2322,7 +2148,7 @@ csv_import_trans_assistant_create (CsvImportTrans *info)
         }
         gtk_combo_box_set_active (GTK_COMBO_BOX(info->date_format_combo), 0);
         g_signal_connect (G_OBJECT(info->date_format_combo), "changed",
-                         G_CALLBACK(date_format_selected), (gpointer)info);
+                         G_CALLBACK(csv_tximp_preview_date_fmt_sel_cb), (gpointer)info);
 
         /* Add it to the assistant. */
         auto date_format_container = GTK_CONTAINER(gtk_builder_get_object (builder, "date_format_container"));
@@ -2338,7 +2164,7 @@ csv_import_trans_assistant_create (CsvImportTrans *info)
         /* Default will the locale */
         gtk_combo_box_set_active (GTK_COMBO_BOX(info->currency_format_combo), 0);
         g_signal_connect (G_OBJECT(info->currency_format_combo), "changed",
-                         G_CALLBACK(currency_format_selected), (gpointer)info);
+                         G_CALLBACK(csv_tximp_preview_currency_fmt_sel_cb), (gpointer)info);
 
         /* Add it to the assistant. */
         auto currency_format_container = GTK_CONTAINER(gtk_builder_get_object (builder, "currency_format_container"));
@@ -2348,8 +2174,6 @@ csv_import_trans_assistant_create (CsvImportTrans *info)
         /* Connect the CSV/Fixed-Width radio button event handler. */
         info->csv_button = GTK_WIDGET(gtk_builder_get_object (builder, "csv_button"));
         info->fixed_button = GTK_WIDGET(gtk_builder_get_object (builder, "fixed_button"));
-        g_signal_connect (info->csv_button, "toggled",
-                         G_CALLBACK(separated_or_fixed_selected), (gpointer)info);
 
         /* Load the data treeview and connect it to its resizing event handler. */
         info->treeview = (GtkTreeView*)GTK_WIDGET(gtk_builder_get_object (builder, "treeview"));
@@ -2367,9 +2191,6 @@ csv_import_trans_assistant_create (CsvImportTrans *info)
     info->account_match_view  = GTK_WIDGET(gtk_builder_get_object (builder, "account_match_view"));
     info->account_match_label = GTK_WIDGET(gtk_builder_get_object (builder, "account_match_label"));
     info->account_match_btn = GTK_WIDGET(gtk_builder_get_object (builder, "account_match_change"));
-    // This is for double click mouse and buttons...
-    g_signal_connect (G_OBJECT(info->account_match_view), "button_press_event", G_CALLBACK(import_account_button_cb), info);
-    g_signal_connect (G_OBJECT(info->account_match_btn), "clicked", G_CALLBACK(import_account_select_cb), info);
 
     /* Doc Page */
     info->doc_page = GTK_WIDGET(gtk_builder_get_object (builder, "doc_page"));
@@ -2382,14 +2203,10 @@ csv_import_trans_assistant_create (CsvImportTrans *info)
     info->summary_page  = GTK_WIDGET(gtk_builder_get_object (builder, "summary_page"));
     info->summary_label = GTK_WIDGET(gtk_builder_get_object (builder, "summary_label"));
 
-    g_signal_connect (G_OBJECT(window), "destroy",
-                      G_CALLBACK (csv_import_trans_assistant_destroy_cb), info);
-
-    gnc_restore_window_size (GNC_PREFS_GROUP, GTK_WINDOW(info->window));
+    gnc_restore_window_size (GNC_PREFS_GROUP, GTK_WINDOW(info->csv_imp_asst));
 
     gtk_builder_connect_signals (builder, info);
     g_object_unref (G_OBJECT(builder));
-    return window;
 }
 
 
@@ -2411,13 +2228,13 @@ gnc_file_csv_trans_import(void)
      * we need to detect when we are dealing with a new book. */
     info->new_book = gnc_is_new_book();
 
-    csv_import_trans_assistant_create (info);
+    csv_tximp_assist_create (info);
 
     gnc_register_gui_component (ASSISTANT_CSV_IMPORT_TRANS_CM_CLASS,
-                                NULL, csv_import_trans_close_handler,
+                                nullptr, csv_tximp_close_handler,
                                 info);
 
-    gtk_widget_show_all (info->window);
+    gtk_widget_show_all (GTK_WIDGET(info->csv_imp_asst));
 
-    gnc_window_adjust_for_screen (GTK_WINDOW(info->window));
+    gnc_window_adjust_for_screen (GTK_WINDOW(info->csv_imp_asst));
 }
diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.glade b/src/import-export/csv-imp/assistant-csv-trans-import.glade
index 4c20ee8..3fc098c 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.glade
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.glade
@@ -18,10 +18,11 @@
     <property name="title" translatable="yes">CSV Transaction Import</property>
     <property name="default_width">400</property>
     <property name="default_height">500</property>
-    <signal name="close" handler="csv_import_trans_assistant_close" swapped="no"/>
-    <signal name="apply" handler="csv_import_trans_assistant_finish" swapped="no"/>
-    <signal name="prepare" handler="csv_import_trans_assistant_prepare" swapped="no"/>
-    <signal name="cancel" handler="csv_import_trans_assistant_cancel" swapped="no"/>
+    <signal name="close" handler="csv_tximp_assist_close_cb" swapped="no"/>
+    <signal name="apply" handler="csv_tximp_assist_finish_cb" swapped="no"/>
+    <signal name="prepare" handler="csv_tximp_assist_prepare_cb" swapped="no"/>
+    <signal name="cancel" handler="csv_tximp_assist_cancel_cb" swapped="no"/>
+    <signal name="destroy" handler="csv_tximp_assist_destroy_cb" swapped="no"/>
     <child>
       <object class="GtkLabel" id="start_page">
         <property name="visible">True</property>
@@ -116,7 +117,7 @@ Select location and file name for the Import, then click 'OK'...
                             <property name="can_focus">True</property>
                             <property name="receives_default">True</property>
                             <property name="tooltip_text" translatable="yes">Delete Settings</property>
-                            <signal name="clicked" handler="csv_import_trans_delete_settings_cb" swapped="no"/>
+                            <signal name="clicked" handler="csv_tximp_preview_del_settings_cb" swapped="no"/>
                             <child>
                               <object class="GtkImage" id="image2">
                                 <property name="visible">True</property>
@@ -141,7 +142,7 @@ Select location and file name for the Import, then click 'OK'...
                             <property name="can_focus">True</property>
                             <property name="receives_default">True</property>
                             <property name="tooltip_text" translatable="yes">Save Settings</property>
-                            <signal name="clicked" handler="csv_import_trans_save_settings_cb" swapped="no"/>
+                            <signal name="clicked" handler="csv_tximp_preview_save_settings_cb" swapped="no"/>
                             <child>
                               <object class="GtkImage" id="image1">
                                 <property name="visible">True</property>
@@ -206,6 +207,7 @@ Select location and file name for the Import, then click 'OK'...
                             <property name="receives_default">False</property>
                             <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
                             <property name="draw_indicator">True</property>
+                            <signal name="toggled" handler="csv_tximp_preview_sep_button_cb" swapped="no"/>
                           </object>
                           <packing>
                             <property name="top_attach">2</property>
@@ -222,6 +224,7 @@ Select location and file name for the Import, then click 'OK'...
                             <property name="receives_default">False</property>
                             <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
                             <property name="draw_indicator">True</property>
+                            <signal name="toggled" handler="csv_tximp_preview_sep_button_cb" swapped="no"/>
                           </object>
                           <packing>
                             <property name="left_attach">1</property>
@@ -241,6 +244,7 @@ Select location and file name for the Import, then click 'OK'...
                             <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
                             <property name="active">True</property>
                             <property name="draw_indicator">True</property>
+                            <signal name="toggled" handler="csv_tximp_preview_sep_button_cb" swapped="no"/>
                           </object>
                           <packing>
                             <property name="left_attach">2</property>
@@ -259,6 +263,7 @@ Select location and file name for the Import, then click 'OK'...
                             <property name="receives_default">False</property>
                             <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
                             <property name="draw_indicator">True</property>
+                            <signal name="toggled" handler="csv_tximp_preview_sep_button_cb" swapped="no"/>
                           </object>
                           <packing>
                             <property name="top_attach">3</property>
@@ -275,6 +280,7 @@ Select location and file name for the Import, then click 'OK'...
                             <property name="receives_default">False</property>
                             <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
                             <property name="draw_indicator">True</property>
+                            <signal name="toggled" handler="csv_tximp_preview_sep_button_cb" swapped="no"/>
                           </object>
                           <packing>
                             <property name="left_attach">1</property>
@@ -293,6 +299,7 @@ Select location and file name for the Import, then click 'OK'...
                             <property name="receives_default">False</property>
                             <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
                             <property name="draw_indicator">True</property>
+                            <signal name="toggled" handler="csv_tximp_preview_sep_button_cb" swapped="no"/>
                           </object>
                           <packing>
                             <property name="left_attach">2</property>
@@ -311,6 +318,7 @@ Select location and file name for the Import, then click 'OK'...
                             <property name="receives_default">False</property>
                             <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
                             <property name="draw_indicator">True</property>
+                            <signal name="toggled" handler="csv_tximp_preview_sep_button_cb" swapped="no"/>
                           </object>
                           <packing>
                             <property name="top_attach">4</property>
@@ -330,6 +338,7 @@ Select location and file name for the Import, then click 'OK'...
                             <property name="secondary_icon_activatable">False</property>
                             <property name="primary_icon_sensitive">True</property>
                             <property name="secondary_icon_sensitive">True</property>
+                            <signal name="changed" handler="csv_tximp_preview_sep_button_cb" swapped="no"/>
                           </object>
                           <packing>
                             <property name="left_attach">1</property>
@@ -349,6 +358,7 @@ Select location and file name for the Import, then click 'OK'...
                             <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
                             <property name="active">True</property>
                             <property name="draw_indicator">True</property>
+                            <signal name="toggled" handler="csv_tximp_preview_sep_fixed_sel_cb" swapped="no"/>
                           </object>
                         </child>
                         <child>
@@ -401,7 +411,7 @@ Select location and file name for the Import, then click 'OK'...
 When Multi-split is enabled the importer will assume multiple consecutive lines together hold the information for one transaction. Each line provides information for exactly one split. The first line should also provide the information for the transaction.
 To know which lines belong to the same transaction, the importer will compare the provided transaction information in each line. If that information is empty or the same as the first transaction line the importer will consider this line part of the same transaction.</property>
                             <property name="draw_indicator">True</property>
-                            <signal name="toggled" handler="csv_import_trans_multisplit_cb" swapped="no"/>
+                            <signal name="toggled" handler="csv_tximp_preview_multisplit_cb" swapped="no"/>
                           </object>
                           <packing>
                             <property name="right_attach">3</property>
@@ -578,7 +588,7 @@ To know which lines belong to the same transaction, the importer will compare th
                                     <property name="secondary_icon_sensitive">True</property>
                                     <property name="adjustment">start_row_adj</property>
                                     <property name="numeric">True</property>
-                                    <signal name="value-changed" handler="csv_import_trans_srow_cb" swapped="no"/>
+                                    <signal name="value-changed" handler="csv_tximp_preview_srow_cb" swapped="no"/>
                                   </object>
                                   <packing>
                                     <property name="expand">False</property>
@@ -613,7 +623,7 @@ To know which lines belong to the same transaction, the importer will compare th
                                     <property name="secondary_icon_sensitive">True</property>
                                     <property name="adjustment">end_row_adj</property>
                                     <property name="numeric">True</property>
-                                    <signal name="value-changed" handler="csv_import_trans_erow_cb" swapped="no"/>
+                                    <signal name="value-changed" handler="csv_tximp_preview_erow_cb" swapped="no"/>
                                   </object>
                                   <packing>
                                     <property name="expand">False</property>
@@ -661,7 +671,7 @@ For example
 * if 'Leading Lines to Skip' is set to 3, the first line to import will be line 4. Lines 5, 7, 9,... will be skipped.
 * if 'Leading Lines to Skip' is set to 4, the first line to import will be line 5. Lines 6, 8, 10,... will be skipped.</property>
                             <property name="draw_indicator">True</property>
-                            <signal name="toggled" handler="csv_import_trans_skiprows_cb" swapped="no"/>
+                            <signal name="toggled" handler="csv_tximp_preview_skiprows_cb" swapped="no"/>
                           </object>
                           <packing>
                             <property name="expand">False</property>
@@ -839,7 +849,7 @@ For example
             <property name="visible">True</property>
             <property name="can_focus">False</property>
             <child>
-              <object class="GtkCheckButton" id="check_butt">
+              <object class="GtkCheckButton" id="skip_errors_button">
                 <property name="label" translatable="yes">Skip Errors</property>
                 <property name="can_focus">True</property>
                 <property name="receives_default">False</property>
@@ -847,7 +857,7 @@ For example
                 <property name="image_position">right</property>
                 <property name="active">True</property>
                 <property name="draw_indicator">True</property>
-                <signal name="toggled" handler="csv_import_trans_skip_errors_cb" swapped="no"/>
+                <signal name="toggled" handler="csv_tximp_preview_skiperrors_cb" swapped="no"/>
               </object>
               <packing>
                 <property name="expand">False</property>
@@ -932,6 +942,7 @@ For example
                     </child>
                   </object>
                 </child>
+                <signal name="button_press_event" handler="csv_tximp_acct_match_view_clicked_cb" swapped="no"/>
               </object>
             </child>
           </object>
@@ -965,6 +976,7 @@ For example
                 <property name="can_focus">True</property>
                 <property name="receives_default">True</property>
                 <property name="use_underline">True</property>
+                <signal name="clicked" handler="csv_tximp_acct_match_button_clicked_cb" swapped="no"/>
               </object>
               <packing>
                 <property name="expand">False</property>
diff --git a/src/import-export/csv-imp/gnc-csv-trans-settings.cpp b/src/import-export/csv-imp/gnc-csv-trans-settings.cpp
index b0cecb9..12104cf 100644
--- a/src/import-export/csv-imp/gnc-csv-trans-settings.cpp
+++ b/src/import-export/csv-imp/gnc-csv-trans-settings.cpp
@@ -69,7 +69,7 @@ preset_vec presets;
 static std::shared_ptr<CsvTransSettings> create_int_no_preset(void)
 {
     auto preset = std::make_shared<CsvTransSettings>();
-    preset->name = no_settings;
+    preset->m_name = no_settings;
 
     return preset;
 }
@@ -77,9 +77,9 @@ static std::shared_ptr<CsvTransSettings> create_int_no_preset(void)
 static std::shared_ptr<CsvTransSettings> create_int_gnc_exp_preset(void)
 {
     auto preset = std::make_shared<CsvTransSettings>();
-    preset->name = gnc_exp;
-    preset->skip_start_lines = 1;
-    preset->multi_split = true;
+    preset->m_name = gnc_exp;
+    preset->m_skip_start_lines = 1;
+    preset->m_multi_split = true;
 
     /* FIXME date and currency format should still be aligned with export format!
      * That's currently hard to do, because the export uses whatever the user
@@ -87,7 +87,7 @@ static std::shared_ptr<CsvTransSettings> create_int_gnc_exp_preset(void)
     preset->date_active = 0;
     preset->currency_active = 0;
     */
-    preset->column_types = {
+    preset->m_column_types = {
             GncTransPropType::DATE,
             GncTransPropType::UNIQUE_ID,
             GncTransPropType::NUM,
@@ -150,7 +150,7 @@ const preset_vec& get_trans_presets (void)
     for (auto preset_name : preset_names)
     {
         auto preset = std::make_shared<CsvTransSettings>();
-        preset->name = preset_name;
+        preset->m_name = preset_name;
         preset->load();
         presets.push_back(preset);
     }
@@ -199,64 +199,64 @@ handle_load_error (GError **key_error, const std::string& group)
 bool
 CsvTransSettings::load (void)
 {
-    if (trans_preset_is_reserved_name (name))
+    if (trans_preset_is_reserved_name (m_name))
         return true;
 
     GError *key_error = nullptr;
-    load_error = false;
-    auto group = csv_group_prefix + name;
+    m_load_error = false;
+    auto group = csv_group_prefix + m_name;
     auto keyfile = gnc_state_get_current ();
 
-    skip_start_lines = g_key_file_get_integer (keyfile, group.c_str(), CSV_SKIP_START, &key_error);
-    load_error |= handle_load_error (&key_error, group);
+    m_skip_start_lines = g_key_file_get_integer (keyfile, group.c_str(), CSV_SKIP_START, &key_error);
+    m_load_error |= handle_load_error (&key_error, group);
 
-    skip_end_lines = g_key_file_get_integer (keyfile, group.c_str(), CSV_SKIP_END, &key_error);
-    load_error |= handle_load_error (&key_error, group);
+    m_skip_end_lines = g_key_file_get_integer (keyfile, group.c_str(), CSV_SKIP_END, &key_error);
+    m_load_error |= handle_load_error (&key_error, group);
 
-    skip_alt_lines = g_key_file_get_boolean (keyfile, group.c_str(), CSV_SKIP_ALT, &key_error);
-    load_error |= handle_load_error (&key_error, group);
+    m_skip_alt_lines = g_key_file_get_boolean (keyfile, group.c_str(), CSV_SKIP_ALT, &key_error);
+    m_load_error |= handle_load_error (&key_error, group);
 
-    multi_split = g_key_file_get_boolean (keyfile, group.c_str(), CSV_MULTI_SPLIT, &key_error);
-    load_error |= handle_load_error (&key_error, group);
+    m_multi_split = g_key_file_get_boolean (keyfile, group.c_str(), CSV_MULTI_SPLIT, &key_error);
+    m_load_error |= handle_load_error (&key_error, group);
 
     auto csv_format = g_key_file_get_boolean (keyfile, group.c_str(), CSV_FORMAT, &key_error);
     if (key_error) csv_format = true; // default to true, but above command will return false in case of error
-    load_error |= handle_load_error (&key_error, group);
+    m_load_error |= handle_load_error (&key_error, group);
     if (csv_format)
-        file_format = GncImpFileFormat::CSV;
+        m_file_format = GncImpFileFormat::CSV;
     else
-        file_format = GncImpFileFormat::FIXED_WIDTH;
+        m_file_format = GncImpFileFormat::FIXED_WIDTH;
 
     gchar *key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_SEP, &key_error);
     if (key_char && *key_char != '\0')
-        separators = key_char;
-    load_error |= handle_load_error (&key_error, group);
+        m_separators = key_char;
+    m_load_error |= handle_load_error (&key_error, group);
     if (key_char)
         g_free (key_char);
 
-    date_format = g_key_file_get_integer (keyfile, group.c_str(), CSV_DATE, &key_error);
-    load_error |= handle_load_error (&key_error, group);
+    m_date_format = g_key_file_get_integer (keyfile, group.c_str(), CSV_DATE, &key_error);
+    m_load_error |= handle_load_error (&key_error, group);
 
-    currency_format = g_key_file_get_integer (keyfile, group.c_str(), CSV_CURRENCY, &key_error);
-    load_error |= handle_load_error (&key_error, group);
+    m_currency_format = g_key_file_get_integer (keyfile, group.c_str(), CSV_CURRENCY, &key_error);
+    m_load_error |= handle_load_error (&key_error, group);
 
     key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_ENCODING, &key_error);
     if (key_char && *key_char != '\0')
-        encoding = key_char;
+        m_encoding = key_char;
     else
         "UTF-8";
-    load_error |= handle_load_error (&key_error, group);
+    m_load_error |= handle_load_error (&key_error, group);
     if (key_char)
         g_free (key_char);
 
     key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_ACCOUNT, &key_error);
     if (key_char && *key_char != '\0')
-        base_account = gnc_account_lookup_by_full_name (gnc_get_current_root_account(), key_char);
-    load_error |= handle_load_error (&key_error, group);
+        m_base_account = gnc_account_lookup_by_full_name (gnc_get_current_root_account(), key_char);
+    m_load_error |= handle_load_error (&key_error, group);
     if (key_char)
         g_free (key_char);
 
-    column_types.clear();
+    m_column_types.clear();
     gsize list_len;
     gchar** col_types_str = g_key_file_get_string_list (keyfile, group.c_str(), CSV_COL_TYPES,
             &list_len, &key_error);
@@ -268,12 +268,12 @@ CsvTransSettings::load (void)
         {
             /* Found a valid column type. Now check whether it is allowed
              * in the selected mode (two-split vs multi-split) */
-            auto prop = sanitize_trans_prop (col_types_it->first, multi_split);
-                column_types.push_back(prop);
+            auto prop = sanitize_trans_prop (col_types_it->first, m_multi_split);
+                m_column_types.push_back(prop);
             if (prop != col_types_it->first)
                 PWARN("Found column type '%s', but this is blacklisted when multi-split mode is %s. "
                         "Inserting column type 'NONE' instead'.",
-                        col_types_it->second, multi_split ? "enabled" : "disabled");
+                        col_types_it->second, m_multi_split ? "enabled" : "disabled");
         }
         else
             PWARN("Found invalid column type '%s'. Inserting column type 'NONE' instead'.",
@@ -283,19 +283,19 @@ CsvTransSettings::load (void)
     if (col_types_str)
         g_strfreev (col_types_str);
 
-    column_widths.clear();
+    m_column_widths.clear();
     gint *col_widths_int = g_key_file_get_integer_list (keyfile, group.c_str(), CSV_COL_WIDTHS,
             &list_len, &key_error);
     for (uint i = 0; i < list_len; i++)
     {
         if (col_widths_int[i] > 0)
-            column_widths.push_back(col_widths_int[i]);
+            m_column_widths.push_back(col_widths_int[i]);
     }
-    load_error |= handle_load_error (&key_error, group);
+    m_load_error |= handle_load_error (&key_error, group);
     if (col_widths_int)
         g_free (col_widths_int);
 
-    return load_error;
+    return m_load_error;
 }
 
 
@@ -307,46 +307,46 @@ CsvTransSettings::load (void)
 bool
 CsvTransSettings::save (void)
 {
-    if (trans_preset_is_reserved_name (name))
+    if (trans_preset_is_reserved_name (m_name))
     {
-        PWARN ("Ignoring attempt to save to reserved name '%s'", name.c_str());
+        PWARN ("Ignoring attempt to save to reserved name '%s'", m_name.c_str());
         return true;
     }
 
     auto keyfile = gnc_state_get_current ();
-    auto group = csv_group_prefix + name;
+    auto group = csv_group_prefix + m_name;
 
     // Drop previous saved settings with this name
     g_key_file_remove_group (keyfile, group.c_str(), nullptr);
 
     // Start Saving the settings
-    g_key_file_set_string (keyfile, group.c_str(), CSV_NAME, name.c_str());
-    g_key_file_set_boolean (keyfile, group.c_str(), CSV_MULTI_SPLIT, multi_split);
-    g_key_file_set_integer (keyfile, group.c_str(), CSV_SKIP_START, skip_start_lines);
-    g_key_file_set_integer (keyfile, group.c_str(), CSV_SKIP_END, skip_end_lines);
-    g_key_file_set_boolean (keyfile, group.c_str(), CSV_SKIP_ALT, skip_alt_lines);
+    g_key_file_set_string (keyfile, group.c_str(), CSV_NAME, m_name.c_str());
+    g_key_file_set_boolean (keyfile, group.c_str(), CSV_MULTI_SPLIT, m_multi_split);
+    g_key_file_set_integer (keyfile, group.c_str(), CSV_SKIP_START, m_skip_start_lines);
+    g_key_file_set_integer (keyfile, group.c_str(), CSV_SKIP_END, m_skip_end_lines);
+    g_key_file_set_boolean (keyfile, group.c_str(), CSV_SKIP_ALT, m_skip_alt_lines);
     g_key_file_set_boolean (keyfile, group.c_str(), CSV_FORMAT,
-        (file_format == GncImpFileFormat::CSV) ? true : false);
+        (m_file_format == GncImpFileFormat::CSV) ? true : false);
 
-    g_key_file_set_string (keyfile, group.c_str(), CSV_SEP, separators.c_str());
-    g_key_file_set_integer (keyfile, group.c_str(), CSV_DATE, date_format);
-    g_key_file_set_integer (keyfile, group.c_str(), CSV_CURRENCY, currency_format);
-    g_key_file_set_string (keyfile, group.c_str(), CSV_ENCODING, encoding.c_str());
+    g_key_file_set_string (keyfile, group.c_str(), CSV_SEP, m_separators.c_str());
+    g_key_file_set_integer (keyfile, group.c_str(), CSV_DATE, m_date_format);
+    g_key_file_set_integer (keyfile, group.c_str(), CSV_CURRENCY, m_currency_format);
+    g_key_file_set_string (keyfile, group.c_str(), CSV_ENCODING, m_encoding.c_str());
 
-    if (base_account)
-        g_key_file_set_string (keyfile, group.c_str(), CSV_ACCOUNT, gnc_account_get_full_name(base_account));
+    if (m_base_account)
+        g_key_file_set_string (keyfile, group.c_str(), CSV_ACCOUNT, gnc_account_get_full_name(m_base_account));
 
     std::vector<const char*> col_types_str;
-    for (auto col_type : column_types)
+    for (auto col_type : m_column_types)
         col_types_str.push_back(gnc_csv_col_type_strs[col_type]);
 
     if (!col_types_str.empty())
         g_key_file_set_string_list (keyfile, group.c_str(), CSV_COL_TYPES,
                 col_types_str.data(), col_types_str.size());
 
-    if (!column_widths.empty())
+    if (!m_column_widths.empty())
         g_key_file_set_integer_list (keyfile, group.c_str(), CSV_COL_WIDTHS,
-                (gint*)(column_widths.data()), column_widths.size());
+                (gint*)(m_column_widths.data()), m_column_widths.size());
 
     // Do a test read of encoding
     GError *key_error = nullptr;
@@ -356,7 +356,7 @@ CsvTransSettings::save (void)
     if (enc_val)
         g_free (enc_val);
 
-    if ((key_error) || (enc_str != encoding.c_str()))
+    if ((key_error) || (enc_str != m_encoding.c_str()))
     {
         if (key_error)
         {
@@ -373,11 +373,11 @@ CsvTransSettings::save (void)
 void
 CsvTransSettings::remove (void)
 {
-    if (trans_preset_is_reserved_name (name))
+    if (trans_preset_is_reserved_name (m_name))
         return;
 
     auto keyfile = gnc_state_get_current ();
-    auto group = csv_group_prefix + name;
+    auto group = csv_group_prefix + m_name;
     g_key_file_remove_group (keyfile, group.c_str(), nullptr);
 }
 
@@ -385,8 +385,8 @@ CsvTransSettings::remove (void)
 bool
 CsvTransSettings::read_only (void)
 {
-    return ((name == no_settings) ||
-            (name == _(no_settings.c_str())) ||
-            (name == gnc_exp) ||
-            (name == _(gnc_exp.c_str())));
+    return ((m_name == no_settings) ||
+            (m_name == _(no_settings.c_str())) ||
+            (m_name == gnc_exp) ||
+            (m_name == _(gnc_exp.c_str())));
 }
diff --git a/src/import-export/csv-imp/gnc-csv-trans-settings.hpp b/src/import-export/csv-imp/gnc-csv-trans-settings.hpp
index fe53b34..f137113 100644
--- a/src/import-export/csv-imp/gnc-csv-trans-settings.hpp
+++ b/src/import-export/csv-imp/gnc-csv-trans-settings.hpp
@@ -48,11 +48,11 @@ enum SETTINGS_COL {SET_GROUP, SET_NAME};
 
 struct CsvTransSettings
 {
-    CsvTransSettings() : file_format (GncImpFileFormat::CSV), encoding {"UTF-8"},
-            multi_split (false), date_format {0}, currency_format {0},
-            skip_start_lines{0}, skip_end_lines{0}, skip_alt_lines (false),
-            separators {","}, base_account {nullptr},
-            load_error {false}, internal {false} { }
+    CsvTransSettings() : m_file_format (GncImpFileFormat::CSV), m_encoding {"UTF-8"},
+            m_multi_split (false), m_date_format {0}, m_currency_format {0},
+            m_skip_start_lines{0}, m_skip_end_lines{0}, m_skip_alt_lines (false),
+            m_separators {","}, m_base_account {nullptr},
+            m_load_error {false} { }
 
 /** Save the gathered widget properties to a key File.
  *
@@ -82,23 +82,22 @@ void remove (void);
 bool read_only (void);
 
 
-std::string   name;                         // Name given to this preset by the user
-GncImpFileFormat file_format;               // CSV import Format
-std::string   encoding;                     // File encoding
-bool          multi_split;                  // Assume multiple lines per transaction
-int           date_format;                  // Date Active id
-int           currency_format;              // Currency Active id
-uint           skip_start_lines;             // Number of header rows to skip
-uint           skip_end_lines;               // Number of footer rows to skip
-bool          skip_alt_lines;                // Skip alternate rows
-std::string   separators;                   // Separators for csv format
+std::string   m_name;                         // Name given to this preset by the user
+GncImpFileFormat m_file_format;               // CSV import Format
+std::string   m_encoding;                     // File encoding
+bool          m_multi_split;                  // Assume multiple lines per transaction
+int           m_date_format;                  // Date Active id
+int           m_currency_format;              // Currency Active id
+uint          m_skip_start_lines;             // Number of header rows to skip
+uint          m_skip_end_lines;               // Number of footer rows to skip
+bool          m_skip_alt_lines;               // Skip alternate rows
+std::string   m_separators;                   // Separators for csv format
 
-Account      *base_account;                 // Base account
-std::vector<GncTransPropType>  column_types;// The Column types in order
-std::vector<uint> column_widths;            // The Column widths
+Account      *m_base_account;                 // Base account
+std::vector<GncTransPropType> m_column_types; // The Column types in order
+std::vector<uint> m_column_widths;            // The Column widths
 
-bool          load_error;                   // Was there an error while parsing the state file ?
-bool          internal;                     // true for internally generated presets
+bool          m_load_error;                   // Was there an error while parsing the state file ?
 };
 
 using preset_vec = std::vector<std::shared_ptr<CsvTransSettings>>;
diff --git a/src/import-export/csv-imp/gnc-tx-import.cpp b/src/import-export/csv-imp/gnc-tx-import.cpp
index ccba3fe..189011d 100644
--- a/src/import-export/csv-imp/gnc-tx-import.cpp
+++ b/src/import-export/csv-imp/gnc-tx-import.cpp
@@ -66,8 +66,8 @@ GncTxImport::GncTxImport(GncImpFileFormat format)
     /* All of the data pointers are initially NULL. This is so that, if
      * gnc_csv_parse_data_free is called before all of the data is
      * initialized, only the data that needs to be freed is freed. */
-    parse_errors = false;
-    file_format(m_settings.file_format = format);
+    m_parse_errors = false;
+    file_format(m_settings.m_file_format = format);
 }
 
 /** Destructor for GncTxImport.
@@ -85,31 +85,31 @@ GncTxImport::~GncTxImport()
  */
 void GncTxImport::file_format(GncImpFileFormat format)
 {
-    if (tokenizer && m_settings.file_format == format)
+    if (m_tokenizer && m_settings.m_file_format == format)
         return;
 
     auto new_encoding = std::string("UTF-8");
     auto new_imp_file = std::string();
 
     // Recover common settings from old tokenizer
-    if (tokenizer)
+    if (m_tokenizer)
     {
-        new_encoding = tokenizer->encoding();
-        new_imp_file = tokenizer->current_file();
+        new_encoding = m_tokenizer->encoding();
+        new_imp_file = m_tokenizer->current_file();
     }
 
-    m_settings.file_format = format;
-    tokenizer = gnc_tokenizer_factory(m_settings.file_format);
+    m_settings.m_file_format = format;
+    m_tokenizer = gnc_tokenizer_factory(m_settings.m_file_format);
 
     // Set up new tokenizer with common settings
     // recovered from old tokenizer
-    tokenizer->encoding(new_encoding);
+    m_tokenizer->encoding(new_encoding);
     load_file(new_imp_file);
 }
 
 GncImpFileFormat GncTxImport::file_format()
 {
-    return m_settings.file_format;
+    return m_settings.m_file_format;
 }
 
 /** Toggles the multi-split state of the importer and will subsequently
@@ -120,19 +120,19 @@ GncImpFileFormat GncTxImport::file_format()
  */
 void GncTxImport::multi_split (bool multi_split)
 {
-    m_settings.multi_split = multi_split;
-    for (auto col_it = m_settings.column_types.begin(); col_it != m_settings.column_types.end();
+    m_settings.m_multi_split = multi_split;
+    for (auto col_it = m_settings.m_column_types.begin(); col_it != m_settings.m_column_types.end();
             col_it++)
     {
-        auto san_prop = sanitize_trans_prop (*col_it, m_settings.multi_split);
+        auto san_prop = sanitize_trans_prop (*col_it, m_settings.m_multi_split);
         if (san_prop != *col_it)
             *col_it = san_prop;
     }
-    if (m_settings.multi_split)
-        m_settings.base_account = nullptr;
+    if (m_settings.m_multi_split)
+        m_settings.m_base_account = nullptr;
 }
 
-bool GncTxImport::multi_split () { return m_settings.multi_split; }
+bool GncTxImport::multi_split () { return m_settings.m_multi_split; }
 
 /** Sets a base account. This is the account all import data relates to.
  *  As such at least one split of each transaction that will be generated
@@ -145,32 +145,32 @@ bool GncTxImport::multi_split () { return m_settings.multi_split; }
  */
 void GncTxImport::base_account (Account* base_account)
 {
-    if (m_settings.multi_split)
+    if (m_settings.m_multi_split)
     {
-        m_settings.base_account = nullptr;
+        m_settings.m_base_account = nullptr;
         return;
     }
 
-    m_settings.base_account = base_account;
+    m_settings.m_base_account = base_account;
 
-    if (m_settings.base_account)
+    if (m_settings.m_base_account)
     {
-        auto col_type = std::find (m_settings.column_types.begin(),
-                m_settings.column_types.end(), GncTransPropType::ACCOUNT);
-        if (col_type != m_settings.column_types.end())
+        auto col_type = std::find (m_settings.m_column_types.begin(),
+                m_settings.m_column_types.end(), GncTransPropType::ACCOUNT);
+        if (col_type != m_settings.m_column_types.end())
             *col_type = GncTransPropType::NONE;
     }
 }
 
-Account *GncTxImport::base_account () { return m_settings.base_account; }
+Account *GncTxImport::base_account () { return m_settings.m_base_account; }
 
 void GncTxImport::currency_format (int currency_format)
-    { m_settings.currency_format = currency_format; }
-int GncTxImport::currency_format () { return m_settings.currency_format; }
+    { m_settings.m_currency_format = currency_format; }
+int GncTxImport::currency_format () { return m_settings.m_currency_format; }
 
 void GncTxImport::date_format (int date_format)
-    { m_settings.date_format = date_format; }
-int GncTxImport::date_format () { return m_settings.date_format; }
+    { m_settings.m_date_format = date_format; }
+int GncTxImport::date_format () { return m_settings.m_date_format; }
 
 /** Converts raw file data using a new encoding. This function must be
  * called after load_file only if load_file guessed
@@ -181,45 +181,45 @@ void GncTxImport::encoding (const std::string& encoding)
 {
 
     // TODO investigate if we can catch conversion errors and report them
-    if (tokenizer)
-        tokenizer->encoding(encoding); // May throw
+    if (m_tokenizer)
+        m_tokenizer->encoding(encoding); // May throw
 
-    m_settings.encoding = encoding;
+    m_settings.m_encoding = encoding;
 }
 
-std::string GncTxImport::encoding () { return m_settings.encoding; }
+std::string GncTxImport::encoding () { return m_settings.m_encoding; }
 
 void GncTxImport::skip_start_lines (uint num)
-    { m_settings.skip_start_lines = num; }
-uint GncTxImport::skip_start_lines () { return m_settings.skip_start_lines; }
+    { m_settings.m_skip_start_lines = num; }
+uint GncTxImport::skip_start_lines () { return m_settings.m_skip_start_lines; }
 
-void GncTxImport::skip_end_lines (uint num) { m_settings.skip_end_lines = num; }
-uint GncTxImport::skip_end_lines () { return m_settings.skip_end_lines; }
+void GncTxImport::skip_end_lines (uint num) { m_settings.m_skip_end_lines = num; }
+uint GncTxImport::skip_end_lines () { return m_settings.m_skip_end_lines; }
 
 void GncTxImport::skip_alt_lines (bool skip)
-    { m_settings.skip_alt_lines = skip; }
-bool GncTxImport::skip_alt_lines () { return m_settings.skip_alt_lines; }
+    { m_settings.m_skip_alt_lines = skip; }
+bool GncTxImport::skip_alt_lines () { return m_settings.m_skip_alt_lines; }
 
 void GncTxImport::separators (std::string separators)
 {
     if (file_format() != GncImpFileFormat::CSV)
         return;
 
-    m_settings.separators = separators;
-    auto csvtok = dynamic_cast<GncCsvTokenizer*>(tokenizer.get());
+    m_settings.m_separators = separators;
+    auto csvtok = dynamic_cast<GncCsvTokenizer*>(m_tokenizer.get());
     csvtok->set_separators (separators);
 
 }
-std::string GncTxImport::separators () { return m_settings.separators; }
+std::string GncTxImport::separators () { return m_settings.m_separators; }
 
 void GncTxImport::settings (const CsvTransSettings& settings)
 {
     m_settings = settings;
-    file_format (m_settings.file_format);
-    multi_split (m_settings.multi_split);
-    base_account (m_settings.base_account);
-    encoding (m_settings.encoding);
-    separators (m_settings.separators);
+    file_format (m_settings.m_file_format);
+    multi_split (m_settings.m_multi_split);
+    base_account (m_settings.m_base_account);
+    encoding (m_settings.m_encoding);
+    separators (m_settings.m_separators);
     try
     {
         tokenize(false);
@@ -232,13 +232,13 @@ void GncTxImport::settings (const CsvTransSettings& settings)
 bool GncTxImport::save_settings ()
 {
 
-    if (trans_preset_is_reserved_name (m_settings.name))
+    if (trans_preset_is_reserved_name (m_settings.m_name))
         return true;
     return m_settings.save();
 }
 
-void GncTxImport::settings_name (std::string name) { m_settings.name = name; }
-std::string GncTxImport::settings_name () { return m_settings.name; }
+void GncTxImport::settings_name (std::string name) { m_settings.m_name = name; }
+std::string GncTxImport::settings_name () { return m_settings.m_name; }
 
 /** Loads a file into a GncTxImport. This is the first function
  * that must be called after creating a new GncTxImport. As long as
@@ -252,7 +252,7 @@ void GncTxImport::load_file (const std::string& filename)
     /* Get the raw data first and handle an error if one occurs. */
     try
     {
-        tokenizer->load_file (filename);
+        m_tokenizer->load_file (filename);
         return;
     }
     catch (std::ifstream::failure& ios_err)
@@ -276,15 +276,15 @@ void GncTxImport::load_file (const std::string& filename)
  */
 void GncTxImport::tokenize (bool guessColTypes)
 {
-    if (!tokenizer)
+    if (!m_tokenizer)
         return;
 
     uint max_cols = 0;
-    tokenizer->tokenize();
-    parsed_lines.clear();
-    for (auto tokenized_line : tokenizer->get_tokens())
+    m_tokenizer->tokenize();
+    m_parsed_lines.clear();
+    for (auto tokenized_line : m_tokenizer->get_tokens())
     {
-        parsed_lines.push_back (std::make_tuple (tokenized_line, std::string(),
+        m_parsed_lines.push_back (std::make_tuple (tokenized_line, std::string(),
                 nullptr, nullptr));
         auto length = tokenized_line.size();
         if (length > max_cols)
@@ -292,13 +292,13 @@ void GncTxImport::tokenize (bool guessColTypes)
     }
 
     /* If it failed, generate an error. */
-    if (parsed_lines.size() == 0)
+    if (m_parsed_lines.size() == 0)
     {
         throw (std::range_error ("Tokenizing failed."));
         return;
     }
 
-    m_settings.column_types.resize(max_cols, GncTransPropType::NONE);
+    m_settings.m_column_types.resize(max_cols, GncTransPropType::NONE);
 
     if (guessColTypes)
     {
@@ -319,15 +319,15 @@ std::string GncTxImport::verify ()
     auto error_text = std::string();
 
     /* Check if the import file did actually contain any information */
-    if (parsed_lines.size() == 0)
+    if (m_parsed_lines.size() == 0)
     {
         error_text = _("No valid data found in the selected file. It may be empty or the selected encoding is wrong.");
         return error_text;
     }
 
     /* Check if at least one line is selected for importing */
-    auto skip_alt_offset = m_settings.skip_alt_lines ? 1 : 0;
-    if (m_settings.skip_start_lines + m_settings.skip_end_lines + skip_alt_offset >= parsed_lines.size())
+    auto skip_alt_offset = m_settings.m_skip_alt_lines ? 1 : 0;
+    if (m_settings.m_skip_start_lines + m_settings.m_skip_end_lines + skip_alt_offset >= m_parsed_lines.size())
     {
         error_text = _("No lines are selected for importing. Please reduce the number of lines to skip.");
         return error_text;
@@ -344,20 +344,20 @@ std::string GncTxImport::verify ()
         /* Attempt to parse the date column for each selected line */
         try
         {
-            auto date_col = std::find(m_settings.column_types.begin(),
-                    m_settings.column_types.end(), GncTransPropType::DATE) -
-                            m_settings.column_types.begin();
-            for (uint i = 0; i < parsed_lines.size(); i++)
+            auto date_col = std::find(m_settings.m_column_types.begin(),
+                    m_settings.m_column_types.end(), GncTransPropType::DATE) -
+                            m_settings.m_column_types.begin();
+            for (uint i = 0; i < m_parsed_lines.size(); i++)
             {
-                if ((i < m_settings.skip_start_lines) ||             // start rows to skip
-                    (i >= parsed_lines.size()
-                            - m_settings.skip_end_lines) ||          // end rows to skip
-                    (((i - m_settings.skip_start_lines) % 2 == 1) && // skip every second row...
-                            m_settings.skip_alt_lines))              // ...if requested
+                if ((i < m_settings.m_skip_start_lines) ||             // start rows to skip
+                    (i >= m_parsed_lines.size()
+                            - m_settings.m_skip_end_lines) ||          // end rows to skip
+                    (((i - m_settings.m_skip_start_lines) % 2 == 1) && // skip every second row...
+                            m_settings.m_skip_alt_lines))              // ...if requested
                     continue;
                 else
                 {
-                    auto first_line = std::get<0>(parsed_lines[i]);
+                    auto first_line = std::get<0>(m_parsed_lines[i]);
                     auto date_str = first_line[date_col];
                     if (!date_str.empty())
                         parse_date (date_str, date_format());
@@ -375,12 +375,12 @@ std::string GncTxImport::verify ()
      */
     if (!check_for_column_type(GncTransPropType::ACCOUNT))
     {
-        if (m_settings.multi_split)
+        if (m_settings.m_multi_split)
         {
             error_text += newline + _("Please select an account column.");
             newline = "\n";
         }
-        else if (!m_settings.base_account)
+        else if (!m_settings.m_base_account)
         {
             error_text += newline + _("Please select an account column or set a base account in the Account field.");
             newline = "\n";
@@ -480,22 +480,22 @@ std::shared_ptr<DraftTransaction> GncTxImport::trans_properties_to_trans (std::v
         /* We're about to continue with a new transaction
          * Time to do some closing actions on the previous one
          */
-        if (current_draft && current_draft->void_reason)
+        if (m_current_draft && m_current_draft->void_reason)
         {
             /* The import data specifies this transaction was voided.
              * So void the created transaction as well.
              * Attention: this assumes the imported transaction was balanced.
              * If not, this will cause an imbalance split to be added automatically!
              */
-            xaccTransCommitEdit (current_draft->trans);
-            xaccTransVoid (current_draft->trans, current_draft->void_reason->c_str());
+            xaccTransCommitEdit (m_current_draft->trans);
+            xaccTransVoid (m_current_draft->trans, m_current_draft->void_reason->c_str());
         }
-        current_draft = std::make_shared<DraftTransaction>(trans);
-        current_draft->void_reason = trans_props->get_void_reason();
+        m_current_draft = std::make_shared<DraftTransaction>(trans);
+        m_current_draft->void_reason = trans_props->get_void_reason();
         created_trans = true;
     }
-    else if (m_settings.multi_split)  // in multi_split mode create_trans will return a nullptr for all but the first split
-        trans = current_draft->trans;
+    else if (m_settings.m_multi_split)  // in multi_split mode create_trans will return a nullptr for all but the first split
+        trans = m_current_draft->trans;
     else // in non-multi-split mode each line should be a transaction, so not having one here is an error
         throw std::invalid_argument ("Failed to create transaction from selected columns.");
 
@@ -508,7 +508,7 @@ std::shared_ptr<DraftTransaction> GncTxImport::trans_properties_to_trans (std::v
      * The return value will be added to a list for further processing,
      * we want each transaction to appear only once in that list.
      */
-    return created_trans ? current_draft : nullptr;
+    return created_trans ? m_current_draft : nullptr;
 }
 
 void GncTxImport::create_transaction (std::vector<parse_line_t>::iterator& parsed_line)
@@ -521,10 +521,10 @@ void GncTxImport::create_transaction (std::vector<parse_line_t>::iterator& parse
     error_message.clear();
 
     /* Convert all tokens in this line into transaction/split properties. */
-    auto col_types_it = m_settings.column_types.cbegin();
+    auto col_types_it = m_settings.m_column_types.cbegin();
     auto line_it = line.cbegin();
     for (col_types_it, line_it;
-            col_types_it != m_settings.column_types.cend() &&
+            col_types_it != m_settings.m_column_types.cend() &&
             line_it != line.cend();
             ++col_types_it, ++line_it)
     {
@@ -534,7 +534,7 @@ void GncTxImport::create_transaction (std::vector<parse_line_t>::iterator& parse
                 continue; /* We do nothing with "None"-type columns. */
             else if  (*col_types_it <= GncTransPropType::TRANS_PROPS)
             {
-                if (m_settings.multi_split && line_it->empty())
+                if (m_settings.m_multi_split && line_it->empty())
                     continue; // In multi-split mode, transaction properties can be empty
                 trans_props->set_property(*col_types_it, *line_it);
             }
@@ -543,7 +543,7 @@ void GncTxImport::create_transaction (std::vector<parse_line_t>::iterator& parse
         }
         catch (const std::exception& e)
         {
-            parse_errors = true;
+            m_parse_errors = true;
             if (!error_message.empty())
                 error_message += "\n";
             error_message += _(gnc_csv_col_type_strs[*col_types_it]);
@@ -555,26 +555,26 @@ void GncTxImport::create_transaction (std::vector<parse_line_t>::iterator& parse
 
     /* For multi-split input data, we need to check whether this line is part of a transaction that
      * has already be started by a previous line. */
-    if (m_settings.multi_split)
+    if (m_settings.m_multi_split)
     {
-        if (trans_props->is_part_of(parent))
+        if (trans_props->is_part_of(m_parent))
         {
             /* This line is part of an already started transaction
              * continue with that one instead to make sure the split from this line
              * gets added to the proper transaction */
-            std::get<2>(*parsed_line) = parent;
+            std::get<2>(*parsed_line) = m_parent;
 
             /* Check if the parent line is ready for conversion. If not,
              * this child line can't be converted either.
              */
-            if (!parent->verify_essentials().empty())
+            if (!m_parent->verify_essentials().empty())
                 error_message = _("First line of this transaction has errors.");
         }
         else
         {
             /* This line starts a new transaction, set it as parent for
              * subsequent lines. */
-            parent = trans_props;
+            m_parent = trans_props;
         }
     }
 
@@ -585,13 +585,13 @@ void GncTxImport::create_transaction (std::vector<parse_line_t>::iterator& parse
     auto line_acct = split_props->get_account();
     if (!line_acct)
     {
-        if (m_settings.base_account)
-            split_props->set_account(m_settings.base_account);
+        if (m_settings.m_base_account)
+            split_props->set_account(m_settings.m_base_account);
         else
         {
             // Oops - the user didn't select an Account column *and* we didn't get a default value either!
             // Note if you get here this suggests a bug in the code!
-            parse_errors = true;
+            m_parse_errors = true;
             error_message = _("No account column selected and no default account specified either.\n"
                                        "This should never happen. Please report this as a bug.");
             PINFO("User warning: %s", error_message.c_str());
@@ -610,12 +610,12 @@ void GncTxImport::create_transaction (std::vector<parse_line_t>::iterator& parse
         if (draft_trans)
         {
             auto trans_date = xaccTransGetDate (draft_trans->trans);
-            transactions.insert (std::pair<time64, std::shared_ptr<DraftTransaction>>(trans_date,std::move(draft_trans)));
+            m_transactions.insert (std::pair<time64, std::shared_ptr<DraftTransaction>>(trans_date,std::move(draft_trans)));
         }
     }
     catch (const std::invalid_argument& e)
     {
-        parse_errors = true;
+        m_parse_errors = true;
         error_message = e.what();
         PINFO("User warning: %s", error_message.c_str());
     }
@@ -637,23 +637,23 @@ void GncTxImport::create_transactions (bool redo_errors)
     if (!redo_errors)
     {
         /* Clear error messages on full run */
-        for (auto orig_line : parsed_lines)
+        for (auto orig_line : m_parsed_lines)
             std::get<1>(orig_line).clear();
 
         /* Drop all existing draft transactions on a full run */
-        transactions.clear();
+        m_transactions.clear();
     }
 
     /* compute start and end iterators based on user-set restrictions */
-    auto parsed_lines_it = parsed_lines.begin();
+    auto parsed_lines_it = m_parsed_lines.begin();
     std::advance(parsed_lines_it, skip_start_lines());
 
-    auto parsed_lines_max = parsed_lines.begin();
-    std::advance(parsed_lines_max, parsed_lines.size() - skip_end_lines());
+    auto parsed_lines_max = m_parsed_lines.begin();
+    std::advance(parsed_lines_max, m_parsed_lines.size() - skip_end_lines());
 
     auto odd_line = false;
-    parse_errors = false;
-    parent = nullptr;
+    m_parse_errors = false;
+    m_parent = nullptr;
 
     /* Iterate over all parsed lines */
     for (parsed_lines_it, odd_line;
@@ -686,21 +686,21 @@ void GncTxImport::create_transactions (bool redo_errors)
 bool
 GncTxImport::check_for_column_type (GncTransPropType type)
 {
-    return (std::find (m_settings.column_types.begin(),
-                       m_settings.column_types.end(), type)
-                        != m_settings.column_types.end());
+    return (std::find (m_settings.m_column_types.begin(),
+                       m_settings.m_column_types.end(), type)
+                        != m_settings.m_column_types.end());
 }
 
 void
 GncTxImport::set_column_type (uint position, GncTransPropType type)
 {
-    if (position >= m_settings.column_types.size())
+    if (position >= m_settings.m_column_types.size())
         return;
 
     // Column types should be unique, so remove any previous occurrence of the new type
-    std::replace(m_settings.column_types.begin(), m_settings.column_types.end(),
+    std::replace(m_settings.m_column_types.begin(), m_settings.m_column_types.end(),
             type, GncTransPropType::NONE);
-    m_settings.column_types.at (position) = type;
+    m_settings.m_column_types.at (position) = type;
 
     // If the user has set an Account column, we can't have a base account set
     if (type == GncTransPropType::ACCOUNT)
@@ -709,5 +709,5 @@ GncTxImport::set_column_type (uint position, GncTransPropType type)
 
 std::vector<GncTransPropType> GncTxImport::column_types ()
 {
-    return m_settings.column_types;
+    return m_settings.m_column_types;
 }
diff --git a/src/import-export/csv-imp/gnc-tx-import.hpp b/src/import-export/csv-imp/gnc-tx-import.hpp
index e82354c..49ef643 100644
--- a/src/import-export/csv-imp/gnc-tx-import.hpp
+++ b/src/import-export/csv-imp/gnc-tx-import.hpp
@@ -144,13 +144,13 @@ public:
     void set_column_type (uint position, GncTransPropType type);
     std::vector<GncTransPropType> column_types ();
 
-    std::unique_ptr<GncTokenizer> tokenizer;    /**< Will handle file loading/encoding conversion/splitting into fields */
-    std::vector<parse_line_t> parsed_lines;     /**< source file parsed into a two-dimensional array of strings.
+    std::unique_ptr<GncTokenizer> m_tokenizer;    /**< Will handle file loading/encoding conversion/splitting into fields */
+    std::vector<parse_line_t> m_parsed_lines;     /**< source file parsed into a two-dimensional array of strings.
                                                      Per line also holds possible error messages and objects with extracted transaction
                                                      and split properties. */
-    std::multimap <time64, std::shared_ptr<DraftTransaction>> transactions;  /**< map of transaction objects created
+    std::multimap <time64, std::shared_ptr<DraftTransaction>> m_transactions;  /**< map of transaction objects created
                                                      from parsed_lines and column_types, ordered by date */
-    bool parse_errors;          /**< Indicates whether the last parse_to_trans run had any errors */
+    bool m_parse_errors;          /**< Indicates whether the last parse_to_trans run had any errors */
 
 private:
     /** A helper function used by create_transactions. It will attempt
@@ -177,8 +177,8 @@ private:
     /* The parameters below are only used while creating
      * transactions. They keep state information during the conversion.
      */
-    std::shared_ptr<GncPreTrans> parent = nullptr;
-    std::shared_ptr<DraftTransaction> current_draft = nullptr;
+    std::shared_ptr<GncPreTrans> m_parent = nullptr;
+    std::shared_ptr<DraftTransaction> m_current_draft = nullptr;
 };
 
 

commit 4032b553f4f2097fa39dfaa4faed5a2abbd9fb6c
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sat Dec 17 19:10:02 2016 +0100

    Revisit csv settings
    
    - Store importer settings in a separate object inside the CsvTxImport
    - make CsvTxImport responsible for maintaining a consistent settings state
    - reduce the assistant's responsability to passing settings changes to CsvTxImport and
      visual display. It no longer does validation
    - Refine the gui some more

diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index 7eabe75..45105ce 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -69,7 +69,7 @@ extern "C"
 /* This static indicates the debugging module that this .o belongs to.  */
 static QofLogModule log_module = GNC_MOD_ASSISTANT;
 
-typedef struct
+struct  CsvImportTrans
 {
 
     GtkWidget       *window;
@@ -78,7 +78,6 @@ typedef struct
 
     GtkWidget       *file_page;                     /**< Assistant file page widget */
     GtkWidget       *file_chooser;                  /**< The widget for the file chooser */
-    std::string      starting_dir;                  /**< The starting directory for import file */
     std::string      file_name;                     /**< The import file name */
     std::string      error_text;                    /**< Error Text */
 
@@ -132,7 +131,7 @@ typedef struct
     GtkWidget            *match_label;              /**< The match label at the bottom of the page */
     GtkWidget            *help_button;              /**< The widget for the help button on the matcher page */
     GtkWidget            *cancel_button;            /**< The widget for the new cancel button when going back is blocked */
-    bool                  match_parse_run;          /**< This is set after the first run */
+    bool                  match_parse_run = false;  /**< This is set after the first run */
 
     GtkWidget            *summary_page;             /**< Assistant summary page widget */
     GtkWidget            *summary_label;            /**< The summary text */
@@ -141,7 +140,7 @@ typedef struct
     int                   callcount;                /**< Number of times the assistant page forward function called */
     int                   next_page;                /**< The saved assistant next page number */
 
-} CsvImportTrans;
+};
 
 
 /*************************************************************************/
@@ -154,9 +153,9 @@ void csv_import_trans_assistant_cancel (GtkAssistant *gtkassistant, gpointer use
 void csv_import_trans_assistant_close (GtkAssistant *gtkassistant, gpointer user_data);
 void csv_import_trans_srow_cb (GtkWidget *spin, gpointer user_data);
 void csv_import_trans_erow_cb (GtkWidget *spin, gpointer user_data);
-void csv_import_trans_skip_errors_cb (GtkWidget *cb, gpointer user_data);
-void csv_import_trans_skiprows_cb (GtkWidget *checkbox, gpointer user_data);
-void csv_import_trans_multisplit_cb (GtkWidget *checkbox, gpointer user_data);
+void csv_import_trans_skip_errors_cb (GtkToggleButton *checkbox, gpointer user_data);
+void csv_import_trans_skiprows_cb (GtkToggleButton *checkbox, gpointer user_data);
+void csv_import_trans_multisplit_cb (GtkToggleButton *checkbox, gpointer user_data);
 void csv_import_trans_auto_cb (GtkWidget *cb, gpointer user_data);
 void csv_import_trans_file_chooser_confirm_cb (GtkWidget *button, CsvImportTrans *info);
 
@@ -176,11 +175,9 @@ void csv_import_trans_assistant_doc_page_prepare (GtkAssistant *assistant, gpoin
 void csv_import_trans_assistant_match_page_prepare (GtkAssistant *assistant, gpointer user_data);
 void csv_import_trans_assistant_summary_page_prepare (GtkAssistant *assistant, gpointer user_data);
 
-void csv_import_trans_load_settings (CsvImportTrans *info);
-
-void csv_import_refresh_preview_table (CsvImportTrans* info);
+void csv_import_preview_refresh_table (CsvImportTrans* info);
+void csv_import_preview_refresh (CsvImportTrans *info);
 void csv_import_validate_preview_settings (CsvImportTrans* info);
-void gnc_csv_reset_preview_setting (CsvImportTrans* info, bool block);
 bool get_list_of_accounts (CsvImportTrans* info, GtkTreeModel *store);
 
 /*************************************************************************/
@@ -216,148 +213,6 @@ static void populate_settings_combo(GtkComboBox* combo)
     }
 }
 
-/*******************************************************
- * csv_import_trans_load_settings
- *
- * Load the settings from a key file
- *******************************************************/
-void
-csv_import_trans_load_settings (CsvImportTrans *info)
-{
-    GtkTreeIter   iter;
-
-    // Get the Active Selection
-    if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX(info->settings_combo), &iter))
-        return;
-
-    CsvTransSettings *preset;
-    GtkTreeModel *model = gtk_combo_box_get_model (GTK_COMBO_BOX(info->settings_combo));
-    gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1);
-
-    // Test for default selection, return
-    if (!preset)
-        return;
-
-    if (preset->load_error)
-    {
-        const gchar *title = _("Load the Import Settings.");
-        GtkWidget *dialog = gtk_message_dialog_new (GTK_WINDOW(info->window),
-                                    (GtkDialogFlags) 0,
-                                    GTK_MESSAGE_ERROR,
-                                    GTK_BUTTONS_OK,
-                                    "%s", title);
-        gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
-            "%s", _("There were problems reading some saved settings, continuing to load.\n Please review and save again."));
-        gtk_dialog_run (GTK_DIALOG (dialog));
-        gtk_widget_destroy (dialog);
-    }
-
-    // Set start row
-    GtkAdjustment *adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(info->start_row_spin));
-    gtk_adjustment_set_upper (adj, info->parse_data->parsed_lines.size());
-    gtk_spin_button_set_value (GTK_SPIN_BUTTON(info->start_row_spin),
-            preset->header_rows);
-
-    // Set end row
-    adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(info->end_row_spin));
-    gtk_adjustment_set_upper (adj, info->parse_data->parsed_lines.size());
-    gtk_spin_button_set_value (GTK_SPIN_BUTTON(info->end_row_spin),
-            preset->footer_rows);
-
-    // Set Alternate rows
-    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->skip_rows), preset->skip_alt_rows);
-
-    // Set Multi-split indicator
-    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->multi_split_cbutton), preset->multi_split);
-
-    // Set Import Format
-    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->csv_button), preset->csv_format);
-    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->fixed_button), !preset->csv_format);
-
-    // This section deals with the combo's and character encoding
-    info->parse_data->date_format = preset->date_active;
-    gtk_combo_box_set_active (GTK_COMBO_BOX(info->date_format_combo), preset->date_active);
-
-    info->parse_data->currency_format = preset->currency_active;
-    gtk_combo_box_set_active (GTK_COMBO_BOX(info->currency_format_combo), preset->currency_active);
-    info->parse_data->convert_encoding (preset->encoding);
-    go_charmap_sel_set_encoding (info->encselector, preset->encoding.c_str());
-
-    try
-    {
-        if (preset->csv_format)
-        {
-            // Handle separators, only relevant if the file format is csv
-            info->parse_data->file_format (GncImpFileFormat::CSV);
-            for (int i = 0; i < SEP_NUM_OF_TYPES; i++)
-                gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->sep_buttons[i]), preset->separator[i]);
-            gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->custom_cbutton), preset->custom);
-            if (preset->custom)
-                gtk_entry_set_text (GTK_ENTRY(info->custom_entry), preset->custom_entry.c_str());
-            else
-                gtk_entry_set_text (GTK_ENTRY(info->custom_entry), "");
-
-            sep_button_clicked (NULL, info);
-        }
-        else
-        {
-            // Handle column widths, only relevant if the file format is fixed width
-            info->parse_data->file_format (GncImpFileFormat::FIXED_WIDTH);
-            GncFwTokenizer *fwtok = dynamic_cast<GncFwTokenizer*>(info->parse_data->tokenizer.get());
-            fwtok->columns(preset->column_widths);
-
-            info->parse_data->tokenize (false);
-            csv_import_refresh_preview_table (info);
-        }
-    }
-    catch (std::range_error &e)
-    {
-        // Catch parse errors
-        gnc_error_dialog (NULL, "%s", _("There was a problem with the column widths, please review."));
-        return;
-    }
-    catch (...)
-    {
-        // FIXME Handle file loading errors (possibly thrown by file_format above)
-        PWARN("Got an error during file loading");
-    }
-
-    // Set column types on the import data table to what's in the loaded preset
-    // Note we can only set types for actual columns in the data. If the loaded
-    // preset has more column types, these will be discarded.
-    // First get an iterator for the first (and only) row of ctstore, which
-    // contains the column types and their (translated) string representation
-    // appearing in the column types treeview.
-    GtkTreeModel *ctstore = gtk_tree_view_get_model (info->ctreeview);
-    gtk_tree_model_get_iter_first (ctstore, &iter);
-
-    for (uint i=0; i < preset->column_types.size() && i < info->parse_data->column_types.size(); i++)
-    {
-        auto col_type = preset->column_types.at(i);
-            info->parse_data->column_types.at(i) = col_type;
-            /* Set the column type. Store is arranged so that every two
-             * columns is a pair of
-             * - the column type as a user visible (translated) string
-             * - the internal type for this column
-             * So ctstore looks like:
-             * col_type_str 0, col_type, col_type_str 1, col_type 1, ..., col_type_str ncols, col_type ncols. */
-            if ((2 * i + 1) < static_cast<uint>(gtk_tree_model_get_n_columns (ctstore)))
-                gtk_list_store_set (GTK_LIST_STORE(ctstore), &iter,
-                        2 * i, _(gnc_csv_col_type_strs[col_type]),
-                        2 * i + 1, col_type,
-                        -1);
-    }
-
-    /* The user can't both select a base account and an account column.
-     * So now the column_types vector is updated also update the base account
-     * selector's availability.
-     */
-    if (info->parse_data->check_for_column_type(GncTransPropType::ACCOUNT))
-    {
-        gnc_account_sel_set_account(GNC_ACCOUNT_SEL(info->acct_selector), nullptr, false);
-        info->parse_data->set_base_account(nullptr);
-    }
-}
 
 static void handle_save_del_sensitivity (GtkWidget* combo, CsvImportTrans *info)
 {
@@ -399,7 +254,34 @@ static void handle_save_del_sensitivity (GtkWidget* combo, CsvImportTrans *info)
 void
 csv_import_trans_changed_settings_cb (GtkWidget *combo, CsvImportTrans *info)
 {
-    csv_import_trans_load_settings (info);
+    // Get the Active Selection
+    GtkTreeIter iter;
+    if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX(info->settings_combo), &iter))
+        return;
+
+    CsvTransSettings *preset = nullptr;
+    GtkTreeModel *model = gtk_combo_box_get_model (GTK_COMBO_BOX(info->settings_combo));
+    gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1);
+
+    if (!preset)
+        return;
+
+    info->parse_data->settings (*preset);
+    if (preset->load_error)
+    {
+        const gchar *title = _("Load the Import Settings.");
+        GtkWidget *dialog = gtk_message_dialog_new (GTK_WINDOW(info->window),
+                                    (GtkDialogFlags) 0,
+                                    GTK_MESSAGE_ERROR,
+                                    GTK_BUTTONS_OK,
+                                    "%s", title);
+        gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+            "%s", _("There were problems reading some saved settings, continuing to load.\n Please review and save again."));
+        gtk_dialog_run (GTK_DIALOG (dialog));
+        gtk_widget_destroy (dialog);
+    }
+
+    csv_import_preview_refresh (info);
     handle_save_del_sensitivity (combo, info);
     csv_import_validate_preview_settings (info);
 }
@@ -413,14 +295,15 @@ csv_import_trans_changed_settings_cb (GtkWidget *combo, CsvImportTrans *info)
 void
 csv_import_trans_changed_settings_text_cb (GtkWidget *entry, CsvImportTrans *info)
 {
+    auto text = gtk_entry_get_text (GTK_ENTRY(entry));
+    if (text)
+        info->parse_data->settings_name(text);
+
     auto combo = gtk_widget_get_parent (entry);
     handle_save_del_sensitivity (combo, info);
 }
 
 
-
-
-
 /*******************************************************
  * csv_import_trans_delete_settings_cb
  *
@@ -455,7 +338,7 @@ csv_import_trans_delete_settings_cb (GtkWidget *button, CsvImportTrans *info)
         preset->remove();
         populate_settings_combo(GTK_COMBO_BOX(info->settings_combo));
         gtk_combo_box_set_active (GTK_COMBO_BOX(info->settings_combo), 0); // Default
-        gnc_csv_reset_preview_setting (info, false); // Reset the widgets
+        csv_import_preview_refresh (info); // Reset the widgets
     }
 }
 
@@ -467,119 +350,46 @@ csv_import_trans_delete_settings_cb (GtkWidget *button, CsvImportTrans *info)
 void
 csv_import_trans_save_settings_cb (GtkWidget *button, CsvImportTrans *info)
 {
-    const gchar *title = _("Save the Import Settings.");
-    GtkTreeIter iter;
-
-    // Get the Entry Text
-    auto entry = gtk_bin_get_child (GTK_BIN(info->settings_combo));
-    auto entry_text = gtk_entry_get_text (GTK_ENTRY(entry));
+    auto title = _("Save the Import Settings.");
+    auto new_name = info->parse_data->settings_name();
 
     /* Check if the entry text matches an already existing preset */
-    int response = GTK_RESPONSE_OK;
+    GtkTreeIter iter;
     if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX(info->settings_combo), &iter))
     {
 
         GtkTreeModel *model = gtk_combo_box_get_model (GTK_COMBO_BOX(info->settings_combo));
         bool valid = gtk_tree_model_get_iter_first (model, &iter);
-        bool found = false;
         while (valid)
         {
             // Walk through the list, reading each row
             CsvTransSettings *preset;
             gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1);
 
-            if (preset && (preset->name == std::string(entry_text)))
+            if (preset && (preset->name == std::string(new_name)))
             {
-                found = true;
+                auto dialog = gtk_message_dialog_new (GTK_WINDOW(info->window),
+                                        (GtkDialogFlags) 0,
+                                        GTK_MESSAGE_QUESTION,
+                                        GTK_BUTTONS_OK_CANCEL,
+                                        "%s", title);
+                gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+                "%s", _("Setting name already exists, over write?"));
+                auto response = gtk_dialog_run (GTK_DIALOG (dialog));
+                gtk_widget_destroy (dialog);
+
+                if (response != GTK_RESPONSE_OK)
+                    return;
+
                 break;
             }
             valid = gtk_tree_model_iter_next (model, &iter);
         }
-
-        if (found) // We have found entry_text in liststore
-        {
-            auto dialog = gtk_message_dialog_new (GTK_WINDOW(info->window),
-                                    (GtkDialogFlags) 0,
-                                    GTK_MESSAGE_QUESTION,
-                                    GTK_BUTTONS_OK_CANCEL,
-                                    "%s", title);
-            gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
-            "%s", _("Setting name already exists, over write?"));
-            response = gtk_dialog_run (GTK_DIALOG (dialog));
-            gtk_widget_destroy (dialog);
-        }
     }
 
-    if (response != GTK_RESPONSE_OK)
-        return;
-
     /* All checks passed, let's save this preset */
-    CsvTransSettings preset;
-    preset.name = entry_text;
-
-    /* This section deals with the header and rows */
-    preset.header_rows = gtk_spin_button_get_value (GTK_SPIN_BUTTON(info->start_row_spin));
-    preset.footer_rows = gtk_spin_button_get_value (GTK_SPIN_BUTTON(info->end_row_spin));
-    preset.skip_alt_rows = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(info->skip_rows));
-    preset.csv_format = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(info->csv_button));
-
-    /* This Section deals with the separators */
-    for (int i = 0; i < SEP_NUM_OF_TYPES; i++)
-    {
-        preset.separator[i] = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(info->sep_buttons[i]));
-    }
-    preset.custom = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(info->custom_cbutton));
-    auto tmp_text = gtk_entry_get_text (GTK_ENTRY(info->custom_entry));
-    if (tmp_text && *tmp_text != '\0')
-        preset.custom_entry = tmp_text;
-
-    /* This section deals with the combo's and character encoding */
-    preset.date_active = gtk_combo_box_get_active (GTK_COMBO_BOX(info->date_format_combo));
-    preset.currency_active = gtk_combo_box_get_active (GTK_COMBO_BOX(info->currency_format_combo));
-    preset.encoding = go_charmap_sel_get_encoding (info->encselector);
-    preset.multi_split = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(info->multi_split_cbutton));
-
-    /* This section deals with the Treeview column names */
-    GList *columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(info->ctreeview));
-    // ctstore contains the actual strings appearing in the column types treeview.
-    GtkTreeModel *ctstore = gtk_tree_view_get_model (info->ctreeview);
-    // Get an iterator for the first (and only) row.
-    gtk_tree_model_get_iter_first (ctstore, &iter);
-
-    preset.column_types.clear();
-    GList *column;
-    int i;
-    for (column = columns, i = 0; column; column = g_list_next (column), i++)
-    {
-        auto col_type = GncTransPropType::NONE;
-        gchar *col_type_str = NULL;
-
-        /* Get the column type. Store is arranged so that every two
-         * columns is a pair of
-         * - the column type as a user visible (translated) string
-         * - the internal type for this column
-         * So ctstore looks like:
-         * col_type_str 0, col_type, col_type_str 1, col_type 1, ..., col_type_str ncols, col_type ncols. */
-        gtk_tree_model_get (ctstore, &iter, 2 * i + 1, &col_type, -1);
-        preset.column_types.push_back(col_type);
-
-    }
-    g_list_free (columns);
-
-    /* Save the column widths in fixed mode */
-    if (!preset.csv_format)
-    {
-        GncFwTokenizer *fwtok = dynamic_cast<GncFwTokenizer*>(info->parse_data->tokenizer.get());
-        preset.column_widths = fwtok->get_columns();
-    }
-
-    // Save the settings
-    if (!preset.save ())
+    if (!info->parse_data->save_settings())
     {
-        GtkTreeModel *model;
-        GtkTreeIter   iter;
-        bool          valid = false;
-
         auto dialog = gtk_message_dialog_new (GTK_WINDOW(info->window),
                                     (GtkDialogFlags) 0,
                                     GTK_MESSAGE_INFO,
@@ -592,18 +402,19 @@ csv_import_trans_save_settings_cb (GtkWidget *button, CsvImportTrans *info)
 
         // Update the settings store
         populate_settings_combo(GTK_COMBO_BOX(info->settings_combo));
-        model = gtk_combo_box_get_model (GTK_COMBO_BOX(info->settings_combo));
+        auto model = gtk_combo_box_get_model (GTK_COMBO_BOX(info->settings_combo));
 
         // Get the first entry in model
-        valid = gtk_tree_model_get_iter_first (model, &iter);
+        GtkTreeIter   iter;
+        bool valid = gtk_tree_model_get_iter_first (model, &iter);
         while (valid)
         {
-            gchar *name = NULL;
+            gchar *name = nullptr;
 
             // Walk through the list, reading each row
             gtk_tree_model_get (model, &iter, SET_NAME, &name, -1);
 
-            if (g_strcmp0 (name, entry_text) == 0) // Set Active, the one Saved.
+            if (g_strcmp0 (name, new_name.c_str()) == 0) // Set Active, the one Saved.
                 gtk_combo_box_set_active_iter (GTK_COMBO_BOX(info->settings_combo), &iter);
 
             g_free (name);
@@ -648,15 +459,15 @@ csv_import_trans_file_chooser_confirm_cb (GtkWidget *button, CsvImportTrans *inf
     auto starting_dir = g_path_get_dirname (filepath);
 
     info->file_name = file_name;
-    info->starting_dir = starting_dir;
+    gnc_set_default_directory (GNC_PREFS_GROUP, starting_dir);
+
+    DEBUG("file_name selected is %s", info->file_name.c_str());
+    DEBUG("starting directory is %s", starting_dir);
 
     g_free (filepath);
     g_free (file_name);
     g_free (starting_dir);
 
-    DEBUG("file_name selected is %s", info->file_name.c_str());
-    DEBUG("starting directory is %s", info->starting_dir.c_str());
-
     /* Load the file into parse_data. */
     auto parse_data = new GncTxImport;
     /* Assume data is CSV. User can later override to Fixed Width if needed */
@@ -682,18 +493,11 @@ csv_import_trans_file_chooser_confirm_cb (GtkWidget *button, CsvImportTrans *inf
     }
 
     if (info->parse_data) // Free parse_data if we have come back here
-    {
         delete info->parse_data;
-        gnc_csv_reset_preview_setting (info, true);
-    }
     info->parse_data = parse_data;
     info->previewing_errors = false; /* We're looking at all the data. */
     info->skip_errors = false; // Set skip_errors to False
-
-    /* Reset a couple of widgets on the preview page */
-    // - leading and trailing lines to skip = 0
-    gtk_spin_button_set_value (GTK_SPIN_BUTTON(info->start_row_spin), 0);
-    gtk_spin_button_set_value (GTK_SPIN_BUTTON(info->end_row_spin), 0);
+    csv_import_preview_refresh (info);
 
     /* Get settings store and populate */
     populate_settings_combo(GTK_COMBO_BOX(info->settings_combo));
@@ -719,11 +523,11 @@ void row_selection_update (CsvImportTrans* info)
     for (uint i = 0; i < info->parse_data->parsed_lines.size(); i++)
     {
         const char *color = NULL;
-        if ((i < info->parse_data->skip_start_lines) ||             // start rows to skip
+        if ((i < info->parse_data->skip_start_lines()) ||             // start rows to skip
             (i >= info->parse_data->parsed_lines.size()
-                    - info->parse_data->skip_end_lines) ||          // end rows to skip
-            (((i - info->parse_data->skip_start_lines) % 2 == 1) && // skip every second row...
-             info->parse_data->skip_alt_lines))                   // ...if requested
+                    - info->parse_data->skip_end_lines()) ||          // end rows to skip
+            (((i - info->parse_data->skip_start_lines()) % 2 == 1) && // skip every second row...
+             info->parse_data->skip_alt_lines()))                     // ...if requested
             color = "pink";
         else
             color = NULL;                                           // all other rows
@@ -734,6 +538,76 @@ void row_selection_update (CsvImportTrans* info)
     }
 }
 
+/*******************************************************
+ * csv_import_preview_refresh
+ *
+ * Update the preview page based on the current state of the importer.
+ * Should be called when settings are changed.
+ *******************************************************/
+void
+csv_import_preview_refresh (CsvImportTrans *info)
+{
+    GtkTreeIter   iter;
+
+    // Set start row
+    GtkAdjustment *adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(info->start_row_spin));
+    gtk_adjustment_set_upper (adj, info->parse_data->parsed_lines.size());
+    gtk_spin_button_set_value (GTK_SPIN_BUTTON(info->start_row_spin),
+            info->parse_data->skip_start_lines());
+
+    // Set end row
+    adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(info->end_row_spin));
+    gtk_adjustment_set_upper (adj, info->parse_data->parsed_lines.size());
+    gtk_spin_button_set_value (GTK_SPIN_BUTTON(info->end_row_spin),
+            info->parse_data->skip_end_lines());
+
+    // Set Alternate rows
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->skip_rows), info->parse_data->skip_alt_lines());
+
+    // Set multi-split indicator
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->multi_split_cbutton), info->parse_data->multi_split());
+
+    // Set Import Format
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->csv_button),
+            (info->parse_data->file_format() == GncImpFileFormat::CSV));
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->fixed_button),
+            (info->parse_data->file_format() != GncImpFileFormat::CSV));
+
+    // This section deals with the combo's and character encoding
+    gtk_combo_box_set_active (GTK_COMBO_BOX(info->date_format_combo), info->parse_data->date_format());
+    gtk_combo_box_set_active (GTK_COMBO_BOX(info->currency_format_combo), info->parse_data->currency_format());
+    go_charmap_sel_set_encoding (info->encselector, info->parse_data->encoding().c_str());
+
+    gnc_account_sel_set_account(GNC_ACCOUNT_SEL(info->acct_selector),
+            info->parse_data->base_account() , false);
+
+    // Handle separator checkboxes and custom field, only relevant if the file format is csv
+    if (info->parse_data->file_format() == GncImpFileFormat::CSV)
+    {
+        auto separators = info->parse_data->separators();
+        const auto stock_sep_chars = std::string (" \t,:;-");
+        for (int i = 0; i < SEP_NUM_OF_TYPES; i++)
+            gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->sep_buttons[i]),
+                separators.find (stock_sep_chars[i]) != std::string::npos);
+
+        // If there are any other separators in the separators string,
+        // add them as custom separators
+        auto pos = separators.find_first_of (stock_sep_chars);
+        while (!separators.empty() && pos != std::string::npos)
+        {
+            separators.erase(pos);
+            pos = separators.find_first_of (" \t,:;-");
+        }
+        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->custom_cbutton),
+                !separators.empty());
+        gtk_entry_set_text (GTK_ENTRY(info->custom_entry), separators.c_str());
+    }
+
+    // Repopulate the parsed data table
+    csv_import_preview_refresh_table (info);
+}
+
+
 
 /*******************************************************
  * csv_import_trans_srow_cb
@@ -746,12 +620,12 @@ void csv_import_trans_srow_cb (GtkWidget *spin, gpointer user_data)
     GtkAdjustment *adj;
 
     /* Get number of lines to skip at the beginning */
-    info->parse_data->skip_start_lines = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON(spin));
+    info->parse_data->skip_start_lines (gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON(spin)));
 
     /* And adjust maximum number of lines that can be skipped at the end accordingly */
     adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(info->end_row_spin));
     gtk_adjustment_set_upper (adj, info->parse_data->parsed_lines.size()
-            - info->parse_data->skip_start_lines);
+            - info->parse_data->skip_start_lines());
 
     /* Refresh the row highlighting */
     row_selection_update (info);
@@ -771,12 +645,12 @@ void csv_import_trans_erow_cb (GtkWidget *spin, gpointer user_data)
     GtkAdjustment *adj;
 
     /* Get number of lines to skip at the end */
-    info->parse_data->skip_end_lines = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON(spin));
+    info->parse_data->skip_end_lines (gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON(spin)));
 
     /* And adjust maximum number of lines that can be skipped at the beginning accordingly */
     adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(info->start_row_spin));
     gtk_adjustment_set_upper (adj, info->parse_data->parsed_lines.size()
-            - info->parse_data->skip_end_lines);
+            - info->parse_data->skip_end_lines());
 
     /* Refresh the row highlighting */
     row_selection_update (info);
@@ -790,15 +664,11 @@ void csv_import_trans_erow_cb (GtkWidget *spin, gpointer user_data)
  *
  * call back for Skip Errors
  *******************************************************/
-void csv_import_trans_skip_errors_cb (GtkWidget *cb, gpointer user_data)
+void csv_import_trans_skip_errors_cb (GtkToggleButton *checkbox, gpointer user_data)
 {
     CsvImportTrans *info = (CsvImportTrans*) user_data;
 
-    if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(cb)))
-        info->skip_errors = true;
-    else
-        info->skip_errors = false;
-
+    info->skip_errors = gtk_toggle_button_get_active (checkbox);
     csv_import_validate_preview_settings (info);
 }
 
@@ -808,14 +678,12 @@ void csv_import_trans_skip_errors_cb (GtkWidget *cb, gpointer user_data)
  *
  * call back for import multi-split checkbox
  *******************************************************/
-void csv_import_trans_multisplit_cb (GtkWidget *checkbox, gpointer user_data)
+void csv_import_trans_multisplit_cb (GtkToggleButton *checkbox, gpointer user_data)
 {
     CsvImportTrans *info = (CsvImportTrans*) user_data;
 
-    /* Set the skip_alt_lines variable */
-    auto multi_split = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(checkbox));
-    info->parse_data->set_multi_split (multi_split);
-    csv_import_refresh_preview_table (info);
+    info->parse_data->multi_split (gtk_toggle_button_get_active (checkbox));
+    csv_import_preview_refresh_table (info);
     csv_import_validate_preview_settings (info);
 }
 
@@ -825,16 +693,12 @@ void csv_import_trans_multisplit_cb (GtkWidget *checkbox, gpointer user_data)
  *
  * call back for import skip rows checkbox
  *******************************************************/
-void csv_import_trans_skiprows_cb (GtkWidget *checkbox, gpointer user_data)
+void csv_import_trans_skiprows_cb (GtkToggleButton *checkbox, gpointer user_data)
 {
     CsvImportTrans *info = (CsvImportTrans*) user_data;
 
-    /* Set the skip_alt_lines variable */
-    info->parse_data->skip_alt_lines = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(checkbox));
-
-    /* Refresh the row highlighting */
+    info->parse_data->skip_alt_lines (gtk_toggle_button_get_active (checkbox));
     row_selection_update (info);
-
     csv_import_validate_preview_settings (info);
 }
 
@@ -863,9 +727,6 @@ static GtkCellRenderer* gnc_csv_preview_get_cell_renderer (CsvImportTrans* info,
  */
 void sep_button_clicked (GtkWidget* widget, CsvImportTrans* info)
 {
-    int i;
-    const std::string stock_separator_characters(" \t,:;-");
-    std::string checked_separators;
 
     /* Only manipulate separator characters if the currently open file is
      * csv separated. */
@@ -874,10 +735,12 @@ void sep_button_clicked (GtkWidget* widget, CsvImportTrans* info)
 
     /* Add the corresponding characters to checked_separators for each
      * button that is checked. */
-    for (i = 0; i < SEP_NUM_OF_TYPES; i++)
+    std::string checked_separators;
+    const auto stock_sep_chars = std::string (" \t,:;-");
+    for (int i = 0; i < SEP_NUM_OF_TYPES; i++)
     {
         if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(info->sep_buttons[i])))
-            checked_separators += stock_separator_characters[i];
+            checked_separators += stock_sep_chars[i];
     }
 
     /* Add the custom separator if the user checked its button. */
@@ -889,8 +752,7 @@ void sep_button_clicked (GtkWidget* widget, CsvImportTrans* info)
     }
 
     /* Set the parse options using the checked_separators list. */
-    GncCsvTokenizer *csvtok = dynamic_cast<GncCsvTokenizer*>(info->parse_data->tokenizer.get());
-    csvtok->set_separators (checked_separators);
+    info->parse_data->separators (checked_separators);
 
     /* Parse the data using the new options. We don't want to reguess
      * the column types because we want to leave the user's
@@ -898,6 +760,8 @@ void sep_button_clicked (GtkWidget* widget, CsvImportTrans* info)
     try
     {
         info->parse_data->tokenize (false);
+        csv_import_preview_refresh_table (info);
+        csv_import_validate_preview_settings (info);
     }
     catch (std::range_error &e)
     {
@@ -918,25 +782,18 @@ void sep_button_clicked (GtkWidget* widget, CsvImportTrans* info)
                                          !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget)));
         return;
     }
-
-    /* If we parsed successfully, redisplay the data. */
-    csv_import_refresh_preview_table (info);
-    csv_import_validate_preview_settings (info);
 }
 
 void account_selected_cb (GtkWidget* widget, CsvImportTrans* info)
 {
 
     auto acct = gnc_account_sel_get_account( GNC_ACCOUNT_SEL(widget) );
-    info->parse_data->set_base_account(acct);
-    /* Update the preview. */
-    csv_import_refresh_preview_table (info);
-
+    info->parse_data->base_account(acct);
+    csv_import_preview_refresh_table (info);
     csv_import_validate_preview_settings (info);
 }
 
 
-
 /** Event handler for clicking one of the format type radio
  * buttons. This occurs if the format (Fixed-Width or CSV) is changed.
  * @param csv_button The "Separated" radio button
@@ -945,32 +802,15 @@ void account_selected_cb (GtkWidget* widget, CsvImportTrans* info)
 static void separated_or_fixed_selected (GtkToggleButton* csv_button, CsvImportTrans* info)
 {
     /* Set the parsing type correctly. */
-    if (gtk_toggle_button_get_active (csv_button)) /* If we're in CSV mode ... */
-    {
-        try
-        {
-            info->parse_data->file_format (GncImpFileFormat::CSV);
-            sep_button_clicked (NULL, info);
-            // Note: sep_button_clicked also handles reparsing the data, so we're done here
-            return;
-        }
-        catch (...)
-        {
-            // FIXME Handle file loading errors (possibly thrown by file_format above)
-            PWARN("Got an error during file loading");
-        }
-    }
-
-    /* So we're in fixed-width mode ... */
     try
     {
-        info->parse_data->file_format (GncImpFileFormat::FIXED_WIDTH);
+        if (gtk_toggle_button_get_active (csv_button))
+            info->parse_data->file_format (GncImpFileFormat::CSV);
+        else
+            info->parse_data->file_format (GncImpFileFormat::FIXED_WIDTH);
 
-        /* Reparse the data. */
         info->parse_data->tokenize (false);
-
-        /* Show the new data. */
-        csv_import_refresh_preview_table (info);
+        csv_import_preview_refresh_table (info);
         csv_import_validate_preview_settings (info);
     }
     catch (std::range_error &e)
@@ -1015,8 +855,13 @@ static void encoding_selected (GOCharmapSel* selector, const char* encoding,
         /* Try converting the new encoding and reparsing. */
         try
         {
-            info->parse_data->convert_encoding (encoding);
+            info->parse_data->encoding (encoding);
             info->parse_data->tokenize (false);
+
+            csv_import_preview_refresh_table (info);
+            csv_import_validate_preview_settings (info);
+
+            info->encoding_selected_called = false;
         }
         catch (...)
         {
@@ -1024,13 +869,7 @@ static void encoding_selected (GOCharmapSel* selector, const char* encoding,
             gnc_error_dialog (NULL, "%s", _("Invalid encoding selected"));
             info->encoding_selected_called = false;
             go_charmap_sel_set_encoding (selector, previous_encoding.c_str());
-            return;
         }
-
-        csv_import_refresh_preview_table (info);
-        csv_import_validate_preview_settings (info);
-
-        info->encoding_selected_called = false;
     }
     else /* If this is the first call of the function ... */
     {
@@ -1043,10 +882,9 @@ static void encoding_selected (GOCharmapSel* selector, const char* encoding,
  * @param format_selector The combo box for selecting date formats
  * @param info The display of the data being imported
  */
-static void date_format_selected (GtkComboBoxText* format_selector, CsvImportTrans* info)
+static void date_format_selected (GtkComboBox* format_selector, CsvImportTrans* info)
 {
-    info->parse_data->date_format = gtk_combo_box_get_active (GTK_COMBO_BOX(format_selector));
-
+    info->parse_data->date_format (gtk_combo_box_get_active (format_selector));
     csv_import_validate_preview_settings (info);
 }
 
@@ -1055,10 +893,9 @@ static void date_format_selected (GtkComboBoxText* format_selector, CsvImportTra
  * @param currency_selector The combo box for selecting currency formats
  * @param info The display of the data being imported
  */
-static void currency_format_selected (GtkComboBoxText* currency_selector, CsvImportTrans* info)
+static void currency_format_selected (GtkComboBox* currency_selector, CsvImportTrans* info)
 {
-    info->parse_data->currency_format = gtk_combo_box_get_active (GTK_COMBO_BOX(currency_selector));
-
+    info->parse_data->currency_format (gtk_combo_box_get_active (currency_selector));
     csv_import_validate_preview_settings (info);
 }
 
@@ -1184,7 +1021,7 @@ fixed_context_menu_handler (GnumericPopupMenuElement const *element,
         gnc_error_dialog (NULL, "%s", e.what());
         return FALSE;
     }
-    csv_import_refresh_preview_table (info);
+    csv_import_preview_refresh_table (info);
     csv_import_validate_preview_settings (info);
     return TRUE;
 }
@@ -1241,14 +1078,10 @@ fixed_context_menu (CsvImportTrans* info, GdkEventButton *event,
  */
 static void treeview_resized (GtkWidget* widget, GtkAllocation* allocation, CsvImportTrans* info)
 {
-    /* ncols is the number of columns in the data. */
-    int i, ncols = info->parse_data->column_types.size();
-
     /* Go through each column except for the last. (We don't want to set
      * the width of the last column because the user won't be able to
      * shrink the dialog back if it's expanded.) */
-
-    for (i = 0; i < ncols - 1; i++)
+    for (uint i = 0; i < info->parse_data->column_types().size() - 1; i++)
     {
         gint col_width; /* The width of the column in info->treeview. */
         GtkTreeViewColumn* pcol;
@@ -1283,85 +1116,19 @@ static void treeview_resized (GtkWidget* widget, GtkAllocation* allocation, CsvI
 static void column_type_changed (GtkCellRenderer* renderer, gchar* path,
                                 GtkTreeIter* new_text_iter, CsvImportTrans* info)
 {
-    /* ncols is the number of columns in the data. */
-    int i, ncols = info->parse_data->column_types.size();
-    /* store has the actual strings that appear in info->ctreeview. */
-    GtkTreeModel* ctstore = gtk_tree_view_get_model (info->ctreeview);
     GtkTreeModel* model;
-    gint textColumn;
-    GtkTreeIter iter;
     gchar* new_text;
     auto new_col_type = GncTransPropType::NONE;
 
     /* Get the new text */
-    g_object_get (renderer, "model", &model, "text-column", &textColumn, NULL);
+    auto col_num = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT(renderer), "col-num"));
+    g_object_get (renderer, "model", &model, NULL);
     gtk_tree_model_get (model, new_text_iter,
-            textColumn, &new_text,
-            1, &new_col_type,            // Invisible column in the combobox' model containing the column type
+            1, &new_col_type, // Invisible column in the combobox' model containing the column type
             -1);
 
-    /* Get an iterator for the first (and only) row. */
-    gtk_tree_model_get_iter_first (ctstore, &iter);
-
-    /* Go through each column. */
-    for (i = 0; i < ncols; i++)
-    {
-        /* We need all this stuff so that we can find out whether or not
-         * this was the column that was changed. */
-        /* The column in the treeview we are looking at */
-        GtkTreeViewColumn* col = gtk_tree_view_get_column (info->ctreeview, i);
-        /* The list of renderers for col */
-        GList* rend_list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT(col));
-        /* rend_list has only one entry, which we put in col_renderer. */
-        GtkCellRenderer* col_renderer = (GtkCellRenderer*) rend_list->data;
-        g_list_free (rend_list);
-
-        /* If this is not the column that was changed ... */
-        if (col_renderer != renderer)
-        {
-            /* The data type of this column */
-            auto cur_col_type = GncTransPropType::NONE;
-            /* Get the column type. Store is arranged so that every two
-             * columns is a pair of
-             * - the column type as a user visible (translated) string
-             * - the internal type for this column
-             * So store looks like:
-             * col_type_str 0, col_type, col_type_str 1, col_type 1, ..., col_type_str ncols, col_type ncols. */
-            gtk_tree_model_get(ctstore, &iter, 2 * i + 1, &cur_col_type, -1);
-            /* If this column has the same type as the user selected ... */
-            if (cur_col_type == new_col_type)
-            {
-                /* ... set this column to the "None" type. (We can't allow duplicate types.) */
-                gtk_list_store_set (GTK_LIST_STORE(ctstore), &iter,
-                        2 * i, _(gnc_csv_col_type_strs[GncTransPropType::NONE]),
-                        2 * i + 1, GncTransPropType::NONE,
-                        -1);
-                info->parse_data->column_types.at(i) = GncTransPropType::NONE;
-            }
-        }
-        else /* If this is the column that was changed ... */
-        {
-            /* Set the type for this column to what the user selected. (See
-             * comment above "Get the column type. ..." for why we set
-             * column 2*i+1 in store.) */
-            gtk_list_store_set (GTK_LIST_STORE(ctstore), &iter,
-                    2 * i, new_text,
-                    2 * i + 1, new_col_type,
-                    -1);
-            info->parse_data->column_types.at(i) = new_col_type;
-        }
-    }
-
-    /* The user can't both select a base account and an account column.
-     * So now the column_types vector is updated also update the base account
-     * selector's availability.
-     */
-    if (info->parse_data->check_for_column_type(GncTransPropType::ACCOUNT))
-    {
-        gnc_account_sel_set_account(GNC_ACCOUNT_SEL(info->acct_selector), nullptr, false);
-        info->parse_data->set_base_account(nullptr);
-    }
-
+    info->parse_data->set_column_type (col_num, new_col_type);
+    csv_import_preview_refresh_table (info);
     csv_import_validate_preview_settings (info);
 }
 
@@ -1381,7 +1148,7 @@ split_column (CsvImportTrans* info, int col, int dx)
         gnc_error_dialog (NULL, "%s", e.what());
         return;
     }
-    csv_import_refresh_preview_table (info);
+    csv_import_preview_refresh_table (info);
     csv_import_validate_preview_settings (info);
 }
 
@@ -1396,23 +1163,17 @@ split_column (CsvImportTrans* info, int col, int dx)
 static void header_button_press_handler (GtkWidget* button, GdkEventButton* event,
                                         CsvImportTrans* info)
 {
-    /* col is the number of the column that was clicked, and offset is
-       to correct for the indentation of button. */
-    int i, offset;
-    GtkAllocation alloc;
-    int col = 0, ncols = info->parse_data->column_types.size();
-
-    gtk_widget_get_allocation (gtk_bin_get_child (GTK_BIN(button)), &alloc);
-    offset = alloc.x - alloc.x;
     /* Find the column that was clicked. */
-    col = GPOINTER_TO_INT(g_object_get_data (G_OBJECT(button), "col-num"));
+    auto col = GPOINTER_TO_UINT(g_object_get_data (G_OBJECT(button), "col-num"));
 
     /* Don't let the user affect the last column if it has error messages. */
-    if (col == ncols)
-    {
+    if (col == info->parse_data->column_types().size())
         return;
-    }
 
+    /*  calculate offset to compensate for the button indentation. */
+    GtkAllocation alloc;
+    gtk_widget_get_allocation (gtk_bin_get_child (GTK_BIN(button)), &alloc);
+    auto offset = alloc.x - alloc.x;
     /* Double clicks can split columns. */
     if (event->type == GDK_2BUTTON_PRESS && event->button == 1)
     {
@@ -1468,7 +1229,6 @@ check_for_duplicates (GtkListStore *liststore, const gchar *string)
  */
 bool get_list_of_accounts (CsvImportTrans* info, GtkTreeModel *store)
 {
-    int      i, ncols = info->parse_data->column_types.size(); /* ncols is the number of columns in the data. */
     bool     have_accounts = false;
 
     /* ctstore contains the actual strings appearing in the column types treeview. */
@@ -1481,12 +1241,12 @@ bool get_list_of_accounts (CsvImportTrans* info, GtkTreeModel *store)
     /* Get an iterator for the first (and only) row of the column store. */
     gtk_tree_model_get_iter_first (ctstore, &iter1);
 
-    for (uint j = info->parse_data->skip_start_lines;
-            j < info->parse_data->parsed_lines.size() - info->parse_data->skip_end_lines - 1;
+    for (uint j = info->parse_data->skip_start_lines();
+            j < info->parse_data->parsed_lines.size() - info->parse_data->skip_end_lines() - 1;
             j++)
     {
         /* Go through each of the columns. */
-        for (i = 0; i < ncols; i++)
+        for (uint i = 0; i < info->parse_data->column_types().size(); i++)
         {
             gchar* accstr = NULL;   /* The string in this column from datastore. */
             auto col_type = GncTransPropType::NONE;
@@ -1531,10 +1291,11 @@ bool get_list_of_accounts (CsvImportTrans* info, GtkTreeModel *store)
  *
  * @param info The data being previewed
  */
-void csv_import_refresh_preview_table (CsvImportTrans* info)
+void csv_import_preview_refresh_table (CsvImportTrans* info)
 {
     /* ncols is the number of columns in the file data. */
-    auto ncols = info->parse_data->column_types.size();
+    auto column_types = info->parse_data->column_types();
+    auto ncols = column_types.size();
 
     // Set up the header liststore
 
@@ -1564,8 +1325,8 @@ void csv_import_refresh_preview_table (CsvImportTrans* info)
     for (guint i = 0; i < ncols; i++)
     {
         gtk_list_store_set (ctstore, &iter,
-                2 * i, _(gnc_csv_col_type_strs[info->parse_data->column_types[i]]),
-                2 * i + 1, static_cast<int>(info->parse_data->column_types[i]),
+                2 * i, _(gnc_csv_col_type_strs[column_types[i]]),
+                2 * i + 1, static_cast<int>(column_types[i]),
                 -1);
     }
     gtk_list_store_set (ctstore, &iter, 2 * ncols, _("Errors"), -1);
@@ -1630,7 +1391,7 @@ void csv_import_refresh_preview_table (CsvImportTrans* info)
         /* Only add column types that make sense in
          * the chosen import mode (multi-split vs two-split).
          */
-        if (sanitize_trans_prop(col_type.first, info->parse_data->multi_split) == col_type.first)
+        if (sanitize_trans_prop(col_type.first, info->parse_data->multi_split()) == col_type.first)
         {
             GtkTreeIter iter;
             gtk_list_store_append (combostore, &iter);
@@ -1650,6 +1411,8 @@ void csv_import_refresh_preview_table (CsvImportTrans* info)
         /* Set the properties for the dropdown list */
         g_object_set (G_OBJECT(crenderer), "model", combostore, "text-column", 0,
                      "editable", TRUE, "has-entry", FALSE, NULL);
+        g_object_set_data (G_OBJECT(crenderer),
+                           "col-num", GUINT_TO_POINTER(i));
         g_signal_connect (G_OBJECT(crenderer), "changed",
                          G_CALLBACK(column_type_changed), (gpointer)info);
         /* Insert the column */
@@ -1706,25 +1469,6 @@ void csv_import_refresh_preview_table (CsvImportTrans* info)
     gtk_widget_show_all (GTK_WIDGET(info->treeview));
     gtk_widget_show_all (GTK_WIDGET(info->ctreeview));
 
-    /* Update skipped lines visual feedback */
-    row_selection_update (info);
-
-    /* Set the encoding selector to the right encoding. */
-    info->code_encoding_calls = 2;
-    go_charmap_sel_set_encoding (info->encselector, info->parse_data->tokenizer->encoding().c_str());
-
-    /* Set the date format to what's in the combo box (since we don't
-     * necessarily know if this will always be the same). */
-    info->parse_data->date_format = gtk_combo_box_get_active (GTK_COMBO_BOX(info->date_format_combo));
-
-    /* Set the upper limit of spin button to number of rows */
-    GtkAdjustment *adj;
-    adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(info->end_row_spin));
-    gtk_adjustment_set_upper (adj, info->parse_data->parsed_lines.size());
-    adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(info->start_row_spin));
-    gtk_adjustment_set_upper (adj, info->parse_data->parsed_lines.size());
-    if (gtk_adjustment_get_upper (adj) != info->parse_data->parsed_lines.size())
-
     /* Update the row selection highlight */
     row_selection_update (info);
 
@@ -1740,97 +1484,6 @@ void csv_import_validate_preview_settings (CsvImportTrans* info)
     gtk_widget_set_visible (GTK_WIDGET(info->instructions_image), !error_msg.empty());
 }
 
-
-/*******************************************************
- * gnc_csv_reset_preview_setting
- *
- * Reset the widgets on the preview settings page
- *******************************************************/
-void gnc_csv_reset_preview_setting (CsvImportTrans *info, bool block)
-{
-    int i;
-    GtkAdjustment  *adj;
-
-    // Clear the fixed width entries, if any...
-    if (info->parse_data->file_format() == GncImpFileFormat::FIXED_WIDTH)
-    {
-        GncFwTokenizer *fwtok = dynamic_cast<GncFwTokenizer*>(info->parse_data->tokenizer.get());
-        fwtok->columns();
-    }
-
-    // Reset Start Row
-    adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(info->start_row_spin));
-    gtk_spin_button_set_value (GTK_SPIN_BUTTON(info->start_row_spin), 0);
-    gtk_adjustment_set_upper (adj, info->parse_data->parsed_lines.size());
-
-    // Reset End Row
-    adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(info->end_row_spin));
-    gtk_spin_button_set_value (GTK_SPIN_BUTTON(info->end_row_spin), 0);
-    gtk_adjustment_set_upper (adj, info->parse_data->parsed_lines.size());
-
-    // Reset Skip Rows
-    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->skip_rows), FALSE);
-
-    // Reset Multi-split
-    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->multi_split_cbutton), FALSE);
-
-    // Reset Import Format
-    g_signal_handlers_block_by_func (info->csv_button, (gpointer) separated_or_fixed_selected, info);
-    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->csv_button), TRUE);
-    g_signal_handlers_unblock_by_func (info->csv_button, (gpointer) separated_or_fixed_selected, info);
-
-    if (block) // We need to block these when we go back to the file page
-    {
-        g_signal_handlers_block_by_func (info->custom_cbutton, (gpointer) sep_button_clicked, info);
-        g_signal_handlers_block_by_func (info->custom_entry, (gpointer) sep_button_clicked, info);
-    }
-
-    // Reset the separators
-    for (i = 0; i < SEP_NUM_OF_TYPES; i++)
-    {
-        if (block) // We need to block these when we go back to the file page
-            g_signal_handlers_block_by_func (info->sep_buttons[i], (gpointer) sep_button_clicked, info);
-        if (i == 2)
-            gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->sep_buttons[i]), TRUE);
-        else
-            gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->sep_buttons[i]), FALSE);
-        if (block) // Now unblock
-            g_signal_handlers_unblock_by_func (info->sep_buttons[i], (gpointer) sep_button_clicked, info);
-    }
-    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->custom_cbutton), FALSE);
-    gtk_entry_set_text (GTK_ENTRY(info->custom_entry), "");
-
-    if (block) // Now unblock
-    {
-        g_signal_handlers_unblock_by_func (info->custom_cbutton, (gpointer) sep_button_clicked, info);
-        g_signal_handlers_unblock_by_func (info->custom_entry, (gpointer) sep_button_clicked, info);
-    }
-
-    // Reset the combo's and character encoding
-    gtk_combo_box_set_active (GTK_COMBO_BOX(info->date_format_combo), 0);
-    gtk_combo_box_set_active (GTK_COMBO_BOX(info->currency_format_combo), 0);
-    go_charmap_sel_set_encoding (info->encselector, "UTF-8");
-}
-
-
-/*******************************************************
- * load_settings
- *
- * load the default settings for the assistant
- *******************************************************/
-static
-void load_settings (CsvImportTrans *info)
-{
-    info->match_parse_run = false;
-    if (!info->file_name.empty())
-        info->file_name.clear();
-    if (!info->error_text.empty())
-        info->error_text.clear();
-
-    /* The default directory for the user to select files. */
-    info->starting_dir = gnc_get_default_directory (GNC_PREFS_GROUP);
-}
-
 /*======================================================================*/
 /*======================================================================*/
 
@@ -1868,8 +1521,12 @@ csv_import_trans_assistant_file_page_prepare (GtkAssistant *assistant,
     info->skip_errors = false; // Set skip_errors to False to start with.
 
     /* Set the default directory */
-    if (info->starting_dir.size())
-        gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER(info->file_chooser), info->starting_dir.c_str());
+    auto starting_dir = gnc_get_default_directory (GNC_PREFS_GROUP);
+    if (starting_dir)
+    {
+        gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER(info->file_chooser), starting_dir);
+        g_free (starting_dir);
+    }
 
     /* Disable the Forward Assistant Button */
     gtk_assistant_set_page_complete (assistant, page, FALSE);
@@ -1913,7 +1570,7 @@ csv_import_trans_assistant_preview_page_prepare (GtkAssistant *assistant,
         gtk_widget_set_sensitive (info->end_row_spin, FALSE);
         gtk_widget_set_sensitive (info->skip_rows, FALSE);
         gtk_widget_set_sensitive (info->multi_split_cbutton, FALSE);
-        info->parse_data->skip_alt_lines = FALSE;
+        info->parse_data->skip_alt_lines (false);
 
         /* Show the skip errors check button */
         gtk_widget_show (GTK_WIDGET(info->check_butt));
@@ -1934,7 +1591,7 @@ csv_import_trans_assistant_preview_page_prepare (GtkAssistant *assistant,
     gtk_assistant_set_page_complete (assistant, page, false);
 
     /* Load the data into the treeview. */
-    csv_import_refresh_preview_table (info);
+    csv_import_preview_refresh_table (info);
     csv_import_validate_preview_settings (info);
 
 }
@@ -2286,9 +1943,6 @@ csv_import_trans_assistant_summary_page_prepare (GtkAssistant *assistant,
     CsvImportTrans *info = (CsvImportTrans*) user_data;
     gchar *text, *mtext;
 
-    /* Save the Window size and directory */
-    gnc_set_default_directory (GNC_PREFS_GROUP, info->starting_dir.c_str());
-
     /* Remove the added button */
     gtk_assistant_remove_action_widget (assistant, info->help_button);
     gtk_assistant_remove_action_widget (assistant, info->cancel_button);
@@ -2517,9 +2171,6 @@ csv_import_trans_assistant_create (CsvImportTrans *info)
     window = GTK_WIDGET(gtk_builder_get_object (builder, "CSV Transaction Assistant"));
     info->window = window;
 
-    /* Load default settings */
-    load_settings (info);
-
     /* Set the forward function */
     gtk_assistant_set_forward_page_func (GTK_ASSISTANT(window), csv_import_trans_forward_page_func, info, NULL);
 
@@ -2578,7 +2229,7 @@ csv_import_trans_assistant_create (CsvImportTrans *info)
         gtk_combo_box_set_active (GTK_COMBO_BOX(info->settings_combo), 0);
 
         info->combo_hbox = GTK_WIDGET(gtk_builder_get_object (builder, "combo_hbox"));
-        gtk_box_pack_start (GTK_BOX(info->combo_hbox), info->settings_combo, FALSE, FALSE, 6);
+        gtk_box_pack_start (GTK_BOX(info->combo_hbox), info->settings_combo, true, true, 6);
         gtk_widget_show (info->settings_combo);
 
         g_signal_connect (G_OBJECT(info->settings_combo), "changed",
@@ -2590,17 +2241,11 @@ csv_import_trans_assistant_create (CsvImportTrans *info)
                          G_CALLBACK(csv_import_trans_changed_settings_text_cb), (gpointer)info);
 
         // Add Save Settings button
-        info->save_button = gtk_button_new_with_label (_("Save Settings"));
-        gtk_box_pack_start (GTK_BOX(info->combo_hbox), info->save_button, FALSE, FALSE, 6);
-        gtk_widget_show (info->save_button);
-
-        g_signal_connect (G_OBJECT(info->save_button), "clicked",
-                         G_CALLBACK(csv_import_trans_save_settings_cb), (gpointer)info);
+        info->save_button = GTK_WIDGET(gtk_builder_get_object (builder, "save_settings"));
 
         // Add Delete Settings button
-        info->del_button = gtk_button_new_with_label (_("Delete Settings"));
-        gtk_box_pack_start (GTK_BOX(info->combo_hbox), info->del_button, FALSE, FALSE, 6);
-        gtk_widget_show (info->del_button);
+        info->del_button = GTK_WIDGET(gtk_builder_get_object (builder, "delete_settings"));
+
 
         g_signal_connect (G_OBJECT(info->del_button), "clicked",
                          G_CALLBACK(csv_import_trans_delete_settings_cb), (gpointer)info);
diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.glade b/src/import-export/csv-imp/assistant-csv-trans-import.glade
index 3b1c172..4c20ee8 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.glade
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.glade
@@ -12,16 +12,6 @@
       <column type="gpointer"/>
     </columns>
   </object>
-  <object class="GtkAdjustment" id="end_row_adj">
-    <property name="upper">1000</property>
-    <property name="step_increment">1</property>
-    <property name="page_increment">10</property>
-  </object>
-  <object class="GtkAdjustment" id="start_row_adj">
-    <property name="upper">1000</property>
-    <property name="step_increment">1</property>
-    <property name="page_increment">10</property>
-  </object>
   <object class="GtkAssistant" id="CSV Transaction Assistant">
     <property name="can_focus">False</property>
     <property name="border_width">12</property>
@@ -90,644 +80,659 @@ Select location and file name for the Import, then click 'OK'...
         <property name="border_width">12</property>
         <property name="spacing">2</property>
         <child>
-          <object class="GtkHBox" id="hbox1">
+          <object class="GtkTable" id="table1">
             <property name="visible">True</property>
             <property name="can_focus">False</property>
+            <property name="n_rows">2</property>
+            <property name="n_columns">3</property>
+            <property name="column_spacing">5</property>
+            <property name="row_spacing">5</property>
             <child>
-              <object class="GtkVBox" id="vbox1">
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <object class="GtkFrame" id="frame6">
                 <property name="visible">True</property>
                 <property name="can_focus">False</property>
-                <property name="spacing">5</property>
+                <property name="label_xalign">0</property>
+                <property name="shadow_type">in</property>
                 <child>
-                  <object class="GtkFrame" id="frame6">
+                  <object class="GtkAlignment" id="alignment4">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
-                    <property name="label_xalign">0</property>
-                    <property name="shadow_type">in</property>
+                    <property name="bottom_padding">5</property>
+                    <property name="left_padding">5</property>
+                    <property name="right_padding">5</property>
                     <child>
-                      <object class="GtkAlignment" id="alignment4">
+                      <object class="GtkHBox" id="combo_hbox">
                         <property name="visible">True</property>
                         <property name="can_focus">False</property>
-                        <property name="bottom_padding">5</property>
-                        <property name="left_padding">5</property>
-                        <property name="right_padding">5</property>
                         <child>
-                          <object class="GtkHBox" id="combo_hbox">
+                          <object class="GtkButton" id="delete_settings">
                             <property name="visible">True</property>
-                            <property name="can_focus">False</property>
-                            <child>
-                              <placeholder/>
-                            </child>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">True</property>
+                            <property name="tooltip_text" translatable="yes">Delete Settings</property>
+                            <signal name="clicked" handler="csv_import_trans_delete_settings_cb" swapped="no"/>
                             <child>
-                              <placeholder/>
+                              <object class="GtkImage" id="image2">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="stock">gtk-delete</property>
+                              </object>
                             </child>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="pack_type">end</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <placeholder/>
+                        </child>
+                        <child>
+                          <object class="GtkButton" id="save_settings">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">True</property>
+                            <property name="tooltip_text" translatable="yes">Save Settings</property>
+                            <signal name="clicked" handler="csv_import_trans_save_settings_cb" swapped="no"/>
                             <child>
-                              <placeholder/>
+                              <object class="GtkImage" id="image1">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="stock">gtk-save</property>
+                              </object>
                             </child>
                           </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="pack_type">end</property>
+                            <property name="position">2</property>
+                          </packing>
                         </child>
                       </object>
                     </child>
-                    <child type="label">
-                      <object class="GtkLabel" id="label12">
+                  </object>
+                </child>
+                <child type="label">
+                  <object class="GtkLabel" id="label12">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes"> <b>Load and Save Settings</b></property>
+                    <property name="use_markup">True</property>
+                    <property name="track_visited_links">False</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="x_options">GTK_FILL</property>
+                <property name="y_options">GTK_FILL</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkFrame" id="frame10">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                <property name="label_xalign">0</property>
+                <property name="shadow_type">none</property>
+                <child>
+                  <object class="GtkAlignment" id="alignment6">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                    <property name="left_padding">5</property>
+                    <property name="right_padding">5</property>
+                    <child>
+                      <object class="GtkTable" id="table2">
                         <property name="visible">True</property>
                         <property name="can_focus">False</property>
-                        <property name="label" translatable="yes"> <b>Load and Save Settings</b></property>
-                        <property name="use_markup">True</property>
-                        <property name="track_visited_links">False</property>
+                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                        <property name="n_rows">7</property>
+                        <property name="n_columns">3</property>
+                        <property name="column_spacing">3</property>
+                        <child>
+                          <object class="GtkCheckButton" id="space_cbutton">
+                            <property name="label" translatable="yes">Space</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">False</property>
+                            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                            <property name="draw_indicator">True</property>
+                          </object>
+                          <packing>
+                            <property name="top_attach">2</property>
+                            <property name="bottom_attach">3</property>
+                            <property name="x_options">GTK_FILL</property>
+                            <property name="y_options">GTK_FILL</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkCheckButton" id="tab_cbutton">
+                            <property name="label" translatable="yes">Tab</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">False</property>
+                            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                            <property name="draw_indicator">True</property>
+                          </object>
+                          <packing>
+                            <property name="left_attach">1</property>
+                            <property name="right_attach">2</property>
+                            <property name="top_attach">2</property>
+                            <property name="bottom_attach">3</property>
+                            <property name="x_options">GTK_FILL</property>
+                            <property name="y_options">GTK_FILL</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkCheckButton" id="comma_cbutton">
+                            <property name="label" translatable="yes">Comma (,)</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">False</property>
+                            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                            <property name="active">True</property>
+                            <property name="draw_indicator">True</property>
+                          </object>
+                          <packing>
+                            <property name="left_attach">2</property>
+                            <property name="right_attach">3</property>
+                            <property name="top_attach">2</property>
+                            <property name="bottom_attach">3</property>
+                            <property name="x_options">GTK_FILL</property>
+                            <property name="y_options">GTK_FILL</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkCheckButton" id="colon_cbutton">
+                            <property name="label" translatable="yes">Colon (:)</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">False</property>
+                            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                            <property name="draw_indicator">True</property>
+                          </object>
+                          <packing>
+                            <property name="top_attach">3</property>
+                            <property name="bottom_attach">4</property>
+                            <property name="x_options">GTK_FILL</property>
+                            <property name="y_options">GTK_FILL</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkCheckButton" id="semicolon_cbutton">
+                            <property name="label" translatable="yes">Semicolon (;)</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">False</property>
+                            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                            <property name="draw_indicator">True</property>
+                          </object>
+                          <packing>
+                            <property name="left_attach">1</property>
+                            <property name="right_attach">2</property>
+                            <property name="top_attach">3</property>
+                            <property name="bottom_attach">4</property>
+                            <property name="x_options">GTK_FILL</property>
+                            <property name="y_options">GTK_FILL</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkCheckButton" id="hyphen_cbutton">
+                            <property name="label" translatable="yes">Hyphen (-)</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">False</property>
+                            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                            <property name="draw_indicator">True</property>
+                          </object>
+                          <packing>
+                            <property name="left_attach">2</property>
+                            <property name="right_attach">3</property>
+                            <property name="top_attach">3</property>
+                            <property name="bottom_attach">4</property>
+                            <property name="x_options">GTK_FILL</property>
+                            <property name="y_options">GTK_FILL</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkCheckButton" id="custom_cbutton">
+                            <property name="label" translatable="yes">Custom</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">False</property>
+                            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                            <property name="draw_indicator">True</property>
+                          </object>
+                          <packing>
+                            <property name="top_attach">4</property>
+                            <property name="bottom_attach">5</property>
+                            <property name="x_options">GTK_FILL</property>
+                            <property name="y_options">GTK_FILL</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkEntry" id="custom_entry">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                            <property name="invisible_char">●</property>
+                            <property name="invisible_char_set">True</property>
+                            <property name="primary_icon_activatable">False</property>
+                            <property name="secondary_icon_activatable">False</property>
+                            <property name="primary_icon_sensitive">True</property>
+                            <property name="secondary_icon_sensitive">True</property>
+                          </object>
+                          <packing>
+                            <property name="left_attach">1</property>
+                            <property name="right_attach">3</property>
+                            <property name="top_attach">4</property>
+                            <property name="bottom_attach">5</property>
+                            <property name="x_options">GTK_FILL</property>
+                            <property name="y_options">GTK_FILL</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkRadioButton" id="csv_button">
+                            <property name="label" translatable="yes">Separators</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">False</property>
+                            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                            <property name="active">True</property>
+                            <property name="draw_indicator">True</property>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkRadioButton" id="fixed_button">
+                            <property name="label" translatable="yes">Fixed-Width</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">False</property>
+                            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                            <property name="draw_indicator">True</property>
+                            <property name="group">csv_button</property>
+                          </object>
+                          <packing>
+                            <property name="left_attach">1</property>
+                            <property name="right_attach">2</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkHSeparator" id="hseparator1">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                          </object>
+                          <packing>
+                            <property name="right_attach">3</property>
+                            <property name="top_attach">1</property>
+                            <property name="bottom_attach">2</property>
+                            <property name="y_options">GTK_FILL</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkHSeparator" id="hseparator4">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                          </object>
+                          <packing>
+                            <property name="right_attach">3</property>
+                            <property name="top_attach">5</property>
+                            <property name="bottom_attach">6</property>
+                            <property name="y_options">GTK_FILL</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkCheckButton" id="multi_split_button">
+                            <property name="label" translatable="yes">Multi-split</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">False</property>
+                            <property name="tooltip_text" translatable="yes">Normally the importer will assume each line in the input file will correspond to one transaction. Each line can have information for one transaction and one or two splits.
+
+When Multi-split is enabled the importer will assume multiple consecutive lines together hold the information for one transaction. Each line provides information for exactly one split. The first line should also provide the information for the transaction.
+To know which lines belong to the same transaction, the importer will compare the provided transaction information in each line. If that information is empty or the same as the first transaction line the importer will consider this line part of the same transaction.</property>
+                            <property name="draw_indicator">True</property>
+                            <signal name="toggled" handler="csv_import_trans_multisplit_cb" swapped="no"/>
+                          </object>
+                          <packing>
+                            <property name="right_attach">3</property>
+                            <property name="top_attach">6</property>
+                            <property name="bottom_attach">7</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <placeholder/>
+                        </child>
                       </object>
                     </child>
                   </object>
-                  <packing>
-                    <property name="expand">True</property>
-                    <property name="fill">True</property>
-                    <property name="position">0</property>
-                  </packing>
                 </child>
+                <child type="label">
+                  <object class="GtkLabel" id="label19">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                    <property name="label" translatable="yes"><b>File Format</b></property>
+                    <property name="use_markup">True</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="top_attach">1</property>
+                <property name="bottom_attach">2</property>
+                <property name="x_options">GTK_FILL</property>
+                <property name="y_options">GTK_FILL</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkFrame" id="frame8">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="label_xalign">0</property>
+                <property name="shadow_type">none</property>
                 <child>
-                  <object class="GtkHBox" id="hbox8">
+                  <object class="GtkAlignment" id="alignment8">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
-                    <property name="spacing">5</property>
+                    <property name="left_padding">5</property>
+                    <property name="right_padding">5</property>
                     <child>
-                      <object class="GtkVBox" id="vbox2">
+                      <object class="GtkVBox" id="vbox6">
                         <property name="visible">True</property>
                         <property name="can_focus">False</property>
+                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
                         <child>
-                          <object class="GtkFrame" id="frame10">
+                          <object class="GtkTable" id="table3">
                             <property name="visible">True</property>
                             <property name="can_focus">False</property>
-                            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                            <property name="label_xalign">0</property>
-                            <property name="shadow_type">none</property>
+                            <property name="n_rows">6</property>
+                            <property name="n_columns">2</property>
+                            <property name="column_spacing">5</property>
+                            <property name="row_spacing">5</property>
                             <child>
-                              <object class="GtkAlignment" id="alignment6">
+                              <object class="GtkAlignment" id="date_format_container">
                                 <property name="visible">True</property>
                                 <property name="can_focus">False</property>
                                 <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                                <property name="left_padding">5</property>
-                                <property name="right_padding">5</property>
-                                <child>
-                                  <object class="GtkTable" id="table2">
-                                    <property name="visible">True</property>
-                                    <property name="can_focus">False</property>
-                                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                                    <property name="n_rows">7</property>
-                                    <property name="n_columns">3</property>
-                                    <property name="column_spacing">3</property>
-                                    <child>
-                                      <object class="GtkCheckButton" id="space_cbutton">
-                                        <property name="label" translatable="yes">Space</property>
-                                        <property name="visible">True</property>
-                                        <property name="can_focus">True</property>
-                                        <property name="receives_default">False</property>
-                                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                                        <property name="draw_indicator">True</property>
-                                      </object>
-                                      <packing>
-                                        <property name="top_attach">2</property>
-                                        <property name="bottom_attach">3</property>
-                                        <property name="x_options">GTK_FILL</property>
-                                        <property name="y_options">GTK_FILL</property>
-                                      </packing>
-                                    </child>
-                                    <child>
-                                      <object class="GtkCheckButton" id="tab_cbutton">
-                                        <property name="label" translatable="yes">Tab</property>
-                                        <property name="visible">True</property>
-                                        <property name="can_focus">True</property>
-                                        <property name="receives_default">False</property>
-                                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                                        <property name="draw_indicator">True</property>
-                                      </object>
-                                      <packing>
-                                        <property name="left_attach">1</property>
-                                        <property name="right_attach">2</property>
-                                        <property name="top_attach">2</property>
-                                        <property name="bottom_attach">3</property>
-                                        <property name="x_options">GTK_FILL</property>
-                                        <property name="y_options">GTK_FILL</property>
-                                      </packing>
-                                    </child>
-                                    <child>
-                                      <object class="GtkCheckButton" id="comma_cbutton">
-                                        <property name="label" translatable="yes">Comma (,)</property>
-                                        <property name="visible">True</property>
-                                        <property name="can_focus">True</property>
-                                        <property name="receives_default">False</property>
-                                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                                        <property name="active">True</property>
-                                        <property name="draw_indicator">True</property>
-                                      </object>
-                                      <packing>
-                                        <property name="left_attach">2</property>
-                                        <property name="right_attach">3</property>
-                                        <property name="top_attach">2</property>
-                                        <property name="bottom_attach">3</property>
-                                        <property name="x_options">GTK_FILL</property>
-                                        <property name="y_options">GTK_FILL</property>
-                                      </packing>
-                                    </child>
-                                    <child>
-                                      <object class="GtkCheckButton" id="colon_cbutton">
-                                        <property name="label" translatable="yes">Colon (:)</property>
-                                        <property name="visible">True</property>
-                                        <property name="can_focus">True</property>
-                                        <property name="receives_default">False</property>
-                                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                                        <property name="draw_indicator">True</property>
-                                      </object>
-                                      <packing>
-                                        <property name="top_attach">3</property>
-                                        <property name="bottom_attach">4</property>
-                                        <property name="x_options">GTK_FILL</property>
-                                        <property name="y_options">GTK_FILL</property>
-                                      </packing>
-                                    </child>
-                                    <child>
-                                      <object class="GtkCheckButton" id="semicolon_cbutton">
-                                        <property name="label" translatable="yes">Semicolon (;)</property>
-                                        <property name="visible">True</property>
-                                        <property name="can_focus">True</property>
-                                        <property name="receives_default">False</property>
-                                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                                        <property name="draw_indicator">True</property>
-                                      </object>
-                                      <packing>
-                                        <property name="left_attach">1</property>
-                                        <property name="right_attach">2</property>
-                                        <property name="top_attach">3</property>
-                                        <property name="bottom_attach">4</property>
-                                        <property name="x_options">GTK_FILL</property>
-                                        <property name="y_options">GTK_FILL</property>
-                                      </packing>
-                                    </child>
-                                    <child>
-                                      <object class="GtkCheckButton" id="hyphen_cbutton">
-                                        <property name="label" translatable="yes">Hyphen (-)</property>
-                                        <property name="visible">True</property>
-                                        <property name="can_focus">True</property>
-                                        <property name="receives_default">False</property>
-                                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                                        <property name="draw_indicator">True</property>
-                                      </object>
-                                      <packing>
-                                        <property name="left_attach">2</property>
-                                        <property name="right_attach">3</property>
-                                        <property name="top_attach">3</property>
-                                        <property name="bottom_attach">4</property>
-                                        <property name="x_options">GTK_FILL</property>
-                                        <property name="y_options">GTK_FILL</property>
-                                      </packing>
-                                    </child>
-                                    <child>
-                                      <object class="GtkCheckButton" id="custom_cbutton">
-                                        <property name="label" translatable="yes">Custom</property>
-                                        <property name="visible">True</property>
-                                        <property name="can_focus">True</property>
-                                        <property name="receives_default">False</property>
-                                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                                        <property name="draw_indicator">True</property>
-                                      </object>
-                                      <packing>
-                                        <property name="top_attach">4</property>
-                                        <property name="bottom_attach">5</property>
-                                        <property name="x_options">GTK_FILL</property>
-                                        <property name="y_options">GTK_FILL</property>
-                                      </packing>
-                                    </child>
-                                    <child>
-                                      <object class="GtkEntry" id="custom_entry">
-                                        <property name="visible">True</property>
-                                        <property name="can_focus">True</property>
-                                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                                        <property name="invisible_char">●</property>
-                                        <property name="invisible_char_set">True</property>
-                                        <property name="primary_icon_activatable">False</property>
-                                        <property name="secondary_icon_activatable">False</property>
-                                        <property name="primary_icon_sensitive">True</property>
-                                        <property name="secondary_icon_sensitive">True</property>
-                                      </object>
-                                      <packing>
-                                        <property name="left_attach">1</property>
-                                        <property name="right_attach">3</property>
-                                        <property name="top_attach">4</property>
-                                        <property name="bottom_attach">5</property>
-                                        <property name="x_options">GTK_FILL</property>
-                                        <property name="y_options">GTK_FILL</property>
-                                      </packing>
-                                    </child>
-                                    <child>
-                                      <object class="GtkRadioButton" id="csv_button">
-                                        <property name="label" translatable="yes">Separators</property>
-                                        <property name="visible">True</property>
-                                        <property name="can_focus">True</property>
-                                        <property name="receives_default">False</property>
-                                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                                        <property name="active">True</property>
-                                        <property name="draw_indicator">True</property>
-                                      </object>
-                                    </child>
-                                    <child>
-                                      <object class="GtkRadioButton" id="fixed_button">
-                                        <property name="label" translatable="yes">Fixed-Width</property>
-                                        <property name="visible">True</property>
-                                        <property name="can_focus">True</property>
-                                        <property name="receives_default">False</property>
-                                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                                        <property name="draw_indicator">True</property>
-                                        <property name="group">csv_button</property>
-                                      </object>
-                                      <packing>
-                                        <property name="left_attach">1</property>
-                                        <property name="right_attach">2</property>
-                                      </packing>
-                                    </child>
-                                    <child>
-                                      <object class="GtkHSeparator" id="hseparator1">
-                                        <property name="visible">True</property>
-                                        <property name="can_focus">False</property>
-                                      </object>
-                                      <packing>
-                                        <property name="right_attach">3</property>
-                                        <property name="top_attach">1</property>
-                                        <property name="bottom_attach">2</property>
-                                        <property name="y_options">GTK_FILL</property>
-                                      </packing>
-                                    </child>
-                                    <child>
-                                      <object class="GtkHSeparator" id="hseparator4">
-                                        <property name="visible">True</property>
-                                        <property name="can_focus">False</property>
-                                      </object>
-                                      <packing>
-                                        <property name="right_attach">3</property>
-                                        <property name="top_attach">5</property>
-                                        <property name="bottom_attach">6</property>
-                                        <property name="y_options">GTK_FILL</property>
-                                      </packing>
-                                    </child>
-                                    <child>
-                                      <object class="GtkCheckButton" id="multi_split_button">
-                                        <property name="label" translatable="yes">Multi-split</property>
-                                        <property name="visible">True</property>
-                                        <property name="can_focus">True</property>
-                                        <property name="receives_default">False</property>
-                                        <property name="tooltip_text" translatable="yes">Normally the importer will assume each line in the input file will correspond to one transaction. Each line can have information for one transaction and one or two splits.
-
-When Multi-split is enabled the importer will assume multiple consecutive lines together hold the information for one transaction. Each line provides information for exactly one split. The first line should also provide the information for the transaction.
-To know which lines belong to the same transaction, the importer will compare the provided transaction information in each line. If that information is empty or the same as the first transaction line the importer will consider this line part of the same transaction.</property>
-                                        <property name="draw_indicator">True</property>
-                                        <signal name="toggled" handler="csv_import_trans_multisplit_cb" swapped="no"/>
-                                      </object>
-                                      <packing>
-                                        <property name="right_attach">3</property>
-                                        <property name="top_attach">6</property>
-                                        <property name="bottom_attach">7</property>
-                                      </packing>
-                                    </child>
-                                    <child>
-                                      <placeholder/>
-                                    </child>
-                                  </object>
-                                </child>
+                                <property name="xalign">0</property>
                               </object>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="right_attach">2</property>
+                                <property name="top_attach">1</property>
+                                <property name="bottom_attach">2</property>
+                                <property name="x_options">GTK_FILL</property>
+                              </packing>
                             </child>
-                            <child type="label">
-                              <object class="GtkLabel" id="label19">
+                            <child>
+                              <object class="GtkLabel" id="label20">
                                 <property name="visible">True</property>
                                 <property name="can_focus">False</property>
-                                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                                <property name="label" translatable="yes"><b>File Format</b></property>
-                                <property name="use_markup">True</property>
+                                <property name="xalign">0</property>
+                                <property name="label" translatable="yes">Date Format</property>
                               </object>
+                              <packing>
+                                <property name="top_attach">1</property>
+                                <property name="bottom_attach">2</property>
+                                <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+                              </packing>
                             </child>
-                          </object>
-                          <packing>
-                            <property name="expand">True</property>
-                            <property name="fill">True</property>
-                            <property name="position">0</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <object class="GtkFrame" id="frame1">
-                            <property name="visible">True</property>
-                            <property name="can_focus">False</property>
-                            <property name="label_xalign">0</property>
-                            <property name="shadow_type">none</property>
                             <child>
-                              <object class="GtkAlignment" id="alignment1">
+                              <object class="GtkAlignment" id="currency_format_container">
                                 <property name="visible">True</property>
                                 <property name="can_focus">False</property>
-                                <property name="left_padding">5</property>
-                                <child>
-                                  <object class="GtkHBox" id="account_hbox">
-                                    <property name="visible">True</property>
-                                    <property name="can_focus">False</property>
-                                    <child>
-                                      <placeholder/>
-                                    </child>
-                                  </object>
-                                </child>
+                                <property name="xalign">0</property>
                               </object>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="right_attach">2</property>
+                                <property name="top_attach">2</property>
+                                <property name="bottom_attach">3</property>
+                                <property name="x_options">GTK_FILL</property>
+                              </packing>
                             </child>
-                            <child type="label">
-                              <object class="GtkLabel" id="label1">
+                            <child>
+                              <object class="GtkLabel" id="label21">
                                 <property name="visible">True</property>
                                 <property name="can_focus">False</property>
-                                <property name="label" translatable="yes"><b>Account</b></property>
-                                <property name="use_markup">True</property>
+                                <property name="xalign">0</property>
+                                <property name="label" translatable="yes">Currency Format</property>
                               </object>
+                              <packing>
+                                <property name="top_attach">2</property>
+                                <property name="bottom_attach">3</property>
+                                <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+                              </packing>
                             </child>
-                          </object>
-                          <packing>
-                            <property name="expand">True</property>
-                            <property name="fill">True</property>
-                            <property name="position">1</property>
-                          </packing>
-                        </child>
-                      </object>
-                      <packing>
-                        <property name="expand">True</property>
-                        <property name="fill">True</property>
-                        <property name="position">0</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <object class="GtkFrame" id="frame8">
-                        <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <property name="label_xalign">0</property>
-                        <property name="shadow_type">none</property>
-                        <child>
-                          <object class="GtkAlignment" id="alignment8">
-                            <property name="visible">True</property>
-                            <property name="can_focus">False</property>
-                            <property name="left_padding">5</property>
-                            <property name="right_padding">5</property>
                             <child>
-                              <object class="GtkVBox" id="vbox6">
+                              <object class="GtkLabel" id="label16">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="xalign">0</property>
+                                <property name="label" translatable="yes">Encoding</property>
+                              </object>
+                              <packing>
+                                <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkAlignment" id="encoding_container">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="xalign">0</property>
+                              </object>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="right_attach">2</property>
+                                <property name="x_options">GTK_FILL</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkLabel" id="label17">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="xalign">0</property>
+                                <property name="label" translatable="yes">Leading Lines to Skip</property>
+                              </object>
+                              <packing>
+                                <property name="top_attach">4</property>
+                                <property name="bottom_attach">5</property>
+                                <property name="x_options">GTK_FILL</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkLabel" id="label18">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="xalign">0</property>
+                                <property name="label" translatable="yes">Trailing Lines to Skip</property>
+                              </object>
+                              <packing>
+                                <property name="top_attach">5</property>
+                                <property name="bottom_attach">6</property>
+                                <property name="x_options">GTK_FILL</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkHBox" id="hbox2">
                                 <property name="visible">True</property>
                                 <property name="can_focus">False</property>
-                                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
                                 <child>
-                                  <object class="GtkTable" id="table3">
+                                  <object class="GtkSpinButton" id="start_row">
                                     <property name="visible">True</property>
-                                    <property name="can_focus">False</property>
-                                    <property name="n_rows">6</property>
-                                    <property name="n_columns">2</property>
-                                    <property name="column_spacing">5</property>
-                                    <property name="row_spacing">5</property>
-                                    <child>
-                                      <object class="GtkAlignment" id="date_format_container">
-                                        <property name="visible">True</property>
-                                        <property name="can_focus">False</property>
-                                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                                        <property name="xalign">0</property>
-                                      </object>
-                                      <packing>
-                                        <property name="left_attach">1</property>
-                                        <property name="right_attach">2</property>
-                                        <property name="top_attach">1</property>
-                                        <property name="bottom_attach">2</property>
-                                        <property name="x_options">GTK_FILL</property>
-                                      </packing>
-                                    </child>
-                                    <child>
-                                      <object class="GtkLabel" id="label20">
-                                        <property name="visible">True</property>
-                                        <property name="can_focus">False</property>
-                                        <property name="xalign">0</property>
-                                        <property name="label" translatable="yes">Date Format</property>
-                                      </object>
-                                      <packing>
-                                        <property name="top_attach">1</property>
-                                        <property name="bottom_attach">2</property>
-                                        <property name="x_options">GTK_SHRINK | GTK_FILL</property>
-                                      </packing>
-                                    </child>
-                                    <child>
-                                      <object class="GtkAlignment" id="currency_format_container">
-                                        <property name="visible">True</property>
-                                        <property name="can_focus">False</property>
-                                        <property name="xalign">0</property>
-                                      </object>
-                                      <packing>
-                                        <property name="left_attach">1</property>
-                                        <property name="right_attach">2</property>
-                                        <property name="top_attach">2</property>
-                                        <property name="bottom_attach">3</property>
-                                        <property name="x_options">GTK_FILL</property>
-                                      </packing>
-                                    </child>
-                                    <child>
-                                      <object class="GtkLabel" id="label21">
-                                        <property name="visible">True</property>
-                                        <property name="can_focus">False</property>
-                                        <property name="xalign">0</property>
-                                        <property name="label" translatable="yes">Currency Format</property>
-                                      </object>
-                                      <packing>
-                                        <property name="top_attach">2</property>
-                                        <property name="bottom_attach">3</property>
-                                        <property name="x_options">GTK_SHRINK | GTK_FILL</property>
-                                      </packing>
-                                    </child>
-                                    <child>
-                                      <object class="GtkLabel" id="label16">
-                                        <property name="visible">True</property>
-                                        <property name="can_focus">False</property>
-                                        <property name="xalign">0</property>
-                                        <property name="label" translatable="yes">Encoding</property>
-                                      </object>
-                                      <packing>
-                                        <property name="x_options">GTK_SHRINK | GTK_FILL</property>
-                                      </packing>
-                                    </child>
-                                    <child>
-                                      <object class="GtkAlignment" id="encoding_container">
-                                        <property name="visible">True</property>
-                                        <property name="can_focus">False</property>
-                                        <property name="xalign">0</property>
-                                      </object>
-                                      <packing>
-                                        <property name="left_attach">1</property>
-                                        <property name="right_attach">2</property>
-                                        <property name="x_options">GTK_FILL</property>
-                                      </packing>
-                                    </child>
-                                    <child>
-                                      <object class="GtkLabel" id="label17">
-                                        <property name="visible">True</property>
-                                        <property name="can_focus">False</property>
-                                        <property name="xalign">0</property>
-                                        <property name="label" translatable="yes">Leading Lines to Skip</property>
-                                      </object>
-                                      <packing>
-                                        <property name="top_attach">4</property>
-                                        <property name="bottom_attach">5</property>
-                                        <property name="x_options">GTK_FILL</property>
-                                      </packing>
-                                    </child>
-                                    <child>
-                                      <object class="GtkLabel" id="label18">
-                                        <property name="visible">True</property>
-                                        <property name="can_focus">False</property>
-                                        <property name="xalign">0</property>
-                                        <property name="label" translatable="yes">Trailing Lines to Skip</property>
-                                      </object>
-                                      <packing>
-                                        <property name="top_attach">5</property>
-                                        <property name="bottom_attach">6</property>
-                                        <property name="x_options">GTK_FILL</property>
-                                      </packing>
-                                    </child>
-                                    <child>
-                                      <object class="GtkHBox" id="hbox2">
-                                        <property name="visible">True</property>
-                                        <property name="can_focus">False</property>
-                                        <child>
-                                          <object class="GtkSpinButton" id="start_row">
-                                            <property name="visible">True</property>
-                                            <property name="can_focus">True</property>
-                                            <property name="invisible_char">●</property>
-                                            <property name="invisible_char_set">True</property>
-                                            <property name="primary_icon_activatable">False</property>
-                                            <property name="secondary_icon_activatable">False</property>
-                                            <property name="primary_icon_sensitive">True</property>
-                                            <property name="secondary_icon_sensitive">True</property>
-                                            <property name="adjustment">start_row_adj</property>
-                                            <property name="numeric">True</property>
-                                            <signal name="value-changed" handler="csv_import_trans_srow_cb" swapped="no"/>
-                                          </object>
-                                          <packing>
-                                            <property name="expand">False</property>
-                                            <property name="fill">False</property>
-                                            <property name="position">0</property>
-                                          </packing>
-                                        </child>
-                                        <child>
-                                          <placeholder/>
-                                        </child>
-                                      </object>
-                                      <packing>
-                                        <property name="left_attach">1</property>
-                                        <property name="right_attach">2</property>
-                                        <property name="top_attach">4</property>
-                                        <property name="bottom_attach">5</property>
-                                      </packing>
-                                    </child>
-                                    <child>
-                                      <object class="GtkHBox" id="hbox3">
-                                        <property name="visible">True</property>
-                                        <property name="can_focus">False</property>
-                                        <child>
-                                          <object class="GtkSpinButton" id="end_row">
-                                            <property name="visible">True</property>
-                                            <property name="can_focus">True</property>
-                                            <property name="invisible_char">●</property>
-                                            <property name="invisible_char_set">True</property>
-                                            <property name="primary_icon_activatable">False</property>
-                                            <property name="secondary_icon_activatable">False</property>
-                                            <property name="primary_icon_sensitive">True</property>
-                                            <property name="secondary_icon_sensitive">True</property>
-                                            <property name="adjustment">end_row_adj</property>
-                                            <property name="numeric">True</property>
-                                            <signal name="value-changed" handler="csv_import_trans_erow_cb" swapped="no"/>
-                                          </object>
-                                          <packing>
-                                            <property name="expand">False</property>
-                                            <property name="fill">False</property>
-                                            <property name="position">0</property>
-                                          </packing>
-                                        </child>
-                                        <child>
-                                          <placeholder/>
-                                        </child>
-                                      </object>
-                                      <packing>
-                                        <property name="left_attach">1</property>
-                                        <property name="right_attach">2</property>
-                                        <property name="top_attach">5</property>
-                                        <property name="bottom_attach">6</property>
-                                      </packing>
-                                    </child>
-                                    <child>
-                                      <object class="GtkHSeparator" id="hseparator2">
-                                        <property name="visible">True</property>
-                                        <property name="can_focus">False</property>
-                                      </object>
-                                      <packing>
-                                        <property name="right_attach">2</property>
-                                        <property name="top_attach">3</property>
-                                        <property name="bottom_attach">4</property>
-                                      </packing>
-                                    </child>
+                                    <property name="can_focus">True</property>
+                                    <property name="invisible_char">●</property>
+                                    <property name="invisible_char_set">True</property>
+                                    <property name="primary_icon_activatable">False</property>
+                                    <property name="secondary_icon_activatable">False</property>
+                                    <property name="primary_icon_sensitive">True</property>
+                                    <property name="secondary_icon_sensitive">True</property>
+                                    <property name="adjustment">start_row_adj</property>
+                                    <property name="numeric">True</property>
+                                    <signal name="value-changed" handler="csv_import_trans_srow_cb" swapped="no"/>
                                   </object>
                                   <packing>
                                     <property name="expand">False</property>
-                                    <property name="fill">True</property>
+                                    <property name="fill">False</property>
                                     <property name="position">0</property>
                                   </packing>
                                 </child>
                                 <child>
-                                  <object class="GtkCheckButton" id="skip_rows">
-                                    <property name="label" translatable="yes">Skip alternate lines</property>
+                                  <placeholder/>
+                                </child>
+                              </object>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="right_attach">2</property>
+                                <property name="top_attach">4</property>
+                                <property name="bottom_attach">5</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkHBox" id="hbox3">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <child>
+                                  <object class="GtkSpinButton" id="end_row">
                                     <property name="visible">True</property>
                                     <property name="can_focus">True</property>
-                                    <property name="receives_default">False</property>
-                                    <property name="tooltip_text" translatable="yes">Starting from the first line that is actually imported every second line will be skipped. This option will take the leading lines to skip into account as well.
-For example
-* if 'Leading Lines to Skip' is set to 3, the first line to import will be line 4. Lines 5, 7, 9,... will be skipped.
-* if 'Leading Lines to Skip' is set to 4, the first line to import will be line 5. Lines 6, 8, 10,... will be skipped.</property>
-                                    <property name="draw_indicator">True</property>
-                                    <signal name="toggled" handler="csv_import_trans_skiprows_cb" swapped="no"/>
+                                    <property name="invisible_char">●</property>
+                                    <property name="invisible_char_set">True</property>
+                                    <property name="primary_icon_activatable">False</property>
+                                    <property name="secondary_icon_activatable">False</property>
+                                    <property name="primary_icon_sensitive">True</property>
+                                    <property name="secondary_icon_sensitive">True</property>
+                                    <property name="adjustment">end_row_adj</property>
+                                    <property name="numeric">True</property>
+                                    <signal name="value-changed" handler="csv_import_trans_erow_cb" swapped="no"/>
                                   </object>
                                   <packing>
                                     <property name="expand">False</property>
-                                    <property name="fill">True</property>
-                                    <property name="position">1</property>
+                                    <property name="fill">False</property>
+                                    <property name="position">0</property>
                                   </packing>
                                 </child>
                                 <child>
                                   <placeholder/>
                                 </child>
                               </object>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="right_attach">2</property>
+                                <property name="top_attach">5</property>
+                                <property name="bottom_attach">6</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkHSeparator" id="hseparator2">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                              </object>
+                              <packing>
+                                <property name="right_attach">2</property>
+                                <property name="top_attach">3</property>
+                                <property name="bottom_attach">4</property>
+                              </packing>
                             </child>
                           </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">True</property>
+                            <property name="position">0</property>
+                          </packing>
                         </child>
-                        <child type="label">
-                          <object class="GtkLabel" id="label13">
+                        <child>
+                          <object class="GtkCheckButton" id="skip_rows">
+                            <property name="label" translatable="yes">Skip alternate lines</property>
                             <property name="visible">True</property>
-                            <property name="can_focus">False</property>
-                            <property name="label" translatable="yes"><b>Miscellaneous</b></property>
-                            <property name="use_markup">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">False</property>
+                            <property name="tooltip_text" translatable="yes">Starting from the first line that is actually imported every second line will be skipped. This option will take the leading lines to skip into account as well.
+For example
+* if 'Leading Lines to Skip' is set to 3, the first line to import will be line 4. Lines 5, 7, 9,... will be skipped.
+* if 'Leading Lines to Skip' is set to 4, the first line to import will be line 5. Lines 6, 8, 10,... will be skipped.</property>
+                            <property name="draw_indicator">True</property>
+                            <signal name="toggled" handler="csv_import_trans_skiprows_cb" swapped="no"/>
                           </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">True</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <placeholder/>
                         </child>
                       </object>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">False</property>
-                        <property name="position">1</property>
-                      </packing>
                     </child>
                   </object>
-                  <packing>
-                    <property name="expand">False</property>
-                    <property name="fill">False</property>
-                    <property name="padding">5</property>
-                    <property name="position">1</property>
-                  </packing>
+                </child>
+                <child type="label">
+                  <object class="GtkLabel" id="label13">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes"><b>Miscellaneous</b></property>
+                    <property name="use_markup">True</property>
+                  </object>
                 </child>
               </object>
               <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-                <property name="position">0</property>
+                <property name="left_attach">1</property>
+                <property name="right_attach">2</property>
+                <property name="top_attach">1</property>
+                <property name="bottom_attach">2</property>
+                <property name="x_options">GTK_FILL</property>
+                <property name="y_options">GTK_FILL</property>
               </packing>
             </child>
             <child>
-              <placeholder/>
+              <object class="GtkFrame" id="frame1">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="label_xalign">0</property>
+                <property name="shadow_type">none</property>
+                <child>
+                  <object class="GtkAlignment" id="alignment1">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="bottom_padding">5</property>
+                    <property name="left_padding">5</property>
+                    <property name="right_padding">5</property>
+                    <child>
+                      <object class="GtkHBox" id="account_hbox">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <child>
+                          <placeholder/>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child type="label">
+                  <object class="GtkLabel" id="label1">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes"><b>Account</b></property>
+                    <property name="use_markup">True</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="right_attach">2</property>
+                <property name="x_options">GTK_FILL</property>
+                <property name="y_options">GTK_FILL</property>
+              </packing>
             </child>
           </object>
           <packing>
@@ -861,9 +866,6 @@ For example
             <property name="position">3</property>
           </packing>
         </child>
-        <child>
-          <placeholder/>
-        </child>
       </object>
       <packing>
         <property name="page_type">intro</property>
@@ -1068,4 +1070,14 @@ More information can be displayed by using the help button.</property>
       </packing>
     </child>
   </object>
+  <object class="GtkAdjustment" id="end_row_adj">
+    <property name="upper">1000</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
+  <object class="GtkAdjustment" id="start_row_adj">
+    <property name="upper">1000</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
 </interface>
diff --git a/src/import-export/csv-imp/gnc-csv-trans-settings.cpp b/src/import-export/csv-imp/gnc-csv-trans-settings.cpp
index d7f4cb5..b0cecb9 100644
--- a/src/import-export/csv-imp/gnc-csv-trans-settings.cpp
+++ b/src/import-export/csv-imp/gnc-csv-trans-settings.cpp
@@ -24,6 +24,7 @@
     @brief CSV Import Settings
     @author Copyright (c) 2014 Robert Fewell
 */
+
 #include "gnc-csv-trans-settings.hpp"
 
 extern "C"
@@ -33,7 +34,9 @@ extern "C"
 #include <gtk/gtk.h>
 #include <glib/gi18n.h>
 
+#include "Account.h"
 #include "gnc-state.h"
+#include "gnc-ui-util.h"
 }
 
 const std::string csv_group_prefix{"CSV - "};
@@ -41,22 +44,23 @@ const std::string no_settings{N_("No Settings")};
 const std::string gnc_exp{N_("GnuCash Export Format")};
 #define CSV_NAME         "Name"
 #define CSV_FORMAT       "CsvFormat"
-#define CSV_ALT_ROWS     "AltRows"
-#define CSV_SKIP_START   "SkipStartRows"
-#define CSV_SKIP_END     "SkipEndRows"
+#define CSV_SKIP_ALT     "SkipAltLines"
+#define CSV_SKIP_START   "SkipStartLines"
+#define CSV_SKIP_END     "SkipEndLines"
 #define CSV_MULTI_SPLIT  "MultiSplit"
 
-#define CSV_SEP          "Separator"
+#define CSV_SEP          "Separators"
 
 #define CSV_CUSTOM       "Custom"
 #define CSV_CUSTOM_ENTRY "CustomEntry"
 
-#define CSV_DATE         "DateActive"
-#define CSV_CURRENCY     "CurrencyActive"
+#define CSV_DATE         "DateFormat"
+#define CSV_CURRENCY     "CurrencyFormat"
 
 #define CSV_ENCODING     "Encoding"
 #define CSV_COL_TYPES    "ColumnTypes"
 #define CSV_COL_WIDTHS   "ColumnWidths"
+#define CSV_ACCOUNT      "BaseAccount"
 
 G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT;
 
@@ -74,14 +78,12 @@ static std::shared_ptr<CsvTransSettings> create_int_gnc_exp_preset(void)
 {
     auto preset = std::make_shared<CsvTransSettings>();
     preset->name = gnc_exp;
-    preset->header_rows = 1;
+    preset->skip_start_lines = 1;
     preset->multi_split = true;
 
-    preset->separator[SEP_COMMA] = true;
-
     /* FIXME date and currency format should still be aligned with export format!
      * That's currently hard to do, because the export uses whatever the user
-     * had set as preference.
+     * had set as global preference.
     preset->date_active = 0;
     preset->currency_active = 0;
     */
@@ -205,45 +207,37 @@ CsvTransSettings::load (void)
     auto group = csv_group_prefix + name;
     auto keyfile = gnc_state_get_current ();
 
-    header_rows = g_key_file_get_integer (keyfile, group.c_str(), CSV_SKIP_START, &key_error);
+    skip_start_lines = g_key_file_get_integer (keyfile, group.c_str(), CSV_SKIP_START, &key_error);
     load_error |= handle_load_error (&key_error, group);
 
-    footer_rows = g_key_file_get_integer (keyfile, group.c_str(), CSV_SKIP_END, &key_error);
+    skip_end_lines = g_key_file_get_integer (keyfile, group.c_str(), CSV_SKIP_END, &key_error);
     load_error |= handle_load_error (&key_error, group);
 
-    skip_alt_rows = g_key_file_get_boolean (keyfile, group.c_str(), CSV_ALT_ROWS, &key_error);
+    skip_alt_lines = g_key_file_get_boolean (keyfile, group.c_str(), CSV_SKIP_ALT, &key_error);
     load_error |= handle_load_error (&key_error, group);
 
     multi_split = g_key_file_get_boolean (keyfile, group.c_str(), CSV_MULTI_SPLIT, &key_error);
     load_error |= handle_load_error (&key_error, group);
 
-    csv_format = g_key_file_get_boolean (keyfile, group.c_str(), CSV_FORMAT, &key_error);
+    auto csv_format = g_key_file_get_boolean (keyfile, group.c_str(), CSV_FORMAT, &key_error);
     if (key_error) csv_format = true; // default to true, but above command will return false in case of error
     load_error |= handle_load_error (&key_error, group);
+    if (csv_format)
+        file_format = GncImpFileFormat::CSV;
+    else
+        file_format = GncImpFileFormat::FIXED_WIDTH;
 
-    for (uint i = 0; i < SEP_NUM_OF_TYPES; i++)
-    {
-        gchar *sep;
-        sep = g_strdup_printf ("%s%d", CSV_SEP, i);
-        separator[i] = g_key_file_get_boolean (keyfile, group.c_str(), sep, &key_error);
-        load_error |= handle_load_error (&key_error, group);
-        g_free (sep);
-    }
-
-    custom = g_key_file_get_boolean (keyfile, group.c_str(), CSV_CUSTOM, &key_error);
-    load_error |= handle_load_error (&key_error, group);
-
-    gchar *key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_CUSTOM_ENTRY, &key_error);
+    gchar *key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_SEP, &key_error);
     if (key_char && *key_char != '\0')
-        custom_entry = key_char;
+        separators = key_char;
     load_error |= handle_load_error (&key_error, group);
     if (key_char)
         g_free (key_char);
 
-    date_active = g_key_file_get_integer (keyfile, group.c_str(), CSV_DATE, &key_error);
+    date_format = g_key_file_get_integer (keyfile, group.c_str(), CSV_DATE, &key_error);
     load_error |= handle_load_error (&key_error, group);
 
-    currency_active = g_key_file_get_integer (keyfile, group.c_str(), CSV_CURRENCY, &key_error);
+    currency_format = g_key_file_get_integer (keyfile, group.c_str(), CSV_CURRENCY, &key_error);
     load_error |= handle_load_error (&key_error, group);
 
     key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_ENCODING, &key_error);
@@ -255,6 +249,13 @@ CsvTransSettings::load (void)
     if (key_char)
         g_free (key_char);
 
+    key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_ACCOUNT, &key_error);
+    if (key_char && *key_char != '\0')
+        base_account = gnc_account_lookup_by_full_name (gnc_get_current_root_account(), key_char);
+    load_error |= handle_load_error (&key_error, group);
+    if (key_char)
+        g_free (key_char);
+
     column_types.clear();
     gsize list_len;
     gchar** col_types_str = g_key_file_get_string_list (keyfile, group.c_str(), CSV_COL_TYPES,
@@ -321,25 +322,20 @@ CsvTransSettings::save (void)
     // Start Saving the settings
     g_key_file_set_string (keyfile, group.c_str(), CSV_NAME, name.c_str());
     g_key_file_set_boolean (keyfile, group.c_str(), CSV_MULTI_SPLIT, multi_split);
-    g_key_file_set_integer (keyfile, group.c_str(), CSV_SKIP_START, header_rows);
-    g_key_file_set_integer (keyfile, group.c_str(), CSV_SKIP_END, footer_rows);
-    g_key_file_set_boolean (keyfile, group.c_str(), CSV_ALT_ROWS, skip_alt_rows);
-    g_key_file_set_boolean (keyfile, group.c_str(), CSV_FORMAT, csv_format);
-
-    for (guint i = 0; i < SEP_NUM_OF_TYPES; i++)
-    {
-        gchar *sep;
-        sep = g_strdup_printf ("%s%d", CSV_SEP, i);
-        g_key_file_set_boolean (keyfile, group.c_str(), sep, separator[i]);
-        g_free (sep);
-    }
-
-    g_key_file_set_boolean (keyfile, group.c_str(), CSV_CUSTOM, custom);
-    g_key_file_set_string (keyfile, group.c_str(), CSV_CUSTOM_ENTRY, custom_entry.c_str());
-    g_key_file_set_integer (keyfile, group.c_str(), CSV_DATE, date_active);
-    g_key_file_set_integer (keyfile, group.c_str(), CSV_CURRENCY, currency_active);
+    g_key_file_set_integer (keyfile, group.c_str(), CSV_SKIP_START, skip_start_lines);
+    g_key_file_set_integer (keyfile, group.c_str(), CSV_SKIP_END, skip_end_lines);
+    g_key_file_set_boolean (keyfile, group.c_str(), CSV_SKIP_ALT, skip_alt_lines);
+    g_key_file_set_boolean (keyfile, group.c_str(), CSV_FORMAT,
+        (file_format == GncImpFileFormat::CSV) ? true : false);
+
+    g_key_file_set_string (keyfile, group.c_str(), CSV_SEP, separators.c_str());
+    g_key_file_set_integer (keyfile, group.c_str(), CSV_DATE, date_format);
+    g_key_file_set_integer (keyfile, group.c_str(), CSV_CURRENCY, currency_format);
     g_key_file_set_string (keyfile, group.c_str(), CSV_ENCODING, encoding.c_str());
 
+    if (base_account)
+        g_key_file_set_string (keyfile, group.c_str(), CSV_ACCOUNT, gnc_account_get_full_name(base_account));
+
     std::vector<const char*> col_types_str;
     for (auto col_type : column_types)
         col_types_str.push_back(gnc_csv_col_type_strs[col_type]);
diff --git a/src/import-export/csv-imp/gnc-csv-trans-settings.hpp b/src/import-export/csv-imp/gnc-csv-trans-settings.hpp
index 7577563..fe53b34 100644
--- a/src/import-export/csv-imp/gnc-csv-trans-settings.hpp
+++ b/src/import-export/csv-imp/gnc-csv-trans-settings.hpp
@@ -27,9 +27,15 @@
 #ifndef GNC_CSV_TRANS_SETTINGS_H
 #define GNC_CSV_TRANS_SETTINGS_H
 
+extern "C" {
+#include "config.h"
+#include "Account.h"
+}
+
 #include <string>
 #include <vector>
 #include "gnc-trans-props.hpp"
+#include "gnc-tokenizer.hpp"
 
 /** Enumeration for separator checkbutton types. These are the
  *  different types of checkbuttons that the user can click to
@@ -42,18 +48,11 @@ enum SETTINGS_COL {SET_GROUP, SET_NAME};
 
 struct CsvTransSettings
 {
-    CsvTransSettings() : header_rows{0}, footer_rows{0}, csv_format (true),
-                    skip_alt_rows (false), multi_split (false),
-                    encoding {"UTF-8"}, custom {false}, custom_entry {""},
-                    date_active {0}, currency_active {0}, load_error {false},
-                    internal {false}
-                    {
-                        for (uint i = 0; i < SEP_NUM_OF_TYPES; i++)
-                        {
-                            separator[i] = false;
-                        }
-                        separator [SEP_COMMA] = true;
-                    }
+    CsvTransSettings() : file_format (GncImpFileFormat::CSV), encoding {"UTF-8"},
+            multi_split (false), date_format {0}, currency_format {0},
+            skip_start_lines{0}, skip_end_lines{0}, skip_alt_lines (false),
+            separators {","}, base_account {nullptr},
+            load_error {false}, internal {false} { }
 
 /** Save the gathered widget properties to a key File.
  *
@@ -84,21 +83,17 @@ bool read_only (void);
 
 
 std::string   name;                         // Name given to this preset by the user
-int           header_rows;                  // Number of header rows
-int           footer_rows;                  // Number of footer rows
-bool          csv_format;                   // CSV import Format
-bool          skip_alt_rows;                // Skip alternate rows
-bool          multi_split;                  // Assume multiple lines per transaction
-
+GncImpFileFormat file_format;               // CSV import Format
 std::string   encoding;                     // File encoding
-
-bool          separator[SEP_NUM_OF_TYPES];  // The separators
-
-bool          custom;                       // Custom entry set
-std::string   custom_entry;                 // Custom Entry
-
-int           date_active;                  // Date Active id
-int           currency_active;              // Currency Active id
+bool          multi_split;                  // Assume multiple lines per transaction
+int           date_format;                  // Date Active id
+int           currency_format;              // Currency Active id
+uint           skip_start_lines;             // Number of header rows to skip
+uint           skip_end_lines;               // Number of footer rows to skip
+bool          skip_alt_lines;                // Skip alternate rows
+std::string   separators;                   // Separators for csv format
+
+Account      *base_account;                 // Base account
 std::vector<GncTransPropType>  column_types;// The Column types in order
 std::vector<uint> column_widths;            // The Column widths
 
diff --git a/src/import-export/csv-imp/gnc-tx-import.cpp b/src/import-export/csv-imp/gnc-tx-import.cpp
index 7174ea6..ccba3fe 100644
--- a/src/import-export/csv-imp/gnc-tx-import.cpp
+++ b/src/import-export/csv-imp/gnc-tx-import.cpp
@@ -39,6 +39,7 @@ extern "C" {
 #include "gnc-trans-props.hpp"
 #include "gnc-csv-tokenizer.hpp"
 #include "gnc-fw-tokenizer.hpp"
+#include "gnc-csv-trans-settings.hpp"
 
 G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT;
 
@@ -65,16 +66,8 @@ GncTxImport::GncTxImport(GncImpFileFormat format)
     /* All of the data pointers are initially NULL. This is so that, if
      * gnc_csv_parse_data_free is called before all of the data is
      * initialized, only the data that needs to be freed is freed. */
-    date_format = -1;
-    currency_format = 0;
-    skip_start_lines = 0;
-    skip_end_lines = 0;
-    skip_alt_lines = FALSE;
     parse_errors = false;
-    multi_split = false;
-
-    file_fmt = format;
-    tokenizer = gnc_tokenizer_factory(file_fmt);
+    file_format(m_settings.file_format = format);
 }
 
 /** Destructor for GncTxImport.
@@ -92,7 +85,7 @@ GncTxImport::~GncTxImport()
  */
 void GncTxImport::file_format(GncImpFileFormat format)
 {
-    if (file_fmt == format)
+    if (tokenizer && m_settings.file_format == format)
         return;
 
     auto new_encoding = std::string("UTF-8");
@@ -105,8 +98,8 @@ void GncTxImport::file_format(GncImpFileFormat format)
         new_imp_file = tokenizer->current_file();
     }
 
-    file_fmt = format;
-    tokenizer = gnc_tokenizer_factory(file_fmt);
+    m_settings.file_format = format;
+    tokenizer = gnc_tokenizer_factory(m_settings.file_format);
 
     // Set up new tokenizer with common settings
     // recovered from old tokenizer
@@ -116,19 +109,7 @@ void GncTxImport::file_format(GncImpFileFormat format)
 
 GncImpFileFormat GncTxImport::file_format()
 {
-    return file_fmt;
-}
-
-/** Converts raw file data using a new encoding. This function must be
- * called after load_file only if load_file guessed
- * the wrong encoding.
- * @param encoding Encoding that data should be translated using
- */
-void GncTxImport::convert_encoding (const std::string& encoding)
-{
-    // TODO investigate if we can catch conversion errors and report them
-    if (tokenizer)
-        tokenizer->encoding(encoding);
+    return m_settings.file_format;
 }
 
 /** Toggles the multi-split state of the importer and will subsequently
@@ -137,20 +118,22 @@ void GncTxImport::convert_encoding (const std::string& encoding)
  * @param multi_split_val Boolean value with desired state (multi-split
  * vs two-split).
  */
-void GncTxImport::set_multi_split (bool multi_split_val)
+void GncTxImport::multi_split (bool multi_split)
 {
-    multi_split = multi_split_val;
-    for (auto col_it = column_types.begin(); col_it != column_types.end();
+    m_settings.multi_split = multi_split;
+    for (auto col_it = m_settings.column_types.begin(); col_it != m_settings.column_types.end();
             col_it++)
     {
-        auto san_prop = sanitize_trans_prop (*col_it, multi_split);
+        auto san_prop = sanitize_trans_prop (*col_it, m_settings.multi_split);
         if (san_prop != *col_it)
             *col_it = san_prop;
     }
-    if (multi_split)
-        base_account = nullptr;
+    if (m_settings.multi_split)
+        m_settings.base_account = nullptr;
 }
 
+bool GncTxImport::multi_split () { return m_settings.multi_split; }
+
 /** Sets a base account. This is the account all import data relates to.
  *  As such at least one split of each transaction that will be generated
  *  will be in this account.
@@ -160,21 +143,103 @@ void GncTxImport::set_multi_split (bool multi_split_val)
  *  that mode the base_account can't be set.
  * @param base_acct Pointer to an account or NULL.
  */
-void GncTxImport::set_base_account (Account* base_acct)
+void GncTxImport::base_account (Account* base_account)
 {
-    if (multi_split)
+    if (m_settings.multi_split)
+    {
+        m_settings.base_account = nullptr;
         return;
+    }
 
-    base_account = base_acct;
+    m_settings.base_account = base_account;
 
-    if (base_account)
+    if (m_settings.base_account)
     {
-        auto col_type = std::find (column_types.begin(), column_types.end(), GncTransPropType::ACCOUNT);
-        if (col_type != column_types.end())
+        auto col_type = std::find (m_settings.column_types.begin(),
+                m_settings.column_types.end(), GncTransPropType::ACCOUNT);
+        if (col_type != m_settings.column_types.end())
             *col_type = GncTransPropType::NONE;
     }
 }
 
+Account *GncTxImport::base_account () { return m_settings.base_account; }
+
+void GncTxImport::currency_format (int currency_format)
+    { m_settings.currency_format = currency_format; }
+int GncTxImport::currency_format () { return m_settings.currency_format; }
+
+void GncTxImport::date_format (int date_format)
+    { m_settings.date_format = date_format; }
+int GncTxImport::date_format () { return m_settings.date_format; }
+
+/** Converts raw file data using a new encoding. This function must be
+ * called after load_file only if load_file guessed
+ * the wrong encoding.
+ * @param encoding Encoding that data should be translated using
+ */
+void GncTxImport::encoding (const std::string& encoding)
+{
+
+    // TODO investigate if we can catch conversion errors and report them
+    if (tokenizer)
+        tokenizer->encoding(encoding); // May throw
+
+    m_settings.encoding = encoding;
+}
+
+std::string GncTxImport::encoding () { return m_settings.encoding; }
+
+void GncTxImport::skip_start_lines (uint num)
+    { m_settings.skip_start_lines = num; }
+uint GncTxImport::skip_start_lines () { return m_settings.skip_start_lines; }
+
+void GncTxImport::skip_end_lines (uint num) { m_settings.skip_end_lines = num; }
+uint GncTxImport::skip_end_lines () { return m_settings.skip_end_lines; }
+
+void GncTxImport::skip_alt_lines (bool skip)
+    { m_settings.skip_alt_lines = skip; }
+bool GncTxImport::skip_alt_lines () { return m_settings.skip_alt_lines; }
+
+void GncTxImport::separators (std::string separators)
+{
+    if (file_format() != GncImpFileFormat::CSV)
+        return;
+
+    m_settings.separators = separators;
+    auto csvtok = dynamic_cast<GncCsvTokenizer*>(tokenizer.get());
+    csvtok->set_separators (separators);
+
+}
+std::string GncTxImport::separators () { return m_settings.separators; }
+
+void GncTxImport::settings (const CsvTransSettings& settings)
+{
+    m_settings = settings;
+    file_format (m_settings.file_format);
+    multi_split (m_settings.multi_split);
+    base_account (m_settings.base_account);
+    encoding (m_settings.encoding);
+    separators (m_settings.separators);
+    try
+    {
+        tokenize(false);
+    }
+    catch (...)
+    { };
+
+}
+
+bool GncTxImport::save_settings ()
+{
+
+    if (trans_preset_is_reserved_name (m_settings.name))
+        return true;
+    return m_settings.save();
+}
+
+void GncTxImport::settings_name (std::string name) { m_settings.name = name; }
+std::string GncTxImport::settings_name () { return m_settings.name; }
+
 /** Loads a file into a GncTxImport. This is the first function
  * that must be called after creating a new GncTxImport. As long as
  * this function didn't run successfully, the importer can't proceed.
@@ -211,6 +276,9 @@ void GncTxImport::load_file (const std::string& filename)
  */
 void GncTxImport::tokenize (bool guessColTypes)
 {
+    if (!tokenizer)
+        return;
+
     uint max_cols = 0;
     tokenizer->tokenize();
     parsed_lines.clear();
@@ -230,12 +298,7 @@ void GncTxImport::tokenize (bool guessColTypes)
         return;
     }
 
-    if (guessColTypes)
-    {
-        /* Free column_types if it's already been created. */
-        column_types.clear();
-    }
-    column_types.resize(max_cols, GncTransPropType::NONE);
+    m_settings.column_types.resize(max_cols, GncTransPropType::NONE);
 
     if (guessColTypes)
     {
@@ -250,7 +313,7 @@ void GncTxImport::tokenize (bool guessColTypes)
  * @return An empty string if all checks passed or the reason
  *         verification failed otherwise.
  */
-std::string GncTxImport::verify (void)
+std::string GncTxImport::verify ()
 {
     auto newline = std::string();
     auto error_text = std::string();
@@ -263,8 +326,8 @@ std::string GncTxImport::verify (void)
     }
 
     /* Check if at least one line is selected for importing */
-    auto skip_alt_offset = skip_alt_lines ? 1 : 0;
-    if (skip_start_lines + skip_end_lines + skip_alt_offset >= parsed_lines.size())
+    auto skip_alt_offset = m_settings.skip_alt_lines ? 1 : 0;
+    if (m_settings.skip_start_lines + m_settings.skip_end_lines + skip_alt_offset >= parsed_lines.size())
     {
         error_text = _("No lines are selected for importing. Please reduce the number of lines to skip.");
         return error_text;
@@ -281,23 +344,23 @@ std::string GncTxImport::verify (void)
         /* Attempt to parse the date column for each selected line */
         try
         {
-            auto date_col = std::find(column_types.begin(),
-                    column_types.end(), GncTransPropType::DATE) -
-                            column_types.begin();
+            auto date_col = std::find(m_settings.column_types.begin(),
+                    m_settings.column_types.end(), GncTransPropType::DATE) -
+                            m_settings.column_types.begin();
             for (uint i = 0; i < parsed_lines.size(); i++)
             {
-                if ((i < skip_start_lines) ||             // start rows to skip
+                if ((i < m_settings.skip_start_lines) ||             // start rows to skip
                     (i >= parsed_lines.size()
-                            - skip_end_lines) ||          // end rows to skip
-                    (((i - skip_start_lines) % 2 == 1) && // skip every second row...
-                     skip_alt_lines))                     // ...if requested
+                            - m_settings.skip_end_lines) ||          // end rows to skip
+                    (((i - m_settings.skip_start_lines) % 2 == 1) && // skip every second row...
+                            m_settings.skip_alt_lines))              // ...if requested
                     continue;
                 else
                 {
                     auto first_line = std::get<0>(parsed_lines[i]);
                     auto date_str = first_line[date_col];
                     if (!date_str.empty())
-                        parse_date (date_str, date_format);
+                        parse_date (date_str, date_format());
                 }
             }
         }
@@ -312,12 +375,12 @@ std::string GncTxImport::verify (void)
      */
     if (!check_for_column_type(GncTransPropType::ACCOUNT))
     {
-        if (multi_split)
+        if (m_settings.multi_split)
         {
             error_text += newline + _("Please select an account column.");
             newline = "\n";
         }
-        else if (!base_account)
+        else if (!m_settings.base_account)
         {
             error_text += newline + _("Please select an account column or set a base account in the Account field.");
             newline = "\n";
@@ -431,7 +494,7 @@ std::shared_ptr<DraftTransaction> GncTxImport::trans_properties_to_trans (std::v
         current_draft->void_reason = trans_props->get_void_reason();
         created_trans = true;
     }
-    else if (multi_split)  // in multi_split mode create_trans will return a nullptr for all but the first split
+    else if (m_settings.multi_split)  // in multi_split mode create_trans will return a nullptr for all but the first split
         trans = current_draft->trans;
     else // in non-multi-split mode each line should be a transaction, so not having one here is an error
         throw std::invalid_argument ("Failed to create transaction from selected columns.");
@@ -452,16 +515,16 @@ void GncTxImport::create_transaction (std::vector<parse_line_t>::iterator& parse
 {
     StrVec line;
     std::string error_message;
-    auto trans_props = std::make_shared<GncPreTrans>(date_format);
-    auto split_props = std::make_shared<GncPreSplit>(date_format, currency_format);
+    auto trans_props = std::make_shared<GncPreTrans>(date_format());
+    auto split_props = std::make_shared<GncPreSplit>(date_format(), currency_format());
     std::tie(line, error_message, std::ignore, std::ignore) = *parsed_line;
     error_message.clear();
 
     /* Convert all tokens in this line into transaction/split properties. */
-    auto col_types_it = column_types.cbegin();
+    auto col_types_it = m_settings.column_types.cbegin();
     auto line_it = line.cbegin();
     for (col_types_it, line_it;
-            col_types_it != column_types.cend() &&
+            col_types_it != m_settings.column_types.cend() &&
             line_it != line.cend();
             ++col_types_it, ++line_it)
     {
@@ -471,7 +534,7 @@ void GncTxImport::create_transaction (std::vector<parse_line_t>::iterator& parse
                 continue; /* We do nothing with "None"-type columns. */
             else if  (*col_types_it <= GncTransPropType::TRANS_PROPS)
             {
-                if (multi_split && line_it->empty())
+                if (m_settings.multi_split && line_it->empty())
                     continue; // In multi-split mode, transaction properties can be empty
                 trans_props->set_property(*col_types_it, *line_it);
             }
@@ -492,7 +555,7 @@ void GncTxImport::create_transaction (std::vector<parse_line_t>::iterator& parse
 
     /* For multi-split input data, we need to check whether this line is part of a transaction that
      * has already be started by a previous line. */
-    if (multi_split)
+    if (m_settings.multi_split)
     {
         if (trans_props->is_part_of(parent))
         {
@@ -522,8 +585,8 @@ void GncTxImport::create_transaction (std::vector<parse_line_t>::iterator& parse
     auto line_acct = split_props->get_account();
     if (!line_acct)
     {
-        if (base_account)
-            split_props->set_account(base_account);
+        if (m_settings.base_account)
+            split_props->set_account(m_settings.base_account);
         else
         {
             // Oops - the user didn't select an Account column *and* we didn't get a default value either!
@@ -583,10 +646,10 @@ void GncTxImport::create_transactions (bool redo_errors)
 
     /* compute start and end iterators based on user-set restrictions */
     auto parsed_lines_it = parsed_lines.begin();
-    std::advance(parsed_lines_it, skip_start_lines);
+    std::advance(parsed_lines_it, skip_start_lines());
 
     auto parsed_lines_max = parsed_lines.begin();
-    std::advance(parsed_lines_max, parsed_lines.size() - skip_end_lines);
+    std::advance(parsed_lines_max, parsed_lines.size() - skip_end_lines());
 
     auto odd_line = false;
     parse_errors = false;
@@ -604,7 +667,7 @@ void GncTxImport::create_transactions (bool redo_errors)
               skip_rows is enabled AND
               current line is an odd line */
         if ((redo_errors && std::get<1>(*parsed_lines_it).empty()) ||
-           (!redo_errors && skip_alt_lines && odd_line))
+           (!redo_errors && skip_alt_lines() && odd_line))
             continue;
 
         try
@@ -623,5 +686,28 @@ void GncTxImport::create_transactions (bool redo_errors)
 bool
 GncTxImport::check_for_column_type (GncTransPropType type)
 {
-    return (std::find (column_types.begin(), column_types.end(), type) != column_types.end());
+    return (std::find (m_settings.column_types.begin(),
+                       m_settings.column_types.end(), type)
+                        != m_settings.column_types.end());
+}
+
+void
+GncTxImport::set_column_type (uint position, GncTransPropType type)
+{
+    if (position >= m_settings.column_types.size())
+        return;
+
+    // Column types should be unique, so remove any previous occurrence of the new type
+    std::replace(m_settings.column_types.begin(), m_settings.column_types.end(),
+            type, GncTransPropType::NONE);
+    m_settings.column_types.at (position) = type;
+
+    // If the user has set an Account column, we can't have a base account set
+    if (type == GncTransPropType::ACCOUNT)
+        base_account (nullptr);
+}
+
+std::vector<GncTransPropType> GncTxImport::column_types ()
+{
+    return m_settings.column_types;
 }
diff --git a/src/import-export/csv-imp/gnc-tx-import.hpp b/src/import-export/csv-imp/gnc-tx-import.hpp
index 1f9c4e2..e82354c 100644
--- a/src/import-export/csv-imp/gnc-tx-import.hpp
+++ b/src/import-export/csv-imp/gnc-tx-import.hpp
@@ -42,6 +42,7 @@ extern "C" {
 
 #include "gnc-tokenizer.hpp"
 #include "gnc-trans-props.hpp"
+#include "gnc-csv-trans-settings.hpp"
 
 
 /** This struct stores a possibly incomplete transaction
@@ -95,40 +96,60 @@ public:
     void file_format(GncImpFileFormat format);
     GncImpFileFormat file_format();
 
-    void load_file (const std::string& filename);
-    void convert_encoding (const std::string& encoding);
+    void multi_split (bool multi_split);
+    bool multi_split ();
+
+    void base_account (Account *base_account);
+    Account *base_account ();
+
+    void currency_format (int currency_format);
+    int currency_format ();
+
+    void date_format (int date_format);
+    int date_format ();
+
+    void encoding (const std::string& encoding);
+    std::string encoding ();
+
+    void skip_start_lines (uint num);
+    uint skip_start_lines ();
+
+    void skip_end_lines (uint num);
+    uint skip_end_lines ();
 
-    void set_multi_split (bool multi_split_val);
-    void set_base_account (Account *base_acct);
+    void skip_alt_lines (bool skip);
+    bool skip_alt_lines ();
+
+    void separators (std::string separators);
+    std::string separators ();
+
+    void settings (const CsvTransSettings& settings);
+    bool save_settings ();
+
+    void settings_name (std::string name);
+    std::string settings_name ();
+
+
+    void load_file (const std::string& filename);
 
     void tokenize (bool guessColTypes);
 
-    std::string verify(void);
+    std::string verify();
 
     /** This function will attempt to convert all tokenized lines into
      *  transactions using the column types the user has set.
      */
     void create_transactions (bool redo_errors);
     bool check_for_column_type (GncTransPropType type);
+    void set_column_type (uint position, GncTransPropType type);
+    std::vector<GncTransPropType> column_types ();
 
     std::unique_ptr<GncTokenizer> tokenizer;    /**< Will handle file loading/encoding conversion/splitting into fields */
     std::vector<parse_line_t> parsed_lines;     /**< source file parsed into a two-dimensional array of strings.
                                                      Per line also holds possible error messages and objects with extracted transaction
                                                      and split properties. */
-    std::vector<GncTransPropType> column_types; /**< Vector of values from the GncCsvColumnType enumeration */
     std::multimap <time64, std::shared_ptr<DraftTransaction>> transactions;  /**< map of transaction objects created
                                                      from parsed_lines and column_types, ordered by date */
-    int date_format;            /**< The format of the text in the date columns from date_format_internal. */
-    guint skip_start_lines;     /**< Number of lines to skip at the beginning of the parse data. */
-    guint skip_end_lines;       /**< Number of lines to skip at the end of the parse data. */
-    gboolean skip_alt_lines;         /**< Skip Alternate Rows from start row. */
-    bool multi_split;           /**< If false, each line in the import data defines exactly one transaction.
-                                     If true, a transaction can span multiple lines, with each line defining exactly one split.
-                                     In this case the first line should hold the transaction related details in
-                                     addition to the first split details. On each following line for the same
-                                     transaction the transaction related columns should be empty or have
-                                     the same value as the first line. */
-    int currency_format;        /**< The currency format, 0 for locale, 1 for comma dec and 2 for period */
     bool parse_errors;          /**< Indicates whether the last parse_to_trans run had any errors */
 
 private:
@@ -150,12 +171,12 @@ private:
      */
     std::shared_ptr<DraftTransaction> trans_properties_to_trans (std::vector<parse_line_t>::iterator& parsed_line);
 
-    GncImpFileFormat file_fmt = GncImpFileFormat::UNKNOWN;
+    struct CsvTranSettings;
+    CsvTransSettings m_settings;
 
-    /* The variables below are only used while creating
+    /* The parameters below are only used while creating
      * transactions. They keep state information during the conversion.
      */
-    Account *base_account = nullptr;
     std::shared_ptr<GncPreTrans> parent = nullptr;
     std::shared_ptr<DraftTransaction> current_draft = nullptr;
 };

commit 26e59c4e2140a4acc8fc112dfa2790cfa829267e
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Fri Dec 16 21:22:48 2016 +0100

    Revisit error checking on the preview page.
    
    - make the CsvTxImport class responsible for the check
    - guide the user with suggestions rather than have the user click through to the next page to find out things weren't set up properly

diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index 82e6327..7eabe75 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -140,7 +140,6 @@ typedef struct
     bool                  new_book;                 /**< Are we importing into a new book?; if yes, call book options */
     int                   callcount;                /**< Number of times the assistant page forward function called */
     int                   next_page;                /**< The saved assistant next page number */
-    bool                  settings_valid;           /**< Are the settings valid */
 
 } CsvImportTrans;
 
@@ -179,9 +178,9 @@ void csv_import_trans_assistant_summary_page_prepare (GtkAssistant *assistant, g
 
 void csv_import_trans_load_settings (CsvImportTrans *info);
 
-static void gnc_csv_preview_update_assist (CsvImportTrans* info);
+void csv_import_refresh_preview_table (CsvImportTrans* info);
+void csv_import_validate_preview_settings (CsvImportTrans* info);
 void gnc_csv_reset_preview_setting (CsvImportTrans* info, bool block);
-bool preview_settings_valid (CsvImportTrans *info);
 bool get_list_of_accounts (CsvImportTrans* info, GtkTreeModel *store);
 
 /*************************************************************************/
@@ -308,7 +307,7 @@ csv_import_trans_load_settings (CsvImportTrans *info)
             fwtok->columns(preset->column_widths);
 
             info->parse_data->tokenize (false);
-            gnc_csv_preview_update_assist (info);
+            csv_import_refresh_preview_table (info);
         }
     }
     catch (std::range_error &e)
@@ -402,6 +401,7 @@ csv_import_trans_changed_settings_cb (GtkWidget *combo, CsvImportTrans *info)
 {
     csv_import_trans_load_settings (info);
     handle_save_del_sensitivity (combo, info);
+    csv_import_validate_preview_settings (info);
 }
 
 
@@ -755,6 +755,8 @@ void csv_import_trans_srow_cb (GtkWidget *spin, gpointer user_data)
 
     /* Refresh the row highlighting */
     row_selection_update (info);
+
+    csv_import_validate_preview_settings (info);
 }
 
 
@@ -778,6 +780,8 @@ void csv_import_trans_erow_cb (GtkWidget *spin, gpointer user_data)
 
     /* Refresh the row highlighting */
     row_selection_update (info);
+
+    csv_import_validate_preview_settings (info);
 }
 
 
@@ -794,6 +798,8 @@ void csv_import_trans_skip_errors_cb (GtkWidget *cb, gpointer user_data)
         info->skip_errors = true;
     else
         info->skip_errors = false;
+
+    csv_import_validate_preview_settings (info);
 }
 
 
@@ -809,7 +815,8 @@ void csv_import_trans_multisplit_cb (GtkWidget *checkbox, gpointer user_data)
     /* Set the skip_alt_lines variable */
     auto multi_split = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(checkbox));
     info->parse_data->set_multi_split (multi_split);
-    gnc_csv_preview_update_assist (info);
+    csv_import_refresh_preview_table (info);
+    csv_import_validate_preview_settings (info);
 }
 
 
@@ -827,6 +834,8 @@ void csv_import_trans_skiprows_cb (GtkWidget *checkbox, gpointer user_data)
 
     /* Refresh the row highlighting */
     row_selection_update (info);
+
+    csv_import_validate_preview_settings (info);
 }
 
 
@@ -911,10 +920,8 @@ void sep_button_clicked (GtkWidget* widget, CsvImportTrans* info)
     }
 
     /* If we parsed successfully, redisplay the data. */
-    gnc_csv_preview_update_assist (info);
-
-    /* Refresh the row highlighting */
-    row_selection_update (info);
+    csv_import_refresh_preview_table (info);
+    csv_import_validate_preview_settings (info);
 }
 
 void account_selected_cb (GtkWidget* widget, CsvImportTrans* info)
@@ -923,7 +930,9 @@ void account_selected_cb (GtkWidget* widget, CsvImportTrans* info)
     auto acct = gnc_account_sel_get_account( GNC_ACCOUNT_SEL(widget) );
     info->parse_data->set_base_account(acct);
     /* Update the preview. */
-    gnc_csv_preview_update_assist (info);
+    csv_import_refresh_preview_table (info);
+
+    csv_import_validate_preview_settings (info);
 }
 
 
@@ -961,10 +970,8 @@ static void separated_or_fixed_selected (GtkToggleButton* csv_button, CsvImportT
         info->parse_data->tokenize (false);
 
         /* Show the new data. */
-        gnc_csv_preview_update_assist (info);
-
-        /* Refresh the row highlighting */
-        row_selection_update (info);
+        csv_import_refresh_preview_table (info);
+        csv_import_validate_preview_settings (info);
     }
     catch (std::range_error &e)
     {
@@ -1020,10 +1027,8 @@ static void encoding_selected (GOCharmapSel* selector, const char* encoding,
             return;
         }
 
-        gnc_csv_preview_update_assist (info);
-
-        /* Refresh the row highlighting */
-        row_selection_update (info);
+        csv_import_refresh_preview_table (info);
+        csv_import_validate_preview_settings (info);
 
         info->encoding_selected_called = false;
     }
@@ -1041,6 +1046,8 @@ static void encoding_selected (GOCharmapSel* selector, const char* encoding,
 static void date_format_selected (GtkComboBoxText* format_selector, CsvImportTrans* info)
 {
     info->parse_data->date_format = gtk_combo_box_get_active (GTK_COMBO_BOX(format_selector));
+
+    csv_import_validate_preview_settings (info);
 }
 
 
@@ -1051,6 +1058,8 @@ static void date_format_selected (GtkComboBoxText* format_selector, CsvImportTra
 static void currency_format_selected (GtkComboBoxText* currency_selector, CsvImportTrans* info)
 {
     info->parse_data->currency_format = gtk_combo_box_get_active (GTK_COMBO_BOX(currency_selector));
+
+    csv_import_validate_preview_settings (info);
 }
 
 
@@ -1175,10 +1184,8 @@ fixed_context_menu_handler (GnumericPopupMenuElement const *element,
         gnc_error_dialog (NULL, "%s", e.what());
         return FALSE;
     }
-    gnc_csv_preview_update_assist (info);
-
-    /* Refresh the row highlighting */
-    row_selection_update (info);
+    csv_import_refresh_preview_table (info);
+    csv_import_validate_preview_settings (info);
     return TRUE;
 }
 static void
@@ -1354,6 +1361,8 @@ static void column_type_changed (GtkCellRenderer* renderer, gchar* path,
         gnc_account_sel_set_account(GNC_ACCOUNT_SEL(info->acct_selector), nullptr, false);
         info->parse_data->set_base_account(nullptr);
     }
+
+    csv_import_validate_preview_settings (info);
 }
 
 static void
@@ -1372,10 +1381,8 @@ split_column (CsvImportTrans* info, int col, int dx)
         gnc_error_dialog (NULL, "%s", e.what());
         return;
     }
-    gnc_csv_preview_update_assist (info);
-
-    /* Refresh the row highlighting */
-    row_selection_update (info);
+    csv_import_refresh_preview_table (info);
+    csv_import_validate_preview_settings (info);
 }
 
 
@@ -1419,103 +1426,6 @@ static void header_button_press_handler (GtkWidget* button, GdkEventButton* even
 }
 
 
-/* Test for the required minimum number of columns selected and
- * a valid date format.
- * Returns true if we do or false if we don't.
- *
- * @param info The data being previewed
- */
-bool preview_settings_valid (CsvImportTrans* info)
-{
-    int i, ncols = info->parse_data->column_types.size(); /* ncols is the number of columns in the data. */
-    int weight = 0;
-    int oweight = 0;
-    bool valid = true;
-    /* ctstore contains the actual strings appearing in the column types treeview. */
-    GtkTreeModel* ctstore = gtk_tree_view_get_model (info->ctreeview);
-    /* datastore contains the actual strings appearing in the preview treeview. */
-    GtkTreeModel* datastore = gtk_tree_view_get_model (info->treeview);
-    GtkTreeIter iter1, iter2;
-    /* Get an iterator for the first (and only) row. */
-    gtk_tree_model_get_iter_first (ctstore, &iter1);
-
-    /* Get an iterator for the first required row in the data store. */
-    gtk_tree_model_iter_nth_child (GTK_TREE_MODEL(datastore), &iter2, NULL, info->parse_data->skip_start_lines);
-
-    /* Go through each of the columns. */
-    for (i = 0; i < ncols; i++)
-    {
-        gchar* prevstr = NULL; /* The string in this column from datastore. */
-        auto col_type = GncTransPropType::NONE;
-        /* Get the column type. Store is arranged so that every two
-         * columns is a pair of
-         * - the column type as a user visible (translated) string
-         * - the internal type for this column
-         * So ctstore looks like:
-         * col_type_str 0, col_type, col_type_str 1, col_type 1, ..., col_type_str ncols, col_type ncols. */
-        gtk_tree_model_get (ctstore, &iter1, 2 * i + 1, &col_type, -1);
-
-        switch (col_type)
-        {
-        case GncTransPropType::DATE:
-            weight = weight + 1000;
-            gtk_tree_model_get (datastore, &iter2, i + 1, &prevstr, -1);
-
-            if (parse_date (prevstr, info->parse_data->date_format) == -1)
-                valid = false;
-            break;
-
-        case GncTransPropType::DESCRIPTION:
-            weight = weight + 100;
-            break;
-        case GncTransPropType::DEPOSIT:
-        case GncTransPropType::WITHDRAWAL:
-            weight = weight + 10;
-            break;
-
-        case GncTransPropType::NUM:
-        case GncTransPropType::NOTES:
-        case GncTransPropType::MEMO:
-            weight = weight + 1;
-            break;
-
-        case GncTransPropType::ACCOUNT:
-            weight = weight + 1;
-            break;
-
-        case GncTransPropType::TACCOUNT:
-            oweight = oweight + 100;
-            break;
-
-        case GncTransPropType::TMEMO:
-            oweight = oweight + 1;
-            break;
-        default:
-            break;
-        }
-
-        /* Free the type string created by gtk_tree_model_get() */
-        g_free (prevstr);
-    }
-
-    if ((oweight > 0) && (oweight < 99))
-    {
-        info->error_text = _("There are problems with the import settings!\nIf you have an Other Memo column "
-                             "you must have an Other Account column...");
-        return false;
-    }
-
-    if (weight < 1109 || !valid)
-    {
-        info->error_text = _("There are problems with the import settings!\nThe date format could be wrong "
-                             "or there are not enough columns set...");
-        return false;
-    }
-    else
-        return true;
-}
-
-
 /* Test for the string being in the liststore
  * Returns true if it is or false if not.
  *
@@ -1621,7 +1531,7 @@ bool get_list_of_accounts (CsvImportTrans* info, GtkTreeModel *store)
  *
  * @param info The data being previewed
  */
-static void gnc_csv_preview_update_assist (CsvImportTrans* info)
+void csv_import_refresh_preview_table (CsvImportTrans* info)
 {
     /* ncols is the number of columns in the file data. */
     auto ncols = info->parse_data->column_types.size();
@@ -1817,6 +1727,17 @@ static void gnc_csv_preview_update_assist (CsvImportTrans* info)
 
     /* Update the row selection highlight */
     row_selection_update (info);
+
+}
+
+
+void csv_import_validate_preview_settings (CsvImportTrans* info)
+{
+    /* Allow the user to proceed only if there are no inconsistencies in the settings */
+    auto error_msg = info->parse_data->verify();
+    gtk_assistant_set_page_complete (GTK_ASSISTANT(info->window), info->preview_page, error_msg.empty());
+    gtk_label_set_text(GTK_LABEL(info->instructions_label), error_msg.c_str());
+    gtk_widget_set_visible (GTK_WIDGET(info->instructions_image), !error_msg.empty());
 }
 
 
@@ -2007,8 +1928,14 @@ csv_import_trans_assistant_preview_page_prepare (GtkAssistant *assistant,
         gtk_list_store_clear (GTK_LIST_STORE(store)); // Clear list of accounts unless we are looking at errors
     }
 
+    /* Disable the Forward Assistant Button */
+    auto num = gtk_assistant_get_current_page (assistant);
+    auto page = gtk_assistant_get_nth_page (assistant, num);
+    gtk_assistant_set_page_complete (assistant, page, false);
+
     /* Load the data into the treeview. */
-    gnc_csv_preview_update_assist (info);
+    csv_import_refresh_preview_table (info);
+    csv_import_validate_preview_settings (info);
 
 }
 
@@ -2251,45 +2178,29 @@ csv_import_trans_assistant_account_match_page_prepare (GtkAssistant *assistant,
     gint            num = gtk_assistant_get_current_page (assistant);
     gchar          *text, *mtext;
 
-    info->settings_valid = preview_settings_valid (info);
-
-    if (!info->settings_valid && !info->skip_errors)
-    {
-        mtext = g_strdup_printf ("<span size=\"medium\" color=\"red\"><b>%s</b></span>", info->error_text.c_str());
-        gtk_label_set_markup (GTK_LABEL(info->account_match_label), mtext);
-        g_free (mtext);
-
-        // Disable the view when we have an error
-        gtk_widget_set_sensitive (info->account_match_view, FALSE);
-        gtk_widget_set_sensitive (info->account_match_btn, FALSE);
-        gtk_assistant_set_page_complete (assistant, info->account_match_page, FALSE);
-    }
-    else
-    {
-        auto store = gtk_tree_view_get_model (GTK_TREE_VIEW(info->account_match_view));
+    auto store = gtk_tree_view_get_model (GTK_TREE_VIEW(info->account_match_view));
 
-        // Load the account strings into the store
-        get_list_of_accounts (info, store);
+    // Load the account strings into the store
+    get_list_of_accounts (info, store);
 
-        // Match the account strings to the mappings
-        gnc_csv_account_map_load_mappings (store);
+    // Match the account strings to the mappings
+    gnc_csv_account_map_load_mappings (store);
 
-        text = g_strdup_printf (gettext ("To change mapping, Double Click on a row or select a row and press the button..."));
-        mtext = g_strdup_printf ("<span size=\"medium\" color=\"red\"><b>%s</b></span>", text);
-        gtk_label_set_markup (GTK_LABEL(info->account_match_label), mtext);
-        g_free (mtext);
-        g_free (text);
+    text = g_strdup_printf (gettext ("To change mapping, Double Click on a row or select a row and press the button..."));
+    mtext = g_strdup_printf ("<span size=\"medium\" color=\"red\"><b>%s</b></span>", text);
+    gtk_label_set_markup (GTK_LABEL(info->account_match_label), mtext);
+    g_free (mtext);
+    g_free (text);
 
-        // Enable the view, possibly after an error
-        gtk_widget_set_sensitive (info->account_match_view, TRUE);
-        gtk_widget_set_sensitive (info->account_match_btn, TRUE);
+    // Enable the view, possibly after an error
+    gtk_widget_set_sensitive (info->account_match_view, TRUE);
+    gtk_widget_set_sensitive (info->account_match_btn, TRUE);
 
-        /* Enable the Forward Assistant Button */
-        if (import_account_check_all (store))
-           gtk_assistant_set_page_complete (assistant, info->account_match_page, TRUE);
-        else
-            gtk_assistant_set_page_complete (assistant, info->account_match_page, FALSE);
-    }
+    /* Enable the Forward Assistant Button */
+    if (import_account_check_all (store))
+       gtk_assistant_set_page_complete (assistant, info->account_match_page, TRUE);
+    else
+        gtk_assistant_set_page_complete (assistant, info->account_match_page, FALSE);
 }
 
 
@@ -2621,9 +2532,6 @@ csv_import_trans_assistant_create (CsvImportTrans *info)
                                      FALSE);
     gtk_assistant_set_page_complete (GTK_ASSISTANT(window),
                                      GTK_WIDGET(gtk_builder_get_object (builder, "preview_page")),
-                                     TRUE);
-    gtk_assistant_set_page_complete (GTK_ASSISTANT(window),
-                                     GTK_WIDGET(gtk_builder_get_object (builder, "account_page")),
                                      FALSE);
     gtk_assistant_set_page_complete (GTK_ASSISTANT(window),
                                      GTK_WIDGET(gtk_builder_get_object (builder, "account_match_page")),
diff --git a/src/import-export/csv-imp/gnc-tx-import.cpp b/src/import-export/csv-imp/gnc-tx-import.cpp
index 72c3b16..7174ea6 100644
--- a/src/import-export/csv-imp/gnc-tx-import.cpp
+++ b/src/import-export/csv-imp/gnc-tx-import.cpp
@@ -245,6 +245,118 @@ void GncTxImport::tokenize (bool guessColTypes)
     }
 }
 
+/* Test for the required minimum number of columns selected and
+ * a valid date format.
+ * @return An empty string if all checks passed or the reason
+ *         verification failed otherwise.
+ */
+std::string GncTxImport::verify (void)
+{
+    auto newline = std::string();
+    auto error_text = std::string();
+
+    /* Check if the import file did actually contain any information */
+    if (parsed_lines.size() == 0)
+    {
+        error_text = _("No valid data found in the selected file. It may be empty or the selected encoding is wrong.");
+        return error_text;
+    }
+
+    /* Check if at least one line is selected for importing */
+    auto skip_alt_offset = skip_alt_lines ? 1 : 0;
+    if (skip_start_lines + skip_end_lines + skip_alt_offset >= parsed_lines.size())
+    {
+        error_text = _("No lines are selected for importing. Please reduce the number of lines to skip.");
+        return error_text;
+    }
+
+    /* Verify if a date column is selected and it's parsable.
+     */
+    if (!check_for_column_type(GncTransPropType::DATE))
+    {
+        error_text += newline + _("Please select a date column.");
+        newline = "\n";
+    }
+    else
+        /* Attempt to parse the date column for each selected line */
+        try
+        {
+            auto date_col = std::find(column_types.begin(),
+                    column_types.end(), GncTransPropType::DATE) -
+                            column_types.begin();
+            for (uint i = 0; i < parsed_lines.size(); i++)
+            {
+                if ((i < skip_start_lines) ||             // start rows to skip
+                    (i >= parsed_lines.size()
+                            - skip_end_lines) ||          // end rows to skip
+                    (((i - skip_start_lines) % 2 == 1) && // skip every second row...
+                     skip_alt_lines))                     // ...if requested
+                    continue;
+                else
+                {
+                    auto first_line = std::get<0>(parsed_lines[i]);
+                    auto date_str = first_line[date_col];
+                    if (!date_str.empty())
+                        parse_date (date_str, date_format);
+                }
+            }
+        }
+        catch (...)
+        {
+            error_text += newline + _("Not all dates could be parsed. Please verify your chosen date format or adjust the lines to skip.");
+            newline = "\n";
+        }
+
+    /* Verify if an account is selected either in the base account selector
+     * or via a column in the import data.
+     */
+    if (!check_for_column_type(GncTransPropType::ACCOUNT))
+    {
+        if (multi_split)
+        {
+            error_text += newline + _("Please select an account column.");
+            newline = "\n";
+        }
+        else if (!base_account)
+        {
+            error_text += newline + _("Please select an account column or set a base account in the Account field.");
+            newline = "\n";
+        }
+    }
+
+    /* Verify a description column is selected.
+     */
+    if (!check_for_column_type(GncTransPropType::DESCRIPTION))
+    {
+        error_text += newline + _("Please select a description column.");
+        newline = "\n";
+    }
+
+    /* Verify at least one amount column (deposit or withdrawal) column is selected.
+     */
+    if (!check_for_column_type(GncTransPropType::DEPOSIT) &&
+        !check_for_column_type(GncTransPropType::WITHDRAWAL))
+    {
+        error_text += newline + _("Please select a deposit or withdrawal column.");
+        newline = "\n";
+    }
+
+    /* Verify a transfer account is selected if any of the other transfer properties
+     * are selected.
+     */
+    if ((check_for_column_type(GncTransPropType::TACTION) ||
+         check_for_column_type(GncTransPropType::TMEMO) ||
+         check_for_column_type(GncTransPropType::TREC_STATE) ||
+         check_for_column_type(GncTransPropType::TREC_DATE)) &&
+        !check_for_column_type(GncTransPropType::TACCOUNT))
+    {
+        error_text += newline + _("Please select a transfer account column or remove the other transfer related columns.");
+        newline = "\n";
+    }
+
+    return error_text;
+}
+
 
 /** Checks whether the parsed line contains all essential properties.
  * Essential properties are
diff --git a/src/import-export/csv-imp/gnc-tx-import.hpp b/src/import-export/csv-imp/gnc-tx-import.hpp
index f56b138..1f9c4e2 100644
--- a/src/import-export/csv-imp/gnc-tx-import.hpp
+++ b/src/import-export/csv-imp/gnc-tx-import.hpp
@@ -103,6 +103,8 @@ public:
 
     void tokenize (bool guessColTypes);
 
+    std::string verify(void);
+
     /** This function will attempt to convert all tokenized lines into
      *  transactions using the column types the user has set.
      */

commit e5a175a2c7fc06f6433100d3d2fc08ba756eb04d
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Fri Dec 16 19:29:13 2016 +0100

    Move the base account selection to the preview page

diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index 462f45e..82e6327 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -95,7 +95,6 @@ typedef struct
     GtkWidget       *csv_button;                    /**< The widget for the CSV button */
     GtkWidget       *fixed_button;                  /**< The widget for the Fixed Width button */
     GtkWidget       *multi_split_cbutton;           /**< The widget for Multi-split */
-    int              home_account_number;           /**< The number of unique home account strings */
 
     GncTxImport     *parse_data;                    /**< The actual data we are previewing */
     GOCharmapSel    *encselector;                   /**< The widget for selecting the encoding */
@@ -121,11 +120,6 @@ typedef struct
     int              fixed_context_dx;              /**< The horizontal coordinate of the pixel in the header of the column
                                                        * the user has clicked */
 
-    GtkWidget            *account_page;             /**< Assistant account page widget, to be packed with account picker */
-    GtkWidget            *account_label;            /**< The account page label at bottom of page */
-    AccountPickerDialog  *account_picker;           /**< The AccountPickerDialog structure */
-    Account              *account;                  /**< Account returned by AccountPickerDialog */
-
     GtkWidget            *account_match_page;       /**< Assistant account matcher page widget */
     GtkWidget            *account_match_view;       /**< Assistant account matcher view widget */
     GtkWidget            *account_match_label;      /**< Assistant account matcher label widget */
@@ -178,7 +172,6 @@ void account_selected_cb (GtkWidget* widget, CsvImportTrans* info);
 void csv_import_trans_assistant_start_page_prepare (GtkAssistant *gtkassistant, gpointer user_data);
 void csv_import_trans_assistant_file_page_prepare (GtkAssistant *assistant, gpointer user_data);
 void csv_import_trans_assistant_preview_page_prepare (GtkAssistant *gtkassistant, gpointer user_data);
-void csv_import_trans_assistant_account_page_prepare (GtkAssistant *assistant, gpointer user_data);
 void csv_import_trans_assistant_account_match_page_prepare (GtkAssistant *assistant, gpointer user_data);
 void csv_import_trans_assistant_doc_page_prepare (GtkAssistant *assistant, gpointer user_data);
 void csv_import_trans_assistant_match_page_prepare (GtkAssistant *assistant, gpointer user_data);
@@ -1567,8 +1560,6 @@ bool get_list_of_accounts (CsvImportTrans* info, GtkTreeModel *store)
 {
     int      i, ncols = info->parse_data->column_types.size(); /* ncols is the number of columns in the data. */
     bool     have_accounts = false;
-    gint     home_account_number = 0;
-    gint     other_account_number = 0;
 
     /* ctstore contains the actual strings appearing in the column types treeview. */
     GtkTreeModel* ctstore = gtk_tree_view_get_model (info->ctreeview);
@@ -1611,11 +1602,6 @@ bool get_list_of_accounts (CsvImportTrans* info, GtkTreeModel *store)
                     // Append the entry
                     if (!check_for_duplicates (GTK_LIST_STORE(store), accstr))
                     {
-                        if (col_type == GncTransPropType::ACCOUNT) // Count the number of unique account strings
-                            home_account_number = home_account_number + 1;
-                        else
-                            other_account_number = other_account_number + 1;
-
                         gtk_list_store_append (GTK_LIST_STORE(store), &iter3);
                         gtk_list_store_set (GTK_LIST_STORE(store), &iter3, MAPPING_STRING, accstr,
                                             MAPPING_FULLPATH, _("No Linked Account"), MAPPING_ACCOUNT, NULL, -1);
@@ -1626,7 +1612,6 @@ bool get_list_of_accounts (CsvImportTrans* info, GtkTreeModel *store)
             }
         }
     }
-    info->home_account_number = home_account_number;
 
     return have_accounts;
 }
@@ -1821,6 +1806,17 @@ static void gnc_csv_preview_update_assist (CsvImportTrans* info)
     /* Set the date format to what's in the combo box (since we don't
      * necessarily know if this will always be the same). */
     info->parse_data->date_format = gtk_combo_box_get_active (GTK_COMBO_BOX(info->date_format_combo));
+
+    /* Set the upper limit of spin button to number of rows */
+    GtkAdjustment *adj;
+    adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(info->end_row_spin));
+    gtk_adjustment_set_upper (adj, info->parse_data->parsed_lines.size());
+    adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(info->start_row_spin));
+    gtk_adjustment_set_upper (adj, info->parse_data->parsed_lines.size());
+    if (gtk_adjustment_get_upper (adj) != info->parse_data->parsed_lines.size())
+
+    /* Update the row selection highlight */
+    row_selection_update (info);
 }
 
 
@@ -2009,73 +2005,11 @@ csv_import_trans_assistant_preview_page_prepare (GtkAssistant *assistant,
         // Load the account strings into the store
         store = gtk_tree_view_get_model (GTK_TREE_VIEW(info->account_match_view));
         gtk_list_store_clear (GTK_LIST_STORE(store)); // Clear list of accounts unless we are looking at errors
-        info->account = NULL; // Reset home account unless we are looking at errors
     }
 
     /* Load the data into the treeview. */
     gnc_csv_preview_update_assist (info);
 
-    /* Set the upper limit of spin button to number of rows */
-    GtkAdjustment *adj;
-    adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(info->end_row_spin));
-    gtk_adjustment_set_upper (adj, info->parse_data->parsed_lines.size());
-    adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(info->start_row_spin));
-    gtk_adjustment_set_upper (adj, info->parse_data->parsed_lines.size());
-    if (gtk_adjustment_get_upper (adj) != info->parse_data->parsed_lines.size())
-
-    /* Update the row selection highlight */
-    row_selection_update (info);
-}
-
-
-void
-csv_import_trans_assistant_account_page_prepare (GtkAssistant *assistant,
-        gpointer user_data)
-{
-    CsvImportTrans *info = (CsvImportTrans*) user_data;
-    gint num = gtk_assistant_get_current_page (assistant);
-    GtkWidget *page = gtk_assistant_get_nth_page (assistant, num);
-    gchar *text, *mtext;
-
-    info->settings_valid = preview_settings_valid (info);
-
-    if (!info->settings_valid && !info->skip_errors)
-    {
-        mtext = g_strdup_printf ("<span size=\"medium\" color=\"red\"><b>%s</b></span>", info->error_text.c_str());
-        gtk_label_set_markup (GTK_LABEL(info->account_label), mtext);
-        g_free (mtext);
-
-        // Disable the account picker when we have an error
-        gnc_import_account_assist_disable (info->account_picker, TRUE);
-        gtk_assistant_set_page_complete (assistant, page, FALSE);
-    }
-    else
-    {
-        // Check to see if we do not have an account column
-        if (!info->parse_data->check_for_column_type (GncTransPropType::ACCOUNT))
-        {
-            text = g_strdup_printf (gettext ("No Account column present, to select import account double click on the required account and then click Forward to proceed."));
-            mtext = g_strdup_printf ("<span size=\"medium\" color=\"red\"><b>%s</b></span>", text);
-            gtk_label_set_markup (GTK_LABEL(info->account_label), mtext);
-            g_free (mtext);
-            g_free (text);
-
-            // Enable the account picker, possibly after an error
-            gnc_import_account_assist_disable (info->account_picker, FALSE);
-            gtk_widget_set_sensitive (info->account_page, TRUE);
-
-            // Get account from picker, will be null to start but have value if we come back
-            info->account = gnc_import_account_assist_update (info->account_picker);
-
-            // If we have a valid account enable forward button
-            if (info->account == NULL)
-                gtk_assistant_set_page_complete (assistant, page, FALSE);
-            else
-                gtk_assistant_set_page_complete (assistant, page, TRUE);
-        }
-        else
-            gtk_assistant_set_page_complete (assistant, page, TRUE);
-    }
 }
 
 
@@ -2332,10 +2266,12 @@ csv_import_trans_assistant_account_match_page_prepare (GtkAssistant *assistant,
     }
     else
     {
-        GtkTreeModel *store;
+        auto store = gtk_tree_view_get_model (GTK_TREE_VIEW(info->account_match_view));
+
+        // Load the account strings into the store
+        get_list_of_accounts (info, store);
 
         // Match the account strings to the mappings
-        store = gtk_tree_view_get_model (GTK_TREE_VIEW(info->account_match_view));
         gnc_csv_account_map_load_mappings (store);
 
         text = g_strdup_printf (gettext ("To change mapping, Double Click on a row or select a row and press the button..."));
@@ -2491,46 +2427,15 @@ csv_import_trans_forward_page_func (gint current_page, gpointer user_data)
         case 2: //from preview page
             if (info->callcount == 1)
             {
-                GtkTreeModel *store;
-                bool          valid;
-
-                // Load the account strings into the store
-                store = gtk_tree_view_get_model (GTK_TREE_VIEW(info->account_match_view));
-                valid = get_list_of_accounts (info, store);
-
-                info->settings_valid = preview_settings_valid (info);
-
-                // Check to see if we have an account column
-                if (info->parse_data->check_for_column_type (GncTransPropType::ACCOUNT))
-                    next_page = 4;
-                else
-                    next_page = 3;
-
                 // Skip Errors set, goto to doc page
                 if (info->skip_errors)
-                    next_page = 5;
-
-                info->next_page = next_page;
-            }
-            else
-                next_page = info->next_page;
-            break;
-
-        case 3: //from account page
-            if (info->callcount == 1)
-            {
-                info->account = gnc_import_account_assist_update (info->account_picker);
-
+                    next_page = 4;
                 // Check to see if we have an account / other account columns
-                if (info->parse_data->check_for_column_type (GncTransPropType::ACCOUNT) ||
+                else if (info->parse_data->check_for_column_type (GncTransPropType::ACCOUNT) ||
                         info->parse_data->check_for_column_type (GncTransPropType::TACCOUNT))
-                    next_page = 4;
+                    next_page = 3;
                 else
-                    next_page = 5;
-
-                // Skip Errors set, goto to doc page
-                if (info->skip_errors)
-                    next_page = 5;
+                    next_page = 4;
 
                 info->next_page = next_page;
             }
@@ -2538,22 +2443,22 @@ csv_import_trans_forward_page_func (gint current_page, gpointer user_data)
                 next_page = info->next_page;
             break;
 
-        case 4: //from account match page
+        case 3: //from account match page
             if (info->callcount == 1)
             {
-                next_page = 5;
+                next_page = 4;
                 info->next_page = next_page;
             }
             else
                 next_page = info->next_page;
             break;
 
-        case 5: //from doc page
+        case 4: //from doc page
             if (info->callcount == 1)
             {
                 /* Create transactions from the parsed data, first time with false
                    Subsequent times with true */
-                    info->parse_data->create_transactions (info->account, info->match_parse_run);
+                    info->parse_data->create_transactions (info->match_parse_run);
 
                 /* if there are errors, we jump back to preview to correct */
                 if (info->parse_data->parse_errors && !info->skip_errors)
@@ -2562,7 +2467,7 @@ csv_import_trans_forward_page_func (gint current_page, gpointer user_data)
                     next_page = 2;
                 }
                 else
-                    next_page = 6;
+                    next_page = 5;
 
                 info->next_page = next_page;
             }
@@ -2570,20 +2475,20 @@ csv_import_trans_forward_page_func (gint current_page, gpointer user_data)
                 next_page = info->next_page;
             break;
 
-        case 6: //from match page
+        case 5: //from match page
             if (info->callcount == 1)
             {
-                next_page = 7;
+                next_page = 6;
                 info->next_page = next_page;
             }
             else
                 next_page = info->next_page;
             break;
 
-        case 7: //from summary page
+        case 6: //from summary page
             if (info->callcount == 1)
             {
-                next_page = 8;
+                next_page = 7;
                 info->next_page = next_page;
             }
             else
@@ -2612,8 +2517,6 @@ csv_import_trans_assistant_prepare (GtkAssistant *assistant, GtkWidget *page,
         csv_import_trans_assistant_file_page_prepare (assistant, user_data);
     else if (page == info->preview_page)
         csv_import_trans_assistant_preview_page_prepare (assistant, user_data);
-    else if (page == info->account_page)
-        csv_import_trans_assistant_account_page_prepare (assistant, user_data);
     else if (page == info->account_match_page)
         csv_import_trans_assistant_account_match_page_prepare (assistant, user_data);
     else if (page == info->doc_page)
@@ -2677,9 +2580,6 @@ csv_import_trans_close_handler (gpointer user_data)
     if (!(info->parse_data == NULL))
         delete info->parse_data;
 
-    if (!(info->account_picker == NULL))
-        info->account_picker = NULL;
-
     if (!(info->gnc_csv_importer_gui == NULL))
         info->gnc_csv_importer_gui = NULL;
 
@@ -2909,12 +2809,6 @@ csv_import_trans_assistant_create (CsvImportTrans *info)
         info->encoding_selected_called = false;
     }
 
-    /* Account page */
-    /* Initialize the Account Picker and add to the Assistant */
-    info->account_page  = GTK_WIDGET(gtk_builder_get_object (builder, "account_page"));
-    info->account_picker = gnc_import_account_assist_setup (info->account_page);
-    info->account_label = GTK_WIDGET(gtk_builder_get_object (builder, "account_page_label"));
-
     /* Account Match Page */
     info->account_match_page  = GTK_WIDGET(gtk_builder_get_object (builder, "account_match_page"));
     info->account_match_view  = GTK_WIDGET(gtk_builder_get_object (builder, "account_match_view"));
diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.glade b/src/import-export/csv-imp/assistant-csv-trans-import.glade
index 54096fa..3b1c172 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.glade
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.glade
@@ -33,9 +33,6 @@
     <signal name="prepare" handler="csv_import_trans_assistant_prepare" swapped="no"/>
     <signal name="cancel" handler="csv_import_trans_assistant_cancel" swapped="no"/>
     <child>
-      <placeholder/>
-    </child>
-    <child>
       <object class="GtkLabel" id="start_page">
         <property name="visible">True</property>
         <property name="can_focus">False</property>
@@ -153,65 +150,31 @@ Select location and file name for the Import, then click 'OK'...
                     <property name="can_focus">False</property>
                     <property name="spacing">5</property>
                     <child>
-                      <object class="GtkFrame" id="frame10">
+                      <object class="GtkVBox" id="vbox2">
                         <property name="visible">True</property>
                         <property name="can_focus">False</property>
-                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                        <property name="label_xalign">0</property>
-                        <property name="shadow_type">none</property>
                         <child>
-                          <object class="GtkAlignment" id="alignment6">
+                          <object class="GtkFrame" id="frame10">
                             <property name="visible">True</property>
                             <property name="can_focus">False</property>
                             <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                            <property name="left_padding">5</property>
-                            <property name="right_padding">5</property>
+                            <property name="label_xalign">0</property>
+                            <property name="shadow_type">none</property>
                             <child>
-                              <object class="GtkVBox" id="vbox9">
+                              <object class="GtkAlignment" id="alignment6">
                                 <property name="visible">True</property>
                                 <property name="can_focus">False</property>
-                                <child>
-                                  <object class="GtkRadioButton" id="fixed_button">
-                                    <property name="label" translatable="yes">Fixed-Width</property>
-                                    <property name="visible">True</property>
-                                    <property name="can_focus">True</property>
-                                    <property name="receives_default">False</property>
-                                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                                    <property name="draw_indicator">True</property>
-                                    <property name="group">csv_button</property>
-                                  </object>
-                                  <packing>
-                                    <property name="expand">False</property>
-                                    <property name="fill">True</property>
-                                    <property name="position">0</property>
-                                  </packing>
-                                </child>
-                                <child>
-                                  <object class="GtkRadioButton" id="csv_button">
-                                    <property name="label" translatable="yes">Separators</property>
-                                    <property name="visible">True</property>
-                                    <property name="can_focus">True</property>
-                                    <property name="receives_default">False</property>
-                                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                                    <property name="active">True</property>
-                                    <property name="draw_indicator">True</property>
-                                  </object>
-                                  <packing>
-                                    <property name="expand">False</property>
-                                    <property name="fill">True</property>
-                                    <property name="position">1</property>
-                                  </packing>
-                                </child>
+                                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                                <property name="left_padding">5</property>
+                                <property name="right_padding">5</property>
                                 <child>
                                   <object class="GtkTable" id="table2">
                                     <property name="visible">True</property>
                                     <property name="can_focus">False</property>
                                     <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                                    <property name="n_rows">3</property>
+                                    <property name="n_rows">7</property>
                                     <property name="n_columns">3</property>
                                     <property name="column_spacing">3</property>
-                                    <property name="row_spacing">3</property>
-                                    <property name="homogeneous">True</property>
                                     <child>
                                       <object class="GtkCheckButton" id="space_cbutton">
                                         <property name="label" translatable="yes">Space</property>
@@ -222,6 +185,8 @@ Select location and file name for the Import, then click 'OK'...
                                         <property name="draw_indicator">True</property>
                                       </object>
                                       <packing>
+                                        <property name="top_attach">2</property>
+                                        <property name="bottom_attach">3</property>
                                         <property name="x_options">GTK_FILL</property>
                                         <property name="y_options">GTK_FILL</property>
                                       </packing>
@@ -238,6 +203,8 @@ Select location and file name for the Import, then click 'OK'...
                                       <packing>
                                         <property name="left_attach">1</property>
                                         <property name="right_attach">2</property>
+                                        <property name="top_attach">2</property>
+                                        <property name="bottom_attach">3</property>
                                         <property name="x_options">GTK_FILL</property>
                                         <property name="y_options">GTK_FILL</property>
                                       </packing>
@@ -255,6 +222,8 @@ Select location and file name for the Import, then click 'OK'...
                                       <packing>
                                         <property name="left_attach">2</property>
                                         <property name="right_attach">3</property>
+                                        <property name="top_attach">2</property>
+                                        <property name="bottom_attach">3</property>
                                         <property name="x_options">GTK_FILL</property>
                                         <property name="y_options">GTK_FILL</property>
                                       </packing>
@@ -269,8 +238,8 @@ Select location and file name for the Import, then click 'OK'...
                                         <property name="draw_indicator">True</property>
                                       </object>
                                       <packing>
-                                        <property name="top_attach">1</property>
-                                        <property name="bottom_attach">2</property>
+                                        <property name="top_attach">3</property>
+                                        <property name="bottom_attach">4</property>
                                         <property name="x_options">GTK_FILL</property>
                                         <property name="y_options">GTK_FILL</property>
                                       </packing>
@@ -287,8 +256,8 @@ Select location and file name for the Import, then click 'OK'...
                                       <packing>
                                         <property name="left_attach">1</property>
                                         <property name="right_attach">2</property>
-                                        <property name="top_attach">1</property>
-                                        <property name="bottom_attach">2</property>
+                                        <property name="top_attach">3</property>
+                                        <property name="bottom_attach">4</property>
                                         <property name="x_options">GTK_FILL</property>
                                         <property name="y_options">GTK_FILL</property>
                                       </packing>
@@ -305,8 +274,8 @@ Select location and file name for the Import, then click 'OK'...
                                       <packing>
                                         <property name="left_attach">2</property>
                                         <property name="right_attach">3</property>
-                                        <property name="top_attach">1</property>
-                                        <property name="bottom_attach">2</property>
+                                        <property name="top_attach">3</property>
+                                        <property name="bottom_attach">4</property>
                                         <property name="x_options">GTK_FILL</property>
                                         <property name="y_options">GTK_FILL</property>
                                       </packing>
@@ -321,8 +290,8 @@ Select location and file name for the Import, then click 'OK'...
                                         <property name="draw_indicator">True</property>
                                       </object>
                                       <packing>
-                                        <property name="top_attach">2</property>
-                                        <property name="bottom_attach">3</property>
+                                        <property name="top_attach">4</property>
+                                        <property name="bottom_attach">5</property>
                                         <property name="x_options">GTK_FILL</property>
                                         <property name="y_options">GTK_FILL</property>
                                       </packing>
@@ -342,61 +311,140 @@ Select location and file name for the Import, then click 'OK'...
                                       <packing>
                                         <property name="left_attach">1</property>
                                         <property name="right_attach">3</property>
-                                        <property name="top_attach">2</property>
-                                        <property name="bottom_attach">3</property>
+                                        <property name="top_attach">4</property>
+                                        <property name="bottom_attach">5</property>
                                         <property name="x_options">GTK_FILL</property>
                                         <property name="y_options">GTK_FILL</property>
                                       </packing>
                                     </child>
-                                  </object>
-                                  <packing>
-                                    <property name="expand">False</property>
-                                    <property name="fill">True</property>
-                                    <property name="position">2</property>
-                                  </packing>
-                                </child>
-                                <child>
-                                  <object class="GtkHSeparator" id="hseparator4">
-                                    <property name="visible">True</property>
-                                    <property name="can_focus">False</property>
-                                  </object>
-                                  <packing>
-                                    <property name="expand">False</property>
-                                    <property name="fill">True</property>
-                                    <property name="position">3</property>
-                                  </packing>
-                                </child>
-                                <child>
-                                  <object class="GtkCheckButton" id="multi_split_button">
-                                    <property name="label" translatable="yes">Multi-split</property>
-                                    <property name="visible">True</property>
-                                    <property name="can_focus">True</property>
-                                    <property name="receives_default">False</property>
-                                    <property name="tooltip_text" translatable="yes">Normally the importer will assume each line in the input file will correspond to one transaction. Each line can have information for one transaction and one or two splits.
+                                    <child>
+                                      <object class="GtkRadioButton" id="csv_button">
+                                        <property name="label" translatable="yes">Separators</property>
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">True</property>
+                                        <property name="receives_default">False</property>
+                                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                                        <property name="active">True</property>
+                                        <property name="draw_indicator">True</property>
+                                      </object>
+                                    </child>
+                                    <child>
+                                      <object class="GtkRadioButton" id="fixed_button">
+                                        <property name="label" translatable="yes">Fixed-Width</property>
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">True</property>
+                                        <property name="receives_default">False</property>
+                                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                                        <property name="draw_indicator">True</property>
+                                        <property name="group">csv_button</property>
+                                      </object>
+                                      <packing>
+                                        <property name="left_attach">1</property>
+                                        <property name="right_attach">2</property>
+                                      </packing>
+                                    </child>
+                                    <child>
+                                      <object class="GtkHSeparator" id="hseparator1">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">False</property>
+                                      </object>
+                                      <packing>
+                                        <property name="right_attach">3</property>
+                                        <property name="top_attach">1</property>
+                                        <property name="bottom_attach">2</property>
+                                        <property name="y_options">GTK_FILL</property>
+                                      </packing>
+                                    </child>
+                                    <child>
+                                      <object class="GtkHSeparator" id="hseparator4">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">False</property>
+                                      </object>
+                                      <packing>
+                                        <property name="right_attach">3</property>
+                                        <property name="top_attach">5</property>
+                                        <property name="bottom_attach">6</property>
+                                        <property name="y_options">GTK_FILL</property>
+                                      </packing>
+                                    </child>
+                                    <child>
+                                      <object class="GtkCheckButton" id="multi_split_button">
+                                        <property name="label" translatable="yes">Multi-split</property>
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">True</property>
+                                        <property name="receives_default">False</property>
+                                        <property name="tooltip_text" translatable="yes">Normally the importer will assume each line in the input file will correspond to one transaction. Each line can have information for one transaction and one or two splits.
 
 When Multi-split is enabled the importer will assume multiple consecutive lines together hold the information for one transaction. Each line provides information for exactly one split. The first line should also provide the information for the transaction.
 To know which lines belong to the same transaction, the importer will compare the provided transaction information in each line. If that information is empty or the same as the first transaction line the importer will consider this line part of the same transaction.</property>
-                                    <property name="draw_indicator">True</property>
-                                    <signal name="toggled" handler="csv_import_trans_multisplit_cb" swapped="no"/>
+                                        <property name="draw_indicator">True</property>
+                                        <signal name="toggled" handler="csv_import_trans_multisplit_cb" swapped="no"/>
+                                      </object>
+                                      <packing>
+                                        <property name="right_attach">3</property>
+                                        <property name="top_attach">6</property>
+                                        <property name="bottom_attach">7</property>
+                                      </packing>
+                                    </child>
+                                    <child>
+                                      <placeholder/>
+                                    </child>
                                   </object>
-                                  <packing>
-                                    <property name="expand">True</property>
-                                    <property name="fill">True</property>
-                                    <property name="position">4</property>
-                                  </packing>
                                 </child>
                               </object>
                             </child>
+                            <child type="label">
+                              <object class="GtkLabel" id="label19">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                                <property name="label" translatable="yes"><b>File Format</b></property>
+                                <property name="use_markup">True</property>
+                              </object>
+                            </child>
                           </object>
+                          <packing>
+                            <property name="expand">True</property>
+                            <property name="fill">True</property>
+                            <property name="position">0</property>
+                          </packing>
                         </child>
-                        <child type="label">
-                          <object class="GtkLabel" id="label19">
+                        <child>
+                          <object class="GtkFrame" id="frame1">
                             <property name="visible">True</property>
                             <property name="can_focus">False</property>
-                            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                            <property name="label" translatable="yes"><b>File Format</b></property>
-                            <property name="use_markup">True</property>
+                            <property name="label_xalign">0</property>
+                            <property name="shadow_type">none</property>
+                            <child>
+                              <object class="GtkAlignment" id="alignment1">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="left_padding">5</property>
+                                <child>
+                                  <object class="GtkHBox" id="account_hbox">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">False</property>
+                                    <child>
+                                      <placeholder/>
+                                    </child>
+                                  </object>
+                                </child>
+                              </object>
+                            </child>
+                            <child type="label">
+                              <object class="GtkLabel" id="label1">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="label" translatable="yes"><b>Account</b></property>
+                                <property name="use_markup">True</property>
+                              </object>
+                            </child>
                           </object>
+                          <packing>
+                            <property name="expand">True</property>
+                            <property name="fill">True</property>
+                            <property name="position">1</property>
+                          </packing>
                         </child>
                       </object>
                       <packing>
@@ -426,7 +474,7 @@ To know which lines belong to the same transaction, the importer will compare th
                                   <object class="GtkTable" id="table3">
                                     <property name="visible">True</property>
                                     <property name="can_focus">False</property>
-                                    <property name="n_rows">5</property>
+                                    <property name="n_rows">6</property>
                                     <property name="n_columns">2</property>
                                     <property name="column_spacing">5</property>
                                     <property name="row_spacing">5</property>
@@ -516,8 +564,8 @@ To know which lines belong to the same transaction, the importer will compare th
                                         <property name="label" translatable="yes">Leading Lines to Skip</property>
                                       </object>
                                       <packing>
-                                        <property name="top_attach">3</property>
-                                        <property name="bottom_attach">4</property>
+                                        <property name="top_attach">4</property>
+                                        <property name="bottom_attach">5</property>
                                         <property name="x_options">GTK_FILL</property>
                                       </packing>
                                     </child>
@@ -529,8 +577,8 @@ To know which lines belong to the same transaction, the importer will compare th
                                         <property name="label" translatable="yes">Trailing Lines to Skip</property>
                                       </object>
                                       <packing>
-                                        <property name="top_attach">4</property>
-                                        <property name="bottom_attach">5</property>
+                                        <property name="top_attach">5</property>
+                                        <property name="bottom_attach">6</property>
                                         <property name="x_options">GTK_FILL</property>
                                       </packing>
                                     </child>
@@ -565,8 +613,8 @@ To know which lines belong to the same transaction, the importer will compare th
                                       <packing>
                                         <property name="left_attach">1</property>
                                         <property name="right_attach">2</property>
-                                        <property name="top_attach">3</property>
-                                        <property name="bottom_attach">4</property>
+                                        <property name="top_attach">4</property>
+                                        <property name="bottom_attach">5</property>
                                       </packing>
                                     </child>
                                     <child>
@@ -600,8 +648,19 @@ To know which lines belong to the same transaction, the importer will compare th
                                       <packing>
                                         <property name="left_attach">1</property>
                                         <property name="right_attach">2</property>
-                                        <property name="top_attach">4</property>
-                                        <property name="bottom_attach">5</property>
+                                        <property name="top_attach">5</property>
+                                        <property name="bottom_attach">6</property>
+                                      </packing>
+                                    </child>
+                                    <child>
+                                      <object class="GtkHSeparator" id="hseparator2">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">False</property>
+                                      </object>
+                                      <packing>
+                                        <property name="right_attach">2</property>
+                                        <property name="top_attach">3</property>
+                                        <property name="bottom_attach">4</property>
                                       </packing>
                                     </child>
                                   </object>
@@ -625,7 +684,7 @@ For example
                                     <signal name="toggled" handler="csv_import_trans_skiprows_cb" swapped="no"/>
                                   </object>
                                   <packing>
-                                    <property name="expand">True</property>
+                                    <property name="expand">False</property>
                                     <property name="fill">True</property>
                                     <property name="position">1</property>
                                   </packing>
@@ -641,6 +700,7 @@ For example
                           <object class="GtkLabel" id="label13">
                             <property name="visible">True</property>
                             <property name="can_focus">False</property>
+                            <property name="label" translatable="yes"><b>Miscellaneous</b></property>
                             <property name="use_markup">True</property>
                           </object>
                         </child>
@@ -806,31 +866,8 @@ For example
         </child>
       </object>
       <packing>
-        <property name="title" translatable="yes">Preview Settings</property>
-      </packing>
-    </child>
-    <child>
-      <object class="GtkVBox" id="account_page">
-        <property name="visible">True</property>
-        <property name="can_focus">False</property>
-        <property name="border_width">12</property>
-        <child>
-          <object class="GtkLabel" id="account_page_label">
-            <property name="visible">True</property>
-            <property name="can_focus">False</property>
-            <property name="xalign">0</property>
-            <property name="label" translatable="yes">Error text.</property>
-          </object>
-          <packing>
-            <property name="expand">False</property>
-            <property name="fill">False</property>
-            <property name="pack_type">end</property>
-            <property name="position">2</property>
-          </packing>
-        </child>
-      </object>
-      <packing>
-        <property name="title" translatable="yes">Account Selection</property>
+        <property name="page_type">intro</property>
+        <property name="complete">True</property>
       </packing>
     </child>
     <child>
@@ -1030,41 +1067,5 @@ More information can be displayed by using the help button.</property>
         <property name="complete">True</property>
       </packing>
     </child>
-    <child>
-      <placeholder/>
-    </child>
-    <child>
-      <placeholder/>
-    </child>
-    <child>
-      <placeholder/>
-    </child>
-    <child>
-      <placeholder/>
-    </child>
-    <child>
-      <placeholder/>
-    </child>
-    <child>
-      <placeholder/>
-    </child>
-    <child>
-      <placeholder/>
-    </child>
-    <child>
-      <placeholder/>
-    </child>
-    <child>
-      <placeholder/>
-    </child>
-    <child>
-      <placeholder/>
-    </child>
-    <child>
-      <placeholder/>
-    </child>
-    <child>
-      <placeholder/>
-    </child>
   </object>
 </interface>
diff --git a/src/import-export/csv-imp/gnc-csv-trans-settings.cpp b/src/import-export/csv-imp/gnc-csv-trans-settings.cpp
index bf94252..d7f4cb5 100644
--- a/src/import-export/csv-imp/gnc-csv-trans-settings.cpp
+++ b/src/import-export/csv-imp/gnc-csv-trans-settings.cpp
@@ -274,6 +274,10 @@ CsvTransSettings::load (void)
                         "Inserting column type 'NONE' instead'.",
                         col_types_it->second, multi_split ? "enabled" : "disabled");
         }
+        else
+            PWARN("Found invalid column type '%s'. Inserting column type 'NONE' instead'.",
+                    col_types_str[i]);
+
     }
     if (col_types_str)
         g_strfreev (col_types_str);
diff --git a/src/import-export/csv-imp/gnc-tx-import.cpp b/src/import-export/csv-imp/gnc-tx-import.cpp
index d07bb5c..72c3b16 100644
--- a/src/import-export/csv-imp/gnc-tx-import.cpp
+++ b/src/import-export/csv-imp/gnc-tx-import.cpp
@@ -147,6 +147,32 @@ void GncTxImport::set_multi_split (bool multi_split_val)
         if (san_prop != *col_it)
             *col_it = san_prop;
     }
+    if (multi_split)
+        base_account = nullptr;
+}
+
+/** Sets a base account. This is the account all import data relates to.
+ *  As such at least one split of each transaction that will be generated
+ *  will be in this account.
+ *  When a base account is set, there can't be an account column selected
+ *  in the import data.
+ *  In multi-split mode the user has to select an account column so in
+ *  that mode the base_account can't be set.
+ * @param base_acct Pointer to an account or NULL.
+ */
+void GncTxImport::set_base_account (Account* base_acct)
+{
+    if (multi_split)
+        return;
+
+    base_account = base_acct;
+
+    if (base_account)
+    {
+        auto col_type = std::find (column_types.begin(), column_types.end(), GncTransPropType::ACCOUNT);
+        if (col_type != column_types.end())
+            *col_type = GncTransPropType::NONE;
+    }
 }
 
 /** Loads a file into a GncTxImport. This is the first function
@@ -350,6 +376,7 @@ void GncTxImport::create_transaction (std::vector<parse_line_t>::iterator& parse
             PINFO("User warning: %s", error_message.c_str());
         }
     }
+    std::get<2>(*parsed_line) = trans_props;
 
     /* For multi-split input data, we need to check whether this line is part of a transaction that
      * has already be started by a previous line. */
@@ -370,7 +397,6 @@ void GncTxImport::create_transaction (std::vector<parse_line_t>::iterator& parse
         }
         else
         {
-            std::get<2>(*parsed_line) = trans_props;
             /* This line starts a new transaction, set it as parent for
              * subsequent lines. */
             parent = trans_props;
@@ -428,8 +454,7 @@ void GncTxImport::create_transaction (std::vector<parse_line_t>::iterator& parse
  * @param account Account with which transactions are created
  * @param redo_errors true to convert only error data, false to convert all data
  */
-void GncTxImport::create_transactions (Account* account,
-                                       bool redo_errors)
+void GncTxImport::create_transactions (bool redo_errors)
 {
     /* If a full conversion is requested (as opposed to only
      * attempting to convers the lines which had errors in the previous run)
@@ -451,7 +476,6 @@ void GncTxImport::create_transactions (Account* account,
     auto parsed_lines_max = parsed_lines.begin();
     std::advance(parsed_lines_max, parsed_lines.size() - skip_end_lines);
 
-    base_account = account;
     auto odd_line = false;
     parse_errors = false;
     parent = nullptr;
diff --git a/src/import-export/csv-imp/gnc-tx-import.hpp b/src/import-export/csv-imp/gnc-tx-import.hpp
index ab36c37..f56b138 100644
--- a/src/import-export/csv-imp/gnc-tx-import.hpp
+++ b/src/import-export/csv-imp/gnc-tx-import.hpp
@@ -99,13 +99,14 @@ public:
     void convert_encoding (const std::string& encoding);
 
     void set_multi_split (bool multi_split_val);
+    void set_base_account (Account *base_acct);
 
     void tokenize (bool guessColTypes);
 
     /** This function will attempt to convert all tokenized lines into
      *  transactions using the column types the user has set.
      */
-    void create_transactions (Account* account, bool redo_errors);
+    void create_transactions (bool redo_errors);
     bool check_for_column_type (GncTransPropType type);
 
     std::unique_ptr<GncTokenizer> tokenizer;    /**< Will handle file loading/encoding conversion/splitting into fields */

commit e6e36d648b8a211236c58755ad1907400e06bb04
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Fri Dec 16 13:48:08 2016 +0100

    Add account selector to the preview window

diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index 7fc0e5c..462f45e 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -51,6 +51,7 @@ extern "C"
 #include "import-account-matcher.h"
 #include "import-main-matcher.h"
 #include "gnc-csv-account-map.h"
+#include "gnc-account-sel.h"
 
 #include "gnc-csv-gnumeric-popup.h"
 #include "go-charmap-sel.h"
@@ -85,6 +86,7 @@ typedef struct
     GtkWidget       *settings_combo;                /**< The Settings Combo */
     GtkWidget       *save_button;                   /**< The Save Settings button */
     GtkWidget       *del_button;                    /**< The Delete Settings button */
+    GtkWidget       *acct_selector;                 /**< The Account selector */
     GtkWidget       *combo_hbox;                    /**< The Settings Combo hbox */
     GtkWidget       *check_butt;                    /**< The widget for the check label button */
     GtkWidget       *start_row_spin;                /**< The widget for the start row spinner */
@@ -170,6 +172,7 @@ void csv_import_trans_save_settings_cb (GtkWidget *button, CsvImportTrans *info)
 void csv_import_trans_changed_settings_cb (GtkWidget *combo, CsvImportTrans *info);
 void csv_import_trans_changed_settings_text_cb (GtkWidget *entry, CsvImportTrans *info);
 void sep_button_clicked (GtkWidget* widget, CsvImportTrans* info);
+void account_selected_cb (GtkWidget* widget, CsvImportTrans* info);
 }
 
 void csv_import_trans_assistant_start_page_prepare (GtkAssistant *gtkassistant, gpointer user_data);
@@ -352,6 +355,16 @@ csv_import_trans_load_settings (CsvImportTrans *info)
                         2 * i + 1, col_type,
                         -1);
     }
+
+    /* The user can't both select a base account and an account column.
+     * So now the column_types vector is updated also update the base account
+     * selector's availability.
+     */
+    if (info->parse_data->check_for_column_type(GncTransPropType::ACCOUNT))
+    {
+        gnc_account_sel_set_account(GNC_ACCOUNT_SEL(info->acct_selector), nullptr, false);
+        info->parse_data->set_base_account(nullptr);
+    }
 }
 
 static void handle_save_del_sensitivity (GtkWidget* combo, CsvImportTrans *info)
@@ -911,6 +924,16 @@ void sep_button_clicked (GtkWidget* widget, CsvImportTrans* info)
     row_selection_update (info);
 }
 
+void account_selected_cb (GtkWidget* widget, CsvImportTrans* info)
+{
+
+    auto acct = gnc_account_sel_get_account( GNC_ACCOUNT_SEL(widget) );
+    info->parse_data->set_base_account(acct);
+    /* Update the preview. */
+    gnc_csv_preview_update_assist (info);
+}
+
+
 
 /** Event handler for clicking one of the format type radio
  * buttons. This occurs if the format (Fixed-Width or CSV) is changed.
@@ -1313,6 +1336,7 @@ static void column_type_changed (GtkCellRenderer* renderer, gchar* path,
                         2 * i, _(gnc_csv_col_type_strs[GncTransPropType::NONE]),
                         2 * i + 1, GncTransPropType::NONE,
                         -1);
+                info->parse_data->column_types.at(i) = GncTransPropType::NONE;
             }
         }
         else /* If this is the column that was changed ... */
@@ -1324,8 +1348,19 @@ static void column_type_changed (GtkCellRenderer* renderer, gchar* path,
                     2 * i, new_text,
                     2 * i + 1, new_col_type,
                     -1);
+            info->parse_data->column_types.at(i) = new_col_type;
         }
     }
+
+    /* The user can't both select a base account and an account column.
+     * So now the column_types vector is updated also update the base account
+     * selector's availability.
+     */
+    if (info->parse_data->check_for_column_type(GncTransPropType::ACCOUNT))
+    {
+        gnc_account_sel_set_account(GNC_ACCOUNT_SEL(info->acct_selector), nullptr, false);
+        info->parse_data->set_base_account(nullptr);
+    }
 }
 
 static void
@@ -1427,9 +1462,6 @@ bool preview_settings_valid (CsvImportTrans* info)
          * col_type_str 0, col_type, col_type_str 1, col_type 1, ..., col_type_str ncols, col_type ncols. */
         gtk_tree_model_get (ctstore, &iter1, 2 * i + 1, &col_type, -1);
 
-        /* Set the column_types array appropriately*/
-        info->parse_data->column_types[i] = col_type;
-
         switch (col_type)
         {
         case GncTransPropType::DATE:
@@ -2804,6 +2836,16 @@ csv_import_trans_assistant_create (CsvImportTrans *info)
         g_signal_connect (G_OBJECT(info->custom_entry), "changed",
                          G_CALLBACK(sep_button_clicked), (gpointer)info);
 
+        /* Add account selection widget */
+        info->acct_selector = gnc_account_sel_new();
+        auto account_hbox = GTK_WIDGET(gtk_builder_get_object (builder, "account_hbox"));
+        gtk_box_pack_start (GTK_BOX(account_hbox), info->acct_selector, TRUE, TRUE, 6);
+        gtk_widget_show (info->acct_selector);
+
+
+        g_signal_connect(G_OBJECT(info->acct_selector), "account_sel_changed",
+                         G_CALLBACK(account_selected_cb), (gpointer)info);
+
 
         /* Create the encoding selector widget and add it to the assistant */
         info->encselector = GO_CHARMAP_SEL(go_charmap_sel_new(GO_CHARMAP_SEL_TO_UTF8));

commit 550a431cdc102c8208c6963bcc9cce74aad59a13
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Fri Dec 16 13:47:08 2016 +0100

    Some variable declaration cleanups

diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index 1692199..7fc0e5c 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -2729,21 +2729,10 @@ csv_import_trans_assistant_create (CsvImportTrans *info)
 
     /* Preview Settings Page */
     {
-        const char* sep_button_names[] = {"space_cbutton",
-                                          "tab_cbutton",
-                                          "comma_cbutton",
-                                          "colon_cbutton",
-                                          "semicolon_cbutton",
-                                          "hyphen_cbutton"
-        };
-        GtkContainer *date_format_container, *currency_format_container, *encoding_container;
-        int           i;
-        GtkListStore *settings_store;
-
         info->preview_page = GTK_WIDGET(gtk_builder_get_object (builder, "preview_page"));
 
         // Add Settings combo
-        settings_store = gtk_list_store_new (2, G_TYPE_POINTER, G_TYPE_STRING);
+        auto settings_store = gtk_list_store_new (2, G_TYPE_POINTER, G_TYPE_STRING);
         info->settings_combo = gtk_combo_box_new_with_model_and_entry (GTK_TREE_MODEL(settings_store));
         gtk_combo_box_set_entry_text_column (GTK_COMBO_BOX(info->settings_combo), SET_NAME);
         gtk_combo_box_set_active (GTK_COMBO_BOX(info->settings_combo), 0);
@@ -2785,7 +2774,15 @@ csv_import_trans_assistant_create (CsvImportTrans *info)
 
         /* Load the separator buttons from the glade builder file into the
          * info->sep_buttons array. */
-        for (i = 0; i < SEP_NUM_OF_TYPES; i++)
+        const char* sep_button_names[] = {
+                "space_cbutton",
+                "tab_cbutton",
+                "comma_cbutton",
+                "colon_cbutton",
+                "semicolon_cbutton",
+                "hyphen_cbutton"
+            };
+        for (int i = 0; i < SEP_NUM_OF_TYPES; i++)
         {
             info->sep_buttons[i]
                 = (GtkCheckButton*)GTK_WIDGET(gtk_builder_get_object (builder, sep_button_names[i]));
@@ -2814,7 +2811,7 @@ csv_import_trans_assistant_create (CsvImportTrans *info)
         g_signal_connect (G_OBJECT(info->encselector), "charmap_changed",
                          G_CALLBACK(encoding_selected), (gpointer)info);
 
-        encoding_container = GTK_CONTAINER(gtk_builder_get_object (builder, "encoding_container"));
+        auto encoding_container = GTK_CONTAINER(gtk_builder_get_object (builder, "encoding_container"));
         gtk_container_add (encoding_container, GTK_WIDGET(info->encselector));
         gtk_widget_show_all (GTK_WIDGET(encoding_container));
 
@@ -2824,7 +2821,7 @@ csv_import_trans_assistant_create (CsvImportTrans *info)
 
         /* Add in the date format combo box and hook it up to an event handler. */
         info->date_format_combo = GTK_COMBO_BOX_TEXT(gtk_combo_box_text_new());
-        for (i = 0; i < num_date_formats; i++)
+        for (int i = 0; i < num_date_formats; i++)
         {
             gtk_combo_box_text_append_text (info->date_format_combo, _(date_format_user[i]));
         }
@@ -2833,13 +2830,13 @@ csv_import_trans_assistant_create (CsvImportTrans *info)
                          G_CALLBACK(date_format_selected), (gpointer)info);
 
         /* Add it to the assistant. */
-        date_format_container = GTK_CONTAINER(gtk_builder_get_object (builder, "date_format_container"));
+        auto date_format_container = GTK_CONTAINER(gtk_builder_get_object (builder, "date_format_container"));
         gtk_container_add (date_format_container, GTK_WIDGET(info->date_format_combo));
         gtk_widget_show_all (GTK_WIDGET(date_format_container));
 
         /* Add in the currency format combo box and hook it up to an event handler. */
         info->currency_format_combo = GTK_COMBO_BOX_TEXT(gtk_combo_box_text_new());
-        for (i = 0; i < num_currency_formats; i++)
+        for (int i = 0; i < num_currency_formats; i++)
         {
             gtk_combo_box_text_append_text (info->currency_format_combo, _(currency_format_user[i]));
         }
@@ -2849,7 +2846,7 @@ csv_import_trans_assistant_create (CsvImportTrans *info)
                          G_CALLBACK(currency_format_selected), (gpointer)info);
 
         /* Add it to the assistant. */
-        currency_format_container = GTK_CONTAINER(gtk_builder_get_object (builder, "currency_format_container"));
+        auto currency_format_container = GTK_CONTAINER(gtk_builder_get_object (builder, "currency_format_container"));
         gtk_container_add (currency_format_container, GTK_WIDGET(info->currency_format_combo));
         gtk_widget_show_all (GTK_WIDGET(currency_format_container));
 

commit 7a381cd88bde9ba22a1603486166df5921307114
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Fri Dec 16 13:45:52 2016 +0100

    Make sure row coloring happens each time the preview is updated

diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index 25c1714..1692199 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -1779,6 +1779,9 @@ static void gnc_csv_preview_update_assist (CsvImportTrans* info)
     gtk_widget_show_all (GTK_WIDGET(info->treeview));
     gtk_widget_show_all (GTK_WIDGET(info->ctreeview));
 
+    /* Update skipped lines visual feedback */
+    row_selection_update (info);
+
     /* Set the encoding selector to the right encoding. */
     info->code_encoding_calls = 2;
     go_charmap_sel_set_encoding (info->encselector, info->parse_data->tokenizer->encoding().c_str());

commit 91c4202f2a0920cbefb38a7cd90c0eabbe1c7d92
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Fri Dec 16 13:43:52 2016 +0100

    Remove redundant check for valid column types
    
    The CsvTransSettings object guarantees all types in the vector are valid
    so there's no need any more to check this again in the assistant.

diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index a69d909..25c1714 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -336,16 +336,9 @@ csv_import_trans_load_settings (CsvImportTrans *info)
     GtkTreeModel *ctstore = gtk_tree_view_get_model (info->ctreeview);
     gtk_tree_model_get_iter_first (ctstore, &iter);
 
-    bool          error = false;
     for (uint i=0; i < preset->column_types.size() && i < info->parse_data->column_types.size(); i++)
     {
-        auto col_type = GncTransPropType::NONE;
-        auto saved_col_type = preset->column_types.at(i);
-
-        if (saved_col_type >= GncTransPropType::NONE &&
-            saved_col_type <= GncTransPropType::TMEMO)
-        {
-            col_type = saved_col_type;
+        auto col_type = preset->column_types.at(i);
             info->parse_data->column_types.at(i) = col_type;
             /* Set the column type. Store is arranged so that every two
              * columns is a pair of
@@ -358,13 +351,10 @@ csv_import_trans_load_settings (CsvImportTrans *info)
                         2 * i, _(gnc_csv_col_type_strs[col_type]),
                         2 * i + 1, col_type,
                         -1);
-        }
-        else
-            error = true;
     }
-    if (error)
-        gnc_error_dialog (NULL, "%s", _("There was a problem with the column types, please review."));
-}static void handle_save_del_sensitivity (GtkWidget* combo, CsvImportTrans *info)
+}
+
+static void handle_save_del_sensitivity (GtkWidget* combo, CsvImportTrans *info)
 {
     GtkTreeIter iter;
     auto can_delete = false;

commit 7fdf135a5417db8fd6174a147066e330b60017b7
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Tue Dec 13 19:59:45 2016 +0100

    Revisit csv trans import settings handling
    
    - move all gtk related stuff to the assistant code
    - move all checks to the settings object, the assistant should query that
    - handle sensitivity of the save and delete button more intelligently

diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index 034c35d..a69d909 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -83,6 +83,8 @@ typedef struct
 
     GtkWidget       *preview_page;                  /**< Assistant preview page widget */
     GtkWidget       *settings_combo;                /**< The Settings Combo */
+    GtkWidget       *save_button;                   /**< The Save Settings button */
+    GtkWidget       *del_button;                    /**< The Delete Settings button */
     GtkWidget       *combo_hbox;                    /**< The Settings Combo hbox */
     GtkWidget       *check_butt;                    /**< The widget for the check label button */
     GtkWidget       *start_row_spin;                /**< The widget for the start row spinner */
@@ -94,7 +96,6 @@ typedef struct
     int              home_account_number;           /**< The number of unique home account strings */
 
     GncTxImport     *parse_data;                    /**< The actual data we are previewing */
-    CsvTransSettings settings_data;                 /**< The settings to be saved and loaded */
     GOCharmapSel    *encselector;                   /**< The widget for selecting the encoding */
     GtkCheckButton  *sep_buttons[SEP_NUM_OF_TYPES]; /**< Checkbuttons for common separators */
     GtkCheckButton  *custom_cbutton;                /**< The checkbutton for a custom separator */
@@ -166,7 +167,8 @@ void csv_import_trans_file_chooser_confirm_cb (GtkWidget *button, CsvImportTrans
 
 void csv_import_trans_delete_settings_cb (GtkWidget *button, CsvImportTrans *info);
 void csv_import_trans_save_settings_cb (GtkWidget *button, CsvImportTrans *info);
-void csv_import_trans_changed_settings_cb (GtkWidget *button, CsvImportTrans *info);
+void csv_import_trans_changed_settings_cb (GtkWidget *combo, CsvImportTrans *info);
+void csv_import_trans_changed_settings_text_cb (GtkWidget *entry, CsvImportTrans *info);
 void sep_button_clicked (GtkWidget* widget, CsvImportTrans* info);
 }
 
@@ -189,6 +191,36 @@ bool get_list_of_accounts (CsvImportTrans* info, GtkTreeModel *store);
 /*************************************************************************/
 
 
+
+/*******************************************************
+ * populate_settings_combo
+ *
+ * Set the available presets in the settings combo box
+ *******************************************************/
+
+static void populate_settings_combo(GtkComboBox* combo)
+{
+    // Clear the list store
+    GtkTreeModel *model = gtk_combo_box_get_model (combo);
+    gtk_list_store_clear (GTK_LIST_STORE(model));
+
+    // Append the default entry
+
+    auto presets = get_trans_presets ();
+    for (auto preset : presets)
+    {
+        GtkTreeIter iter;
+        gtk_list_store_append (GTK_LIST_STORE(model), &iter);
+        /* FIXME we store the raw pointer to the preset, while it's
+         * managed by a shared pointer. This is dangerous because
+         * when the shared pointer goes out of scope, our pointer will dangle.
+         * For now this is safe, because the shared pointers in this case are
+         * long-lived, but this may need refactoring.
+         */
+        gtk_list_store_set (GTK_LIST_STORE(model), &iter, SET_GROUP, preset.get(), SET_NAME, preset->name.c_str(), -1);
+    }
+}
+
 /*******************************************************
  * csv_import_trans_load_settings
  *
@@ -203,18 +235,15 @@ csv_import_trans_load_settings (CsvImportTrans *info)
     if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX(info->settings_combo), &iter))
         return;
 
-    gchar *group = NULL;
+    CsvTransSettings *preset;
     GtkTreeModel *model = gtk_combo_box_get_model (GTK_COMBO_BOX(info->settings_combo));
-    gtk_tree_model_get (model, &iter, SET_GROUP, &group, -1);
+    gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1);
 
     // Test for default selection, return
-    if (!group)
+    if (!preset)
         return;
 
-    // Load the settings from the keyfile
-    auto group_str = std::string{group};
-    g_free (group);
-    if (info->settings_data.load (group_str))
+    if (preset->load_error)
     {
         const gchar *title = _("Load the Import Settings.");
         GtkWidget *dialog = gtk_message_dialog_new (GTK_WINDOW(info->window),
@@ -232,44 +261,44 @@ csv_import_trans_load_settings (CsvImportTrans *info)
     GtkAdjustment *adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(info->start_row_spin));
     gtk_adjustment_set_upper (adj, info->parse_data->parsed_lines.size());
     gtk_spin_button_set_value (GTK_SPIN_BUTTON(info->start_row_spin),
-            info->settings_data.header_rows);
+            preset->header_rows);
 
     // Set end row
     adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(info->end_row_spin));
     gtk_adjustment_set_upper (adj, info->parse_data->parsed_lines.size());
     gtk_spin_button_set_value (GTK_SPIN_BUTTON(info->end_row_spin),
-            info->settings_data.footer_rows);
+            preset->footer_rows);
 
     // Set Alternate rows
-    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->skip_rows), info->settings_data.skip_alt_rows);
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->skip_rows), preset->skip_alt_rows);
 
     // Set Multi-split indicator
-    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->multi_split_cbutton), info->settings_data.multi_split);
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->multi_split_cbutton), preset->multi_split);
 
     // Set Import Format
-    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->csv_button), info->settings_data.csv_format);
-    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->fixed_button), !info->settings_data.csv_format);
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->csv_button), preset->csv_format);
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->fixed_button), !preset->csv_format);
 
     // This section deals with the combo's and character encoding
-    info->parse_data->date_format = info->settings_data.date_active;
-    gtk_combo_box_set_active (GTK_COMBO_BOX(info->date_format_combo), info->settings_data.date_active);
+    info->parse_data->date_format = preset->date_active;
+    gtk_combo_box_set_active (GTK_COMBO_BOX(info->date_format_combo), preset->date_active);
 
-    info->parse_data->currency_format = info->settings_data.currency_active;
-    gtk_combo_box_set_active (GTK_COMBO_BOX(info->currency_format_combo), info->settings_data.currency_active);
-    info->parse_data->convert_encoding (info->settings_data.encoding);
-    go_charmap_sel_set_encoding (info->encselector, info->settings_data.encoding.c_str());
+    info->parse_data->currency_format = preset->currency_active;
+    gtk_combo_box_set_active (GTK_COMBO_BOX(info->currency_format_combo), preset->currency_active);
+    info->parse_data->convert_encoding (preset->encoding);
+    go_charmap_sel_set_encoding (info->encselector, preset->encoding.c_str());
 
     try
     {
-        if (info->settings_data.csv_format)
+        if (preset->csv_format)
         {
             // Handle separators, only relevant if the file format is csv
             info->parse_data->file_format (GncImpFileFormat::CSV);
             for (int i = 0; i < SEP_NUM_OF_TYPES; i++)
-                gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->sep_buttons[i]), info->settings_data.separator[i]);
-            gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->custom_cbutton), info->settings_data.custom);
-            if (info->settings_data.custom)
-                gtk_entry_set_text (GTK_ENTRY(info->custom_entry), info->settings_data.custom_entry.c_str());
+                gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->sep_buttons[i]), preset->separator[i]);
+            gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->custom_cbutton), preset->custom);
+            if (preset->custom)
+                gtk_entry_set_text (GTK_ENTRY(info->custom_entry), preset->custom_entry.c_str());
             else
                 gtk_entry_set_text (GTK_ENTRY(info->custom_entry), "");
 
@@ -280,7 +309,7 @@ csv_import_trans_load_settings (CsvImportTrans *info)
             // Handle column widths, only relevant if the file format is fixed width
             info->parse_data->file_format (GncImpFileFormat::FIXED_WIDTH);
             GncFwTokenizer *fwtok = dynamic_cast<GncFwTokenizer*>(info->parse_data->tokenizer.get());
-            fwtok->columns(info->settings_data.column_widths);
+            fwtok->columns(preset->column_widths);
 
             info->parse_data->tokenize (false);
             gnc_csv_preview_update_assist (info);
@@ -308,10 +337,10 @@ csv_import_trans_load_settings (CsvImportTrans *info)
     gtk_tree_model_get_iter_first (ctstore, &iter);
 
     bool          error = false;
-    for (uint i=0; i < info->settings_data.column_types.size() && i < info->parse_data->column_types.size(); i++)
+    for (uint i=0; i < preset->column_types.size() && i < info->parse_data->column_types.size(); i++)
     {
         auto col_type = GncTransPropType::NONE;
-        auto saved_col_type = info->settings_data.column_types.at(i);
+        auto saved_col_type = preset->column_types.at(i);
 
         if (saved_col_type >= GncTransPropType::NONE &&
             saved_col_type <= GncTransPropType::TMEMO)
@@ -335,82 +364,104 @@ csv_import_trans_load_settings (CsvImportTrans *info)
     }
     if (error)
         gnc_error_dialog (NULL, "%s", _("There was a problem with the column types, please review."));
+}static void handle_save_del_sensitivity (GtkWidget* combo, CsvImportTrans *info)
+{
+    GtkTreeIter iter;
+    auto can_delete = false;
+    auto can_save = false;
+    auto entry = gtk_bin_get_child (GTK_BIN(combo));
+    auto entry_text = gtk_entry_get_text (GTK_ENTRY(entry));
+    /* Handle sensitivity of the delete button and save button */
+    auto got_iter = gtk_combo_box_get_active_iter (GTK_COMBO_BOX(combo), &iter);
+    if (got_iter)
+    {
+        CsvTransSettings *preset;
+        GtkTreeModel *model = gtk_combo_box_get_model (GTK_COMBO_BOX(combo));
+        gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1);
+
+        if (preset && !trans_preset_is_reserved_name (preset->name))
+        {
+            /* Current preset is not read_only, so buttons can be enabled */
+            can_delete = true;
+            can_save = true;
+        }
+    }
+    else if (entry_text && (strlen (entry_text) > 0) &&
+            !trans_preset_is_reserved_name (std::string(entry_text)))
+        can_save = true;
+
+    gtk_widget_set_sensitive (info->save_button, can_save);
+    gtk_widget_set_sensitive (info->del_button, can_delete);
+
 }
 
 
 /*******************************************************
- * csv_import_trans_delete_settings_cb
+ * csv_import_trans_changed_settings_cb
  *
- * call back to delete a settings entry
+ * Triggered when a preset is selected in the settings combo.
  *******************************************************/
 void
-csv_import_trans_delete_settings_cb (GtkWidget *button, CsvImportTrans *info)
+csv_import_trans_changed_settings_cb (GtkWidget *combo, CsvImportTrans *info)
 {
-    GKeyFile     *keyfile;
-    GtkTreeIter   iter;
-    GtkWidget    *dialog;
-    gint          response;
-    gchar        *group = NULL;
-    const gchar  *title = _("Delete the Import Settings.");
+    csv_import_trans_load_settings (info);
+    handle_save_del_sensitivity (combo, info);
+}
 
-    // Get the Key file
-    keyfile = gnc_state_get_current ();
 
-    // Get the Active Selection
-    if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX(info->settings_combo), &iter))
-    {
-        GtkTreeModel *model;
-        model = gtk_combo_box_get_model (GTK_COMBO_BOX(info->settings_combo));
-        gtk_tree_model_get (model, &iter, SET_GROUP, &group, -1);
+/*******************************************************
+ * csv_import_trans_changed_settings_text_cb
+ *
+ * Triggered while the user is editing text the settings combo.
+ *******************************************************/
+void
+csv_import_trans_changed_settings_text_cb (GtkWidget *entry, CsvImportTrans *info)
+{
+    auto combo = gtk_widget_get_parent (entry);
+    handle_save_del_sensitivity (combo, info);
+}
+
 
-        if (g_strcmp0 (group, NULL) == 0)
-        {
-            dialog = gtk_message_dialog_new (GTK_WINDOW(info->window),
-                                        (GtkDialogFlags) 0,
-                                        GTK_MESSAGE_ERROR,
-                                        GTK_BUTTONS_OK,
-                                        "%s", title);
-            gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
-                "%s", _("You can not Delete the 'No Settings' entry."));
-            gtk_dialog_run (GTK_DIALOG (dialog));
-            gtk_widget_destroy (dialog);
-        }
-        else
-        {
-            dialog = gtk_message_dialog_new (GTK_WINDOW(info->window),
-                                        (GtkDialogFlags) 0,
-                                        GTK_MESSAGE_QUESTION,
-                                        GTK_BUTTONS_OK_CANCEL,
-                                        "%s", title);
-            gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
-                "%s", _("Do you really want to delete the selection."));
-            response = gtk_dialog_run (GTK_DIALOG (dialog));
-            gtk_widget_destroy (dialog);
 
-            if (response == GTK_RESPONSE_OK)
-            {
-                g_key_file_remove_group (keyfile, group, NULL);
-                info->settings_data.find (model);
-                gtk_combo_box_set_active (GTK_COMBO_BOX(info->settings_combo), 0); // Default
-                gnc_csv_reset_preview_setting (info, false); // Reset the widgets
-            }
-        }
-        g_free (group);
-    }
-}
 
 
 /*******************************************************
- * csv_import_trans_changed_settings_cb
+ * csv_import_trans_delete_settings_cb
  *
- * Apply settings when selection changed.
+ * call back to delete a settings entry
  *******************************************************/
 void
-csv_import_trans_changed_settings_cb (GtkWidget *button, CsvImportTrans *info)
+csv_import_trans_delete_settings_cb (GtkWidget *button, CsvImportTrans *info)
 {
-    csv_import_trans_load_settings (info);
-}
+    const gchar  *title = _("Delete the Import Settings.");
 
+    // Get the Active Selection
+    GtkTreeIter iter;
+    if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX(info->settings_combo), &iter))
+        return;
+
+    CsvTransSettings *preset = nullptr;
+    auto model = gtk_combo_box_get_model (GTK_COMBO_BOX(info->settings_combo));
+    gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1);
+
+    auto dialog = gtk_message_dialog_new (GTK_WINDOW(info->window),
+                                (GtkDialogFlags) 0,
+                                GTK_MESSAGE_QUESTION,
+                                GTK_BUTTONS_OK_CANCEL,
+                                "%s", title);
+    gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+        "%s", _("Do you really want to delete the selection?"));
+    auto response = gtk_dialog_run (GTK_DIALOG (dialog));
+    gtk_widget_destroy (dialog);
+
+    if (response == GTK_RESPONSE_OK)
+    {
+        preset->remove();
+        populate_settings_combo(GTK_COMBO_BOX(info->settings_combo));
+        gtk_combo_box_set_active (GTK_COMBO_BOX(info->settings_combo), 0); // Default
+        gnc_csv_reset_preview_setting (info, false); // Reset the widgets
+    }
+}
 
 /*******************************************************
  * csv_import_trans_save_settings_cb
@@ -420,215 +471,162 @@ csv_import_trans_changed_settings_cb (GtkWidget *button, CsvImportTrans *info)
 void
 csv_import_trans_save_settings_cb (GtkWidget *button, CsvImportTrans *info)
 {
-    GtkTreeIter    iter;
-    GtkWidget     *dialog;
-    GtkWidget     *entry;
-    gchar         *group = NULL, *name = NULL;
-    const gchar   *title = _("Save the Import Settings.");
-    bool           error = false;
-    const gchar   *entry_text;
+    const gchar *title = _("Save the Import Settings.");
+    GtkTreeIter iter;
 
     // Get the Entry Text
-    entry = gtk_bin_get_child (GTK_BIN(info->settings_combo));
-    entry_text = gtk_entry_get_text (GTK_ENTRY(entry));
+    auto entry = gtk_bin_get_child (GTK_BIN(info->settings_combo));
+    auto entry_text = gtk_entry_get_text (GTK_ENTRY(entry));
 
-    // If entry used, this lot is by passed.
-    if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX(info->settings_combo), &iter))
+    /* Check if the entry text matches an already existing preset */
+    int response = GTK_RESPONSE_OK;
+    if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX(info->settings_combo), &iter))
     {
-        GtkTreeModel *model;
-        model = gtk_combo_box_get_model (GTK_COMBO_BOX(info->settings_combo));
-        gtk_tree_model_get (model, &iter, SET_GROUP, &group, SET_NAME, &name, -1);
 
-        if (g_strcmp0 (group, NULL) == 0)
+        GtkTreeModel *model = gtk_combo_box_get_model (GTK_COMBO_BOX(info->settings_combo));
+        bool valid = gtk_tree_model_get_iter_first (model, &iter);
+        bool found = false;
+        while (valid)
         {
-            dialog = gtk_message_dialog_new (GTK_WINDOW(info->window),
-                                        (GtkDialogFlags) 0,
-                                        GTK_MESSAGE_ERROR,
-                                        GTK_BUTTONS_OK,
-                                        "%s", title);
-            gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
-                "%s", _("You can not save to 'No Settings'."));
-            gtk_dialog_run (GTK_DIALOG (dialog));
-            gtk_widget_destroy (dialog);
-            error = true;
+            // Walk through the list, reading each row
+            CsvTransSettings *preset;
+            gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1);
+
+            if (preset && (preset->name == std::string(entry_text)))
+            {
+                found = true;
+                break;
+            }
+            valid = gtk_tree_model_iter_next (model, &iter);
         }
-        g_free (group);
-        g_free (name);
-    }
-    else // Check for blank entry_text
-    {
-        if (strlen (entry_text) == 0 || g_strcmp0 (entry_text, NULL) == 0)
+
+        if (found) // We have found entry_text in liststore
         {
-            dialog = gtk_message_dialog_new (GTK_WINDOW(info->window),
-                                        (GtkDialogFlags) 0,
-                                        GTK_MESSAGE_ERROR,
-                                        GTK_BUTTONS_OK,
-                                        "%s", title);
+            auto dialog = gtk_message_dialog_new (GTK_WINDOW(info->window),
+                                    (GtkDialogFlags) 0,
+                                    GTK_MESSAGE_QUESTION,
+                                    GTK_BUTTONS_OK_CANCEL,
+                                    "%s", title);
             gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
-                "%s", _("The settings name is blank."));
-            gtk_dialog_run (GTK_DIALOG (dialog));
+            "%s", _("Setting name already exists, over write?"));
+            response = gtk_dialog_run (GTK_DIALOG (dialog));
             gtk_widget_destroy (dialog);
-            error = true;
         }
-        else // Check for entry_text in settings list
-        {
-            GtkTreeModel *model;
-            GtkTreeIter   iter;
-            bool          valid = false;
-            bool          found = false;
-            int           response;
+    }
 
-            model = gtk_combo_box_get_model (GTK_COMBO_BOX(info->settings_combo));
+    if (response != GTK_RESPONSE_OK)
+        return;
 
-            valid = gtk_tree_model_get_iter_first (model, &iter);
+    /* All checks passed, let's save this preset */
+    CsvTransSettings preset;
+    preset.name = entry_text;
 
-            while (valid)
-            {
-                gchar *name = NULL;
+    /* This section deals with the header and rows */
+    preset.header_rows = gtk_spin_button_get_value (GTK_SPIN_BUTTON(info->start_row_spin));
+    preset.footer_rows = gtk_spin_button_get_value (GTK_SPIN_BUTTON(info->end_row_spin));
+    preset.skip_alt_rows = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(info->skip_rows));
+    preset.csv_format = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(info->csv_button));
 
-                // Walk through the list, reading each row
-                gtk_tree_model_get (model, &iter, SET_NAME, &name, -1);
+    /* This Section deals with the separators */
+    for (int i = 0; i < SEP_NUM_OF_TYPES; i++)
+    {
+        preset.separator[i] = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(info->sep_buttons[i]));
+    }
+    preset.custom = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(info->custom_cbutton));
+    auto tmp_text = gtk_entry_get_text (GTK_ENTRY(info->custom_entry));
+    if (tmp_text && *tmp_text != '\0')
+        preset.custom_entry = tmp_text;
 
-                if (g_strcmp0 (name, entry_text) == 0)
-                    found = true;
+    /* This section deals with the combo's and character encoding */
+    preset.date_active = gtk_combo_box_get_active (GTK_COMBO_BOX(info->date_format_combo));
+    preset.currency_active = gtk_combo_box_get_active (GTK_COMBO_BOX(info->currency_format_combo));
+    preset.encoding = go_charmap_sel_get_encoding (info->encselector);
+    preset.multi_split = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(info->multi_split_cbutton));
 
-                g_free (name);
+    /* This section deals with the Treeview column names */
+    GList *columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(info->ctreeview));
+    // ctstore contains the actual strings appearing in the column types treeview.
+    GtkTreeModel *ctstore = gtk_tree_view_get_model (info->ctreeview);
+    // Get an iterator for the first (and only) row.
+    gtk_tree_model_get_iter_first (ctstore, &iter);
 
-                valid = gtk_tree_model_iter_next (model, &iter);
-            }
+    preset.column_types.clear();
+    GList *column;
+    int i;
+    for (column = columns, i = 0; column; column = g_list_next (column), i++)
+    {
+        auto col_type = GncTransPropType::NONE;
+        gchar *col_type_str = NULL;
+
+        /* Get the column type. Store is arranged so that every two
+         * columns is a pair of
+         * - the column type as a user visible (translated) string
+         * - the internal type for this column
+         * So ctstore looks like:
+         * col_type_str 0, col_type, col_type_str 1, col_type 1, ..., col_type_str ncols, col_type ncols. */
+        gtk_tree_model_get (ctstore, &iter, 2 * i + 1, &col_type, -1);
+        preset.column_types.push_back(col_type);
 
-            if (found) // We have found entry_text in liststore
-            {
-                dialog = gtk_message_dialog_new (GTK_WINDOW(info->window),
-                                        (GtkDialogFlags) 0,
-                                        GTK_MESSAGE_QUESTION,
-                                        GTK_BUTTONS_OK_CANCEL,
-                                        "%s", title);
-                gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
-                "%s", _("Setting name already exists, over write."));
-                response = gtk_dialog_run (GTK_DIALOG (dialog));
-                gtk_widget_destroy (dialog);
-
-                if (response != GTK_RESPONSE_OK)
-                    error = true;
-            }
-        }
     }
+    g_list_free (columns);
 
-    // Call save settings if we have no errors
-    if (!error)
+    /* Save the column widths in fixed mode */
+    if (!preset.csv_format)
     {
-        int           i;
-        GList        *columns;
-        GList        *column;
-        GtkTreeModel *ctstore;
-        GtkTreeIter   iter;
-
-        /* This section deals with the header and rows */
-        info->settings_data.header_rows = gtk_spin_button_get_value (GTK_SPIN_BUTTON(info->start_row_spin));
-        info->settings_data.footer_rows = gtk_spin_button_get_value (GTK_SPIN_BUTTON(info->end_row_spin));
-        info->settings_data.skip_alt_rows = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(info->skip_rows));
-        info->settings_data.csv_format = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(info->csv_button));
-
-        /* This Section deals with the separators */
-        for (i = 0; i < SEP_NUM_OF_TYPES; i++)
-        {
-            info->settings_data.separator[i] = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(info->sep_buttons[i]));
-        }
-        info->settings_data.custom = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(info->custom_cbutton));
-        auto tmp_text = gtk_entry_get_text (GTK_ENTRY(info->custom_entry));
-        if (tmp_text && *tmp_text != '\0')
-            info->settings_data.custom_entry = tmp_text;
-
-        /* This section deals with the combo's and character encoding */
-        info->settings_data.date_active = gtk_combo_box_get_active (GTK_COMBO_BOX(info->date_format_combo));
-        info->settings_data.currency_active = gtk_combo_box_get_active (GTK_COMBO_BOX(info->currency_format_combo));
-        info->settings_data.encoding = go_charmap_sel_get_encoding (info->encselector);
-        info->settings_data.multi_split = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(info->multi_split_cbutton));
-
-        /* This section deals with the Treeview column names */
-        columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(info->ctreeview));
-        // ctstore contains the actual strings appearing in the column types treeview.
-        ctstore = gtk_tree_view_get_model (info->ctreeview);
-        // Get an iterator for the first (and only) row.
-        gtk_tree_model_get_iter_first (ctstore, &iter);
-
-        info->settings_data.column_types.clear();
-        for (column = columns, i = 0; column; column = g_list_next (column), i++)
-        {
-            auto col_type = GncTransPropType::NONE;
-            gchar *col_type_str = NULL;
+        GncFwTokenizer *fwtok = dynamic_cast<GncFwTokenizer*>(info->parse_data->tokenizer.get());
+        preset.column_widths = fwtok->get_columns();
+    }
 
-            /* Get the column type. Store is arranged so that every two
-             * columns is a pair of
-             * - the column type as a user visible (translated) string
-             * - the internal type for this column
-             * So ctstore looks like:
-             * col_type_str 0, col_type, col_type_str 1, col_type 1, ..., col_type_str ncols, col_type ncols. */
-            gtk_tree_model_get (ctstore, &iter, 2 * i + 1, &col_type, -1);
-            info->settings_data.column_types.push_back(col_type);
+    // Save the settings
+    if (!preset.save ())
+    {
+        GtkTreeModel *model;
+        GtkTreeIter   iter;
+        bool          valid = false;
 
-        }
-        g_list_free (columns);
+        auto dialog = gtk_message_dialog_new (GTK_WINDOW(info->window),
+                                    (GtkDialogFlags) 0,
+                                    GTK_MESSAGE_INFO,
+                                    GTK_BUTTONS_OK,
+                                    "%s", title);
+        gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+            "%s", _("The settings have been saved."));
+        gtk_dialog_run (GTK_DIALOG (dialog));
+        gtk_widget_destroy (dialog);
 
-        /* Save the column widths in fixed mode */
-        if (!info->settings_data.csv_format)
-        {
-            GncFwTokenizer *fwtok = dynamic_cast<GncFwTokenizer*>(info->parse_data->tokenizer.get());
-            info->settings_data.column_widths = fwtok->get_columns();
-        }
+        // Update the settings store
+        populate_settings_combo(GTK_COMBO_BOX(info->settings_combo));
+        model = gtk_combo_box_get_model (GTK_COMBO_BOX(info->settings_combo));
 
-        // Save the settings
-        if (!info->settings_data.save (g_strdup (entry_text)))
+        // Get the first entry in model
+        valid = gtk_tree_model_get_iter_first (model, &iter);
+        while (valid)
         {
-            GtkTreeModel *model;
-            GtkTreeIter   iter;
-            bool          valid = false;
-
-            dialog = gtk_message_dialog_new (GTK_WINDOW(info->window),
-                                        (GtkDialogFlags) 0,
-                                        GTK_MESSAGE_INFO,
-                                        GTK_BUTTONS_OK,
-                                        "%s", title);
-            gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
-                "%s", _("The settings have been saved."));
-            gtk_dialog_run (GTK_DIALOG (dialog));
-            gtk_widget_destroy (dialog);
+            gchar *name = NULL;
 
-            // Update the settings store
-            model = gtk_combo_box_get_model (GTK_COMBO_BOX(info->settings_combo));
-            info->settings_data.find (model);
+            // Walk through the list, reading each row
+            gtk_tree_model_get (model, &iter, SET_NAME, &name, -1);
 
-            // Get the first entry in model
-            valid = gtk_tree_model_get_iter_first (model, &iter);
-            while (valid)
-            {
-                gchar *name = NULL;
-
-                // Walk through the list, reading each row
-                gtk_tree_model_get (model, &iter, SET_NAME, &name, -1);
-
-                if (g_strcmp0 (name, entry_text) == 0) // Set Active, the one Saved.
-                    gtk_combo_box_set_active_iter (GTK_COMBO_BOX(info->settings_combo), &iter);
+            if (g_strcmp0 (name, entry_text) == 0) // Set Active, the one Saved.
+                gtk_combo_box_set_active_iter (GTK_COMBO_BOX(info->settings_combo), &iter);
 
-                g_free (name);
+            g_free (name);
 
-                valid = gtk_tree_model_iter_next (model, &iter);
-            }
-        }
-        else
-        {
-            dialog = gtk_message_dialog_new (GTK_WINDOW(info->window),
-                                        (GtkDialogFlags) 0,
-                                        GTK_MESSAGE_ERROR,
-                                        GTK_BUTTONS_OK,
-                                        "%s", title);
-            gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
-                "%s", _("There was a problem saving the settings, please try again."));
-            gtk_dialog_run (GTK_DIALOG (dialog));
-            gtk_widget_destroy (dialog);
+            valid = gtk_tree_model_iter_next (model, &iter);
         }
     }
+    else
+    {
+        auto dialog = gtk_message_dialog_new (GTK_WINDOW(info->window),
+                                    (GtkDialogFlags) 0,
+                                    GTK_MESSAGE_ERROR,
+                                    GTK_BUTTONS_OK,
+                                    "%s", title);
+        gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+            "%s", _("There was a problem saving the settings, please try again."));
+        gtk_dialog_run (GTK_DIALOG (dialog));
+        gtk_widget_destroy (dialog);
+    }
 }
 
 
@@ -702,8 +700,7 @@ csv_import_trans_file_chooser_confirm_cb (GtkWidget *button, CsvImportTrans *inf
     gtk_spin_button_set_value (GTK_SPIN_BUTTON(info->end_row_spin), 0);
 
     /* Get settings store and populate */
-    GtkTreeModel   *settings_store = gtk_combo_box_get_model (GTK_COMBO_BOX(info->settings_combo));
-    info->settings_data.find (settings_store);
+    populate_settings_combo(GTK_COMBO_BOX(info->settings_combo));
     gtk_combo_box_set_active (GTK_COMBO_BOX(info->settings_combo), 0);
 
     gtk_assistant_set_page_complete (assistant, page, TRUE);
@@ -2675,7 +2672,6 @@ csv_import_trans_assistant_create (CsvImportTrans *info)
     GtkWidget *window;
     GtkWidget *box;
     GtkWidget *button, *h_box;
-    GtkWidget *save_button, *del_button;
 
     builder = gtk_builder_new();
     gnc_builder_add_from_file  (builder , "assistant-csv-trans-import.glade", "start_row_adj");
@@ -2754,7 +2750,7 @@ csv_import_trans_assistant_create (CsvImportTrans *info)
         info->preview_page = GTK_WIDGET(gtk_builder_get_object (builder, "preview_page"));
 
         // Add Settings combo
-        settings_store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
+        settings_store = gtk_list_store_new (2, G_TYPE_POINTER, G_TYPE_STRING);
         info->settings_combo = gtk_combo_box_new_with_model_and_entry (GTK_TREE_MODEL(settings_store));
         gtk_combo_box_set_entry_text_column (GTK_COMBO_BOX(info->settings_combo), SET_NAME);
         gtk_combo_box_set_active (GTK_COMBO_BOX(info->settings_combo), 0);
@@ -2766,20 +2762,25 @@ csv_import_trans_assistant_create (CsvImportTrans *info)
         g_signal_connect (G_OBJECT(info->settings_combo), "changed",
                          G_CALLBACK(csv_import_trans_changed_settings_cb), (gpointer)info);
 
+        // Additionally connect to the changed signal of the embedded GtkEntry
+        auto emb_entry = gtk_bin_get_child (GTK_BIN (info->settings_combo));
+        g_signal_connect (G_OBJECT(emb_entry), "changed",
+                         G_CALLBACK(csv_import_trans_changed_settings_text_cb), (gpointer)info);
+
         // Add Save Settings button
-        save_button = gtk_button_new_with_label (_("Save Settings"));
-        gtk_box_pack_start (GTK_BOX(info->combo_hbox), save_button, FALSE, FALSE, 6);
-        gtk_widget_show (save_button);
+        info->save_button = gtk_button_new_with_label (_("Save Settings"));
+        gtk_box_pack_start (GTK_BOX(info->combo_hbox), info->save_button, FALSE, FALSE, 6);
+        gtk_widget_show (info->save_button);
 
-        g_signal_connect (G_OBJECT(save_button), "clicked",
+        g_signal_connect (G_OBJECT(info->save_button), "clicked",
                          G_CALLBACK(csv_import_trans_save_settings_cb), (gpointer)info);
 
         // Add Delete Settings button
-        del_button = gtk_button_new_with_label (_("Delete Settings"));
-        gtk_box_pack_start (GTK_BOX(info->combo_hbox), del_button, FALSE, FALSE, 6);
-        gtk_widget_show (del_button);
+        info->del_button = gtk_button_new_with_label (_("Delete Settings"));
+        gtk_box_pack_start (GTK_BOX(info->combo_hbox), info->del_button, FALSE, FALSE, 6);
+        gtk_widget_show (info->del_button);
 
-        g_signal_connect (G_OBJECT(del_button), "clicked",
+        g_signal_connect (G_OBJECT(info->del_button), "clicked",
                          G_CALLBACK(csv_import_trans_delete_settings_cb), (gpointer)info);
 
         /* The table containing the separator configuration widgets */
diff --git a/src/import-export/csv-imp/gnc-csv-trans-settings.cpp b/src/import-export/csv-imp/gnc-csv-trans-settings.cpp
index 500a96e..bf94252 100644
--- a/src/import-export/csv-imp/gnc-csv-trans-settings.cpp
+++ b/src/import-export/csv-imp/gnc-csv-trans-settings.cpp
@@ -38,6 +38,7 @@ extern "C"
 
 const std::string csv_group_prefix{"CSV - "};
 const std::string no_settings{N_("No Settings")};
+const std::string gnc_exp{N_("GnuCash Export Format")};
 #define CSV_NAME         "Name"
 #define CSV_FORMAT       "CsvFormat"
 #define CSV_ALT_ROWS     "AltRows"
@@ -59,50 +60,108 @@ const std::string no_settings{N_("No Settings")};
 
 G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT;
 
+preset_vec presets;
+
+static std::shared_ptr<CsvTransSettings> create_int_no_preset(void)
+{
+    auto preset = std::make_shared<CsvTransSettings>();
+    preset->name = no_settings;
+
+    return preset;
+}
+
+static std::shared_ptr<CsvTransSettings> create_int_gnc_exp_preset(void)
+{
+    auto preset = std::make_shared<CsvTransSettings>();
+    preset->name = gnc_exp;
+    preset->header_rows = 1;
+    preset->multi_split = true;
+
+    preset->separator[SEP_COMMA] = true;
+
+    /* FIXME date and currency format should still be aligned with export format!
+     * That's currently hard to do, because the export uses whatever the user
+     * had set as preference.
+    preset->date_active = 0;
+    preset->currency_active = 0;
+    */
+    preset->column_types = {
+            GncTransPropType::DATE,
+            GncTransPropType::UNIQUE_ID,
+            GncTransPropType::NUM,
+            GncTransPropType::DESCRIPTION,
+            GncTransPropType::NOTES,
+            GncTransPropType::COMMODITY,
+            GncTransPropType::VOID_REASON,
+            GncTransPropType::ACTION,
+            GncTransPropType::MEMO,
+            GncTransPropType::ACCOUNT,
+            GncTransPropType::NONE,
+            GncTransPropType::NONE,
+            GncTransPropType::DEPOSIT,
+            GncTransPropType::REC_STATE,
+            GncTransPropType::REC_DATE,
+            GncTransPropType::PRICE
+    };
+
+    return preset;
+}
+
 /**************************************************
  * find
  *
  * find all settings entries in the state key file
  **************************************************/
-void
-CsvTransSettings::find (GtkTreeModel *settings_store)
+const preset_vec& get_trans_presets (void)
 {
 
-    // Clear the list store
-    gtk_list_store_clear (GTK_LIST_STORE(settings_store));
-
-    // Append the default entry
-    GtkTreeIter iter;
-    gtk_list_store_append (GTK_LIST_STORE(settings_store), &iter);
-    gtk_list_store_set (GTK_LIST_STORE(settings_store), &iter, SET_GROUP, NULL, SET_NAME, _(no_settings.c_str()), -1);
-
     // Search all Groups in the state key file for ones starting with prefix
-    GKeyFile   *keyfile = gnc_state_get_current ();
+    auto preset_names = std::vector<std::string>();
+    auto keyfile = gnc_state_get_current ();
     gsize grouplength;
     gchar **groups = g_key_file_get_groups (keyfile, &grouplength);
 
+    /* Start by building a sorted list of candidate presets as found in the state file */
     for (gsize i=0; i < grouplength; i++)
     {
-        if (g_str_has_prefix (groups[i], csv_group_prefix.c_str()))
-        {
-            GError *key_error = nullptr;
-            gchar *name = g_key_file_get_string (keyfile, groups[i], CSV_NAME, &key_error);
-
-            if (!key_error)
-            {
-                gtk_list_store_append (GTK_LIST_STORE(settings_store), &iter);
-                gtk_list_store_set (GTK_LIST_STORE(settings_store), &iter, SET_GROUP, groups[i], SET_NAME, name, -1);
-            }
-            else
-            {
-                g_warning ("Error reading group '%s' name '%s': %s", groups[i], CSV_NAME, key_error->message);
-                g_clear_error (&key_error);
-            }
-            g_free (name);
-        }
+        auto group = std::string(groups[i]);
+        auto pos = group.find(csv_group_prefix);
+        if (pos == std::string::npos)
+            continue;
+
+        preset_names.push_back(group.substr(csv_group_prefix.size()));
     }
-    // free the strings
+    // string array from the state file is no longer needed now.
     g_strfreev (groups);
+
+    /* We want our settings to appear sorted alphabetically to the user */
+    std::sort(preset_names.begin(), preset_names.end());
+
+    /* Now add each preset to our global list */
+    presets.clear();
+
+    /* Start with the internally generated ones */
+    presets.push_back(create_int_no_preset());
+    presets.push_back(create_int_gnc_exp_preset());
+
+    /* Then add all the ones we found in the state file */
+    for (auto preset_name : preset_names)
+    {
+        auto preset = std::make_shared<CsvTransSettings>();
+        preset->name = preset_name;
+        preset->load();
+        presets.push_back(preset);
+    }
+
+    return presets;
+}
+
+bool trans_preset_is_reserved_name (const std::string& name)
+{
+    return ((name == no_settings) ||
+            (name == _(no_settings.c_str())) ||
+            (name == gnc_exp) ||
+            (name == _(gnc_exp.c_str())));
 }
 
 
@@ -136,59 +195,63 @@ handle_load_error (GError **key_error, const std::string& group)
  * load the settings from a state key file
  **************************************************/
 bool
-CsvTransSettings::load (const std::string& group)
+CsvTransSettings::load (void)
 {
+    if (trans_preset_is_reserved_name (name))
+        return true;
+
     GError *key_error = nullptr;
-    bool error = false;
+    load_error = false;
+    auto group = csv_group_prefix + name;
     auto keyfile = gnc_state_get_current ();
 
     header_rows = g_key_file_get_integer (keyfile, group.c_str(), CSV_SKIP_START, &key_error);
-    error |= handle_load_error (&key_error, group);
+    load_error |= handle_load_error (&key_error, group);
 
     footer_rows = g_key_file_get_integer (keyfile, group.c_str(), CSV_SKIP_END, &key_error);
-    error |= handle_load_error (&key_error, group);
+    load_error |= handle_load_error (&key_error, group);
 
     skip_alt_rows = g_key_file_get_boolean (keyfile, group.c_str(), CSV_ALT_ROWS, &key_error);
-    error |= handle_load_error (&key_error, group);
+    load_error |= handle_load_error (&key_error, group);
 
     multi_split = g_key_file_get_boolean (keyfile, group.c_str(), CSV_MULTI_SPLIT, &key_error);
-    error |= handle_load_error (&key_error, group);
+    load_error |= handle_load_error (&key_error, group);
 
     csv_format = g_key_file_get_boolean (keyfile, group.c_str(), CSV_FORMAT, &key_error);
     if (key_error) csv_format = true; // default to true, but above command will return false in case of error
-    error |= handle_load_error (&key_error, group);
+    load_error |= handle_load_error (&key_error, group);
 
     for (uint i = 0; i < SEP_NUM_OF_TYPES; i++)
     {
         gchar *sep;
         sep = g_strdup_printf ("%s%d", CSV_SEP, i);
         separator[i] = g_key_file_get_boolean (keyfile, group.c_str(), sep, &key_error);
-        error |= handle_load_error (&key_error, group);
+        load_error |= handle_load_error (&key_error, group);
         g_free (sep);
     }
 
     custom = g_key_file_get_boolean (keyfile, group.c_str(), CSV_CUSTOM, &key_error);
-    error |= handle_load_error (&key_error, group);
+    load_error |= handle_load_error (&key_error, group);
 
     gchar *key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_CUSTOM_ENTRY, &key_error);
     if (key_char && *key_char != '\0')
         custom_entry = key_char;
-    error |= handle_load_error (&key_error, group);
+    load_error |= handle_load_error (&key_error, group);
     if (key_char)
         g_free (key_char);
 
     date_active = g_key_file_get_integer (keyfile, group.c_str(), CSV_DATE, &key_error);
-    error |= handle_load_error (&key_error, group);
+    load_error |= handle_load_error (&key_error, group);
 
     currency_active = g_key_file_get_integer (keyfile, group.c_str(), CSV_CURRENCY, &key_error);
-    error |= handle_load_error (&key_error, group);
+    load_error |= handle_load_error (&key_error, group);
 
     key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_ENCODING, &key_error);
     if (key_char && *key_char != '\0')
         encoding = key_char;
     else
         "UTF-8";
-    error |= handle_load_error (&key_error, group);
+    load_error |= handle_load_error (&key_error, group);
     if (key_char)
         g_free (key_char);
 
@@ -223,11 +286,11 @@ CsvTransSettings::load (const std::string& group)
         if (col_widths_int[i] > 0)
             column_widths.push_back(col_widths_int[i]);
     }
-    error |= handle_load_error (&key_error, group);
+    load_error |= handle_load_error (&key_error, group);
     if (col_widths_int)
         g_free (col_widths_int);
 
-    return error;
+    return load_error;
 }
 
 
@@ -237,16 +300,22 @@ CsvTransSettings::load (const std::string& group)
  * save settings to a key file
  **************************************************/
 bool
-CsvTransSettings::save (const std::string& settings_name)
+CsvTransSettings::save (void)
 {
+    if (trans_preset_is_reserved_name (name))
+    {
+        PWARN ("Ignoring attempt to save to reserved name '%s'", name.c_str());
+        return true;
+    }
+
     auto keyfile = gnc_state_get_current ();
-    std::string group = csv_group_prefix + settings_name;
+    auto group = csv_group_prefix + name;
 
     // Drop previous saved settings with this name
     g_key_file_remove_group (keyfile, group.c_str(), nullptr);
 
     // Start Saving the settings
-    g_key_file_set_string (keyfile, group.c_str(), CSV_NAME, settings_name.c_str());
+    g_key_file_set_string (keyfile, group.c_str(), CSV_NAME, name.c_str());
     g_key_file_set_boolean (keyfile, group.c_str(), CSV_MULTI_SPLIT, multi_split);
     g_key_file_set_integer (keyfile, group.c_str(), CSV_SKIP_START, header_rows);
     g_key_file_set_integer (keyfile, group.c_str(), CSV_SKIP_END, footer_rows);
@@ -300,3 +369,24 @@ CsvTransSettings::save (const std::string& settings_name)
     }
     return error;
 }
+
+void
+CsvTransSettings::remove (void)
+{
+    if (trans_preset_is_reserved_name (name))
+        return;
+
+    auto keyfile = gnc_state_get_current ();
+    auto group = csv_group_prefix + name;
+    g_key_file_remove_group (keyfile, group.c_str(), nullptr);
+}
+
+
+bool
+CsvTransSettings::read_only (void)
+{
+    return ((name == no_settings) ||
+            (name == _(no_settings.c_str())) ||
+            (name == gnc_exp) ||
+            (name == _(gnc_exp.c_str())));
+}
diff --git a/src/import-export/csv-imp/gnc-csv-trans-settings.hpp b/src/import-export/csv-imp/gnc-csv-trans-settings.hpp
index 6d7218a..7577563 100644
--- a/src/import-export/csv-imp/gnc-csv-trans-settings.hpp
+++ b/src/import-export/csv-imp/gnc-csv-trans-settings.hpp
@@ -27,10 +27,6 @@
 #ifndef GNC_CSV_TRANS_SETTINGS_H
 #define GNC_CSV_TRANS_SETTINGS_H
 
-extern "C" {
-#include <gtk/gtk.h>
-}
-
 #include <string>
 #include <vector>
 #include "gnc-trans-props.hpp"
@@ -48,41 +44,46 @@ struct CsvTransSettings
 {
     CsvTransSettings() : header_rows{0}, footer_rows{0}, csv_format (true),
                     skip_alt_rows (false), multi_split (false),
-                    encoding {"UTF-8"}, custom {false}, custom_entry {nullptr},
-                    date_active {0}, currency_active {0}
+                    encoding {"UTF-8"}, custom {false}, custom_entry {""},
+                    date_active {0}, currency_active {0}, load_error {false},
+                    internal {false}
                     {
                         for (uint i = 0; i < SEP_NUM_OF_TYPES; i++)
                         {
                             separator[i] = false;
                         }
+                        separator [SEP_COMMA] = true;
                     }
 
-/** Finds CSV settings entries in the state key file and populates the
- *  tree model.
- *
- *  @param settings_store The liststore that is used for the combo's
- *  which holds the key name and visual text.
- */
-void find (GtkTreeModel *settings_store);
-
 /** Save the gathered widget properties to a key File.
  *
- *  @param settings_name The name the settings will be stored under.
- *
  *  @return true if there was a problem in saving.
  */
-bool save (const std::string& settings_name);
+bool save (void);
 
 /** Load the widget properties from a key File.
  *
+ *  @return true if there was a problem.
+ */
+bool load (void);
+
+/** Remove the preset from the state file.
+ */
+void remove (void);
+
+/** Check whether the user is allowed to save (over) or delete this preset or not.
+ *  The internally generated presets are read-only. The others
+ *  can be saved to the state file or deleted.
+ *
  *  @param group The group name where the settings are stored in the
  *  key file.
  *
  *  @return true if there was a problem.
  */
-bool load (const std::string& group);
+bool read_only (void);
 
 
+std::string   name;                         // Name given to this preset by the user
 int           header_rows;                  // Number of header rows
 int           footer_rows;                  // Number of footer rows
 bool          csv_format;                   // CSV import Format
@@ -101,6 +102,23 @@ int           currency_active;              // Currency Active id
 std::vector<GncTransPropType>  column_types;// The Column types in order
 std::vector<uint> column_widths;            // The Column widths
 
+bool          load_error;                   // Was there an error while parsing the state file ?
+bool          internal;                     // true for internally generated presets
 };
 
+using preset_vec = std::vector<std::shared_ptr<CsvTransSettings>>;
+/** Creates a vector of CsvTransSettings which combines
+ *  - one or more internally defined presets
+ *  - all preset found in the state key file.
+ *
+ *  @return a reference to the populated vector.
+ */
+const preset_vec& get_trans_presets (void);
+
+/** Check whether name can be used as a preset name.
+ *  The names of the internal presets are considered reserved.
+ *  A preset with such a name should not be saved or deleted.
+ */
+bool trans_preset_is_reserved_name (const std::string& name);
+
 #endif

commit 718a755f67d0a133b6e999289bc1bc935c956db9
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Tue Dec 13 13:47:09 2016 +0100

    Drop fixed-format csv importer
    
    It's functionality has been completely replicated
    in the generic csv importer.

diff --git a/po/POTFILES.in b/po/POTFILES.in
index f9917f4..f4fd55f 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -428,12 +428,9 @@ src/import-export/csv-exp/gnc-plugin-csv-export.c
 [type: gettext/gsettings]src/import-export/csv-exp/gschemas/org.gnucash.dialogs.export.csv.gschema.xml.in.in
 src/import-export/csv-imp/assistant-csv-account-import.c
 src/import-export/csv-imp/assistant-csv-account-import.glade
-src/import-export/csv-imp/assistant-csv-fixed-trans-import.cpp
-src/import-export/csv-imp/assistant-csv-fixed-trans-import.glade
 src/import-export/csv-imp/assistant-csv-trans-import.cpp
 src/import-export/csv-imp/assistant-csv-trans-import.glade
 src/import-export/csv-imp/csv-account-import.c
-src/import-export/csv-imp/csv-fixed-trans-import.cpp
 src/import-export/csv-imp/gnc-csv-account-map.c
 src/import-export/csv-imp/gnc-csv-gnumeric-popup.c
 src/import-export/csv-imp/gnc-csv-tokenizer.cpp
diff --git a/src/import-export/csv-imp/CMakeLists.txt b/src/import-export/csv-imp/CMakeLists.txt
index 1a54510..daeeef0 100644
--- a/src/import-export/csv-imp/CMakeLists.txt
+++ b/src/import-export/csv-imp/CMakeLists.txt
@@ -4,11 +4,9 @@ ADD_SUBDIRECTORY(test)
 SET(csv_import_SOURCES
   gncmod-csv-import.c
   assistant-csv-account-import.c
-  assistant-csv-fixed-trans-import.cpp
   assistant-csv-trans-import.cpp
   gnc-plugin-csv-import.c
   csv-account-import.c
-  csv-fixed-trans-import.cpp
   gnc-csv-account-map.c
   gnc-csv-gnumeric-popup.c
   gnc-csv-tokenizer.cpp
@@ -28,11 +26,9 @@ SET_SOURCE_FILES_PROPERTIES (${csv_import_SOURCES} PROPERTIES OBJECT_DEPENDS ${C
 
 SET(csv_import_noinst_HEADERS
   assistant-csv-account-import.h
-  assistant-csv-fixed-trans-import.h
   assistant-csv-trans-import.h
   gnc-plugin-csv-import.h
   csv-account-import.h
-  csv-fixed-trans-import.h
   gnc-csv-account-map.h
   gnc-csv-gnumeric-popup.h
   gnc-csv-tokenizer.hpp
@@ -67,7 +63,7 @@ INSTALL(TARGETS gncmod-csv-import
 
 # No headers to install
 
-SET(csv_import_GLADE assistant-csv-account-import.glade assistant-csv-fixed-trans-import.glade
+SET(csv_import_GLADE assistant-csv-account-import.glade
       assistant-csv-trans-import.glade)
 
 INSTALL(FILES ${csv_import_GLADE} DESTINATION share/gnucash/gtkbuilder)
diff --git a/src/import-export/csv-imp/Makefile.am b/src/import-export/csv-imp/Makefile.am
index 6068ec9..35003e4 100644
--- a/src/import-export/csv-imp/Makefile.am
+++ b/src/import-export/csv-imp/Makefile.am
@@ -5,11 +5,9 @@ pkglib_LTLIBRARIES=libgncmod-csv-import.la
 libgncmod_csv_import_la_SOURCES = \
   gncmod-csv-import.c \
   assistant-csv-account-import.c \
-  assistant-csv-fixed-trans-import.cpp \
   assistant-csv-trans-import.cpp \
   gnc-plugin-csv-import.c \
   csv-account-import.c \
-  csv-fixed-trans-import.cpp \
   gnc-csv-account-map.c \
   gnc-csv-tokenizer.cpp \
   gnc-csv-gnumeric-popup.c \
@@ -22,11 +20,9 @@ libgncmod_csv_import_la_SOURCES = \
 
 noinst_HEADERS = \
   assistant-csv-account-import.h \
-  assistant-csv-fixed-trans-import.h \
   assistant-csv-trans-import.h \
   gnc-plugin-csv-import.h \
   csv-account-import.h \
-  csv-fixed-trans-import.h \
   gnc-csv-account-map.h \
   gnc-csv-tokenizer.hpp \
   gnc-csv-gnumeric-popup.h \
@@ -80,7 +76,6 @@ ui_DATA = \
 gtkbuilderdir = ${GNC_GTKBUILDER_DIR}
 gtkbuilder_DATA = \
 	assistant-csv-account-import.glade \
-        assistant-csv-fixed-trans-import.glade \
 	assistant-csv-trans-import.glade
 
 EXTRA_DIST = $(ui_DATA) $(gtkbuilder_DATA)
diff --git a/src/import-export/csv-imp/assistant-csv-fixed-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-fixed-trans-import.cpp
deleted file mode 100644
index a92bd50..0000000
--- a/src/import-export/csv-imp/assistant-csv-fixed-trans-import.cpp
+++ /dev/null
@@ -1,808 +0,0 @@
-/*******************************************************************\
- * assistant-csv-fixed-trans-import.c -- An assistant for importing *
- *                                       set format transction file.*
- *                                                                  *
- * Copyright (C) 2014 Robert Fewell                                 *
- *                                                                  *
- * 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 2 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, contact:                        *
- *                                                                  *
- * Free Software Foundation           Voice:  +1-617-542-5942       *
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
- * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
-\********************************************************************/
-/** @file assistant-csv-fixed-trans-import.c
-    @brief CSV Import Assistant
-    @author Copyright (c) 2012 Robert Fewell
-*/
-
-#include <guid.hpp>
-
-extern "C"
-{
-
-#include "config.h"
-
-#include <gtk/gtk.h>
-#include <glib/gi18n.h>
-
-#include "dialog-utils.h"
-#include "gnc-ui.h"
-#include "gnc-uri-utils.h"
-#include "gnc-ui-util.h"
-
-#include "gnc-component-manager.h"
-
-#include "assistant-csv-fixed-trans-import.h"
-#include "csv-fixed-trans-import.h"
-}
-
-#include "gnc-tx-import.hpp"
-
-#define GNC_PREFS_GROUP "dialogs.import.csv"
-#define ASSISTANT_CSV_FIXED_TRANS_IMPORT_CM_CLASS "assistant-csv-fixed-trans-import"
-
-/* This static indicates the debugging module that this .o belongs to. */
-static QofLogModule log_module = GNC_MOD_ASSISTANT;
-
-/*************************************************************************/
-
-extern "C"
-{
-void csv_fixed_trans_import_assistant_prepare (GtkAssistant *assistant, GtkWidget *page, gpointer user_data);
-void csv_fixed_trans_import_assistant_finish (GtkAssistant *gtkassistant, gpointer user_data);
-void csv_fixed_trans_import_assistant_cancel (GtkAssistant *gtkassistant, gpointer user_data);
-void csv_fixed_trans_import_assistant_close (GtkAssistant *gtkassistant, gpointer user_data);
-
-void csv_fixed_trans_import_start_page_prepare (GtkAssistant *gtkassistant, gpointer user_data);
-void csv_fixed_trans_import_file_page_prepare (GtkAssistant *assistant, gpointer user_data);
-void csv_fixed_trans_import_view_page_prepare (GtkAssistant *gtkassistant, gpointer user_data);
-void csv_fixed_trans_import_finish_page_prepare (GtkAssistant *assistant, gpointer user_data);
-void csv_fixed_trans_import_summary_page_prepare (GtkAssistant *assistant, gpointer user_data);
-
-void csv_fixed_trans_import_sep_cb (GtkWidget *radio, gpointer user_data );
-void csv_fixed_trans_import_hrows_cb (GtkWidget *spin, gpointer user_data );
-
-void csv_fixed_trans_import_file_chooser_confirm_cb (GtkWidget *button, CsvFTImportInfo *info);
-}
-
-static gchar *gnc_input_dialog (GtkWidget *parent, const gchar *title, const gchar *msg, const gchar *default_input);
-
-static const gchar *finish_tree_string = N_(
-            "The transactions will be imported from the file '%s' when you click 'Apply'.\n\n"
-            "This operation is not reversable, so make sure you have a working backup.\n\n"
-            "You can verify your selections by clicking on 'Back' or 'Cancel' to Abort Import.\n");
-
-static const gchar *new_book_finish_tree_string = N_(
-            "The transactions will be imported from the file '%s' when you click 'Apply'.\n\n"
-            "This operation is not reversable, so please make sure you have a working backup.\n\n"
-            "You can verify your selections by clicking on 'Back' or 'Cancel' to Abort Import.\n\n"
-            "If this is your initial import into a new file, you will first see "
-            "a dialog for setting book options, since these can affect how "
-            "imported data is converted to GnuCash transactions.\n"
-            "Note: After import, you may need to use 'View / Filter By / Other' menu option "
-            "and select to show unused Accounts.\n");
-
-/* Escape '_' in string */
-static gchar *mnemonic_escape (const gchar *source);
-static gchar *mnemonic_escape (const gchar *source)
-{
-    const guchar *p;
-    gchar *dest;
-    gchar *q;
-
-    g_return_val_if_fail (source != NULL, NULL);
-
-    p = (guchar *) source;
-    q = dest = (char *)g_malloc (strlen (source) * 2 + 1);
-
-    while (*p)
-    {
-        switch (*p)
-        {
-        case '_':
-            *q++ = '_';
-            *q++ = '_';
-            break;
-        default:
-            *q++ = *p;
-            break;
-        }
-        p++;
-    }
-    *q = 0;
-    return dest;
-}
-
-static
-void create_regex (GString *regex_str, const gchar *sep)
-{
-    if (!sep) return;
-
-    g_string_printf (regex_str,
-            "\\G(?<date>\"(?:[^\"]|\"\")*\"|[^%s]*)%s"
-            "(?<type>\"(?:[^\"]|\"\")*\"|[^%s]*)%s"
-            "(?<sdate>\"(?:[^\"]|\"\")*\"|[^%s]*)%s"
-            "(?<acct_name>\"(?:[^\"]|\"\")*\"|[^%s]*)%s"
-            "(?<number>\"(?:[^\"]|\"\")*\"|[^%s]*)%s"
-            "(?<description>\"(?:[^\"]|\"\")*\"|[^%s]*)%s"
-            "(?<notes>\"(?:[^\"]|\"\")*\"|[^%s]*)%s"
-            "(?<memo>\"(?:[^\"]|\"\")*\"|[^%s]*)%s"
-            "(?<full_cat_name>\"(?:[^\"]|\"\")*\"|[^%s]*)%s"
-            "(?<cat_name>\"(?:[^\"]|\"\")*\"|[^%s]*)%s"
-            "(?<row_type>\"(?:[^\"]|\"\")*\"|[^%s]*)%s"
-            "(?<action>\"(?:[^\"]|\"\")*\"|[^%s]*)%s"
-            "(?<reconcile>\"(?:[^\"]|\"\")*\"|[^%s]*)%s"
-            "(?<amount_with_sym>\"(?:[^\"]|\"\")*\"|[^%s]*)%s"
-            "(?<commoditym>\"(?:[^\"]|\"\")*\"|[^%s]*)%s"
-            "(?<commodityn>\"(?:[^\"]|\"\")*\"|[^%s]*)%s"
-            "(?<amount_num>\"(?:[^\"]|\"\")*\"|[^%s]*)%s"
-            "(?<rate>\"(?:[^\"]|\"\")*\"|[^%s[:cntrl:]]*)(?:\\R*)",
-            sep, sep, sep, sep, sep, sep, sep, sep, sep, sep, sep, sep, sep, sep, sep, sep, sep, sep,
-            sep, sep, sep, sep, sep, sep, sep, sep, sep, sep, sep, sep, sep, sep, sep, sep, sep);
-}
-
-/*************************************************************************/
-
-/**************************************************
- * csv_fixed_trans_import_file_chooser_confirm_cb
- *
- * call back for ok button in file chooser widget
- **************************************************/
-void
-csv_fixed_trans_import_file_chooser_confirm_cb (GtkWidget *button, CsvFTImportInfo *info)
-{
-    GtkAssistant *assistant = GTK_ASSISTANT(info->window);
-    gint num = gtk_assistant_get_current_page (assistant);
-    GtkWidget *page = gtk_assistant_get_nth_page (assistant, num);
-
-    gchar *file_name;
-    csv_fixed_trans_import_result res;
-
-    gtk_assistant_set_page_complete (assistant, page, FALSE);
-
-    file_name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(info->file_chooser));
-
-    if (file_name)
-    {
-        gchar *filepath = gnc_uri_get_path (file_name);
-        gchar *filedir = g_path_get_dirname (filepath);
-        info->starting_dir = g_strdup (filedir);
-        g_free (filedir);
-        g_free (filepath);
-
-        info->file_name = g_strdup (file_name);
-
-        // generate preview
-        gtk_list_store_clear (info->store);
-        res = csv_fixed_trans_import_read_file (info->file_name, info->regexp->str, info->store, 1 );
-        if (res == RESULT_OPEN_FAILED)
-            gnc_error_dialog (info->window, _("The input file can not be opened."));
-        else if (res == RESULT_OK)
-            gtk_assistant_set_page_complete (assistant, page, TRUE);
-        else if (res == MATCH_FOUND)
-            gtk_assistant_set_page_complete (assistant, page, TRUE);
-    }
-    g_free (file_name);
-
-    DEBUG("file_name selected is %s", info->file_name);
-    DEBUG("starting directory is %s", info->starting_dir);
-
-    /* Step to next page if page is complete */
-    if(gtk_assistant_get_page_complete (assistant, page))
-        gtk_assistant_set_current_page (assistant, num + 1);
-
-}
-
-
-/*******************************************************
- * csv_fxed_trans_import_hrows_cb
- *
- * call back for the start row / number of header rows
- *******************************************************/
-void csv_fixed_trans_import_hrows_cb (GtkWidget *spin, gpointer user_data)
-{
-    CsvFTImportInfo *info = (CsvFTImportInfo*)user_data;
-
-    GtkTreeIter iter;
-    gboolean valid;
-    int num_rows;
-
-    /* Get number of rows for header */
-    info->header_rows = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON(spin));
-
-    /* Get number of rows displayed */
-    num_rows = gtk_tree_model_iter_n_children (GTK_TREE_MODEL(info->store), NULL);
-
-    /* Modify background color for header rows */
-    if (info->header_rows == 0)
-    {
-        valid = gtk_tree_model_iter_nth_child (GTK_TREE_MODEL(info->store), &iter, NULL, 0 );
-        if (valid)
-            gtk_list_store_set (info->store, &iter, FTROW_COLOR, NULL, -1);
-    }
-    else
-    {
-        if (info->header_rows - 1 < num_rows)
-        {
-            valid = gtk_tree_model_iter_nth_child (GTK_TREE_MODEL(info->store), &iter, NULL, info->header_rows - 1 );
-            if (valid)
-                gtk_list_store_set (info->store, &iter, FTROW_COLOR, "pink", -1);
-            valid = gtk_tree_model_iter_next (GTK_TREE_MODEL(info->store), &iter);
-            if (valid)
-                gtk_list_store_set (info->store, &iter, FTROW_COLOR, NULL, -1);
-        }
-    }
-}
-
-
-/*******************************************************
- * csv_fixed_trans_import_sep_cb
- *
- * call back for type of separartor required
- *******************************************************/
-void csv_fixed_trans_import_sep_cb (GtkWidget *radio, gpointer user_data)
-{
-    CsvFTImportInfo *info = (CsvFTImportInfo*)user_data;
-    const gchar *name;
-    gchar *temp;
-    const gchar *sep = NULL;
-
-    if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(radio)))
-    {
-        LEAVE("1st callback of pair. Defer to 2nd callback.");
-        return;
-    }
-
-    name = gtk_buildable_get_name (GTK_BUILDABLE(radio));
-    if (g_strcmp0 (name, "radio_semi") == 0)
-        sep = ";";
-    else if (g_strcmp0 (name, "radio_colon") == 0)
-        sep = ":";
-    else
-        sep = ","; /* Use as default as well */
-
-    create_regex (info->regexp, sep);
-
-    if (g_strcmp0 (name, "radio_custom") == 0)
-    {
-        temp = gnc_input_dialog (0, _("Adjust regular expression used for import"), _("This regular expression is used to parse the import file. Modify according to your needs.\n"), info->regexp->str);
-        if (temp)
-        {
-            g_string_assign (info->regexp, temp);
-            g_free (temp);
-        }
-    }
-
-    /* Generate preview */
-    gtk_list_store_clear (info->store);
-
-    gtk_tree_view_columns_autosize (GTK_TREE_VIEW(info->tree_view));
-
-    gtk_tree_view_set_grid_lines (GTK_TREE_VIEW(info->tree_view), GTK_TREE_VIEW_GRID_LINES_BOTH);
-
-    if (csv_fixed_trans_import_read_file (info->file_name, info->regexp->str, info->store, 11) == MATCH_FOUND)
-        gtk_widget_set_sensitive (info->header_row_spin, TRUE);
-    else
-        gtk_widget_set_sensitive (info->header_row_spin, FALSE);
-
-    /* Reset Header spin to 1 */
-    gtk_spin_button_set_value (GTK_SPIN_BUTTON(info->header_row_spin), 1);
-    csv_fixed_trans_import_hrows_cb (info->header_row_spin, info);
-}
-
-
-/*******************************************************
- * date_format_selected
- *
- * Event handler for selecting a new date format.
- *******************************************************/
-static void date_format_selected (GtkComboBoxText* format_selector, CsvFTImportInfo *info)
-{
-    info->date_format = gtk_combo_box_get_active (GTK_COMBO_BOX(format_selector));
-}
-
-
-/*******************************************************
- * currency_format_selected
- *
- * Event handler for selecting a new currency format.
- *******************************************************/
-static void currency_format_selected (GtkComboBoxText* currency_selector, CsvFTImportInfo *info)
-{
-    info->currency_format = gtk_combo_box_get_active (GTK_COMBO_BOX(currency_selector));
-}
-
-
-/*******************************************************
- * load_settings
- *
- * load the default settings for the assistant
- *******************************************************/
-static void
-load_settings (CsvFTImportInfo *info)
-{
-    info->header_rows = 0;
-    info->error = g_strdup("");
-    info->starting_dir = NULL;
-    info->file_name = NULL;
-    info->date_format = 0;
-    info->currency_format = 0;
-
-    /* The default directory for the user to select files. */
-    info->starting_dir = gnc_get_default_directory (GNC_PREFS_GROUP);
-}
-
-
-/* =============================================================== */
-
-
-/********************************************************************\
- * gnc_input_dialog                                                 *
- *   simple convenience dialog to get a single value from the user  *
- *   user may choose between "Ok" and "Cancel"                      *
- *                                                                  *
- * NOTE: This function does not return until the dialog is closed   *
- *                                                                  *
- * Args:   parent  - the parent window or NULL                      *
- *         title   - the title of the dialog                        *
- *         msg     - the message to display                         *
- *         default_input - will be displayed as default input       *
- * Return: the input (text) the user entered, if pressed "Ok"       *
- *         NULL, if pressed "Cancel"                                *
-\********************************************************************/
-static gchar *
-gnc_input_dialog (GtkWidget *parent, const gchar *title, const gchar *msg, const gchar *default_input)
-{
-    GtkWidget *dialog, *label, *content_area;
-    gint result;
-    GtkWidget *view;
-    GtkTextBuffer *buffer;
-    gchar *user_input;
-    GtkTextIter start, end;
-
-    /* Create the widgets */
-    dialog = gtk_dialog_new_with_buttons (title, GTK_WINDOW(parent),
-                                          (GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
-                                          GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
-                                          GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
-                                          NULL);
-
-    content_area = gtk_dialog_get_content_area (GTK_DIALOG(dialog));
-
-    // add a label
-    label = gtk_label_new (msg);
-    gtk_container_add (GTK_CONTAINER(content_area), label);
-
-    // add a textview
-    view = gtk_text_view_new ();
-    gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW(view), GTK_WRAP_WORD_CHAR);
-    buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(view));
-    gtk_text_buffer_set_text (buffer, default_input, -1);
-    gtk_container_add (GTK_CONTAINER(content_area), view);
-
-    // run the dialog
-    gtk_widget_show_all (dialog);
-    result = gtk_dialog_run (GTK_DIALOG(dialog));
-
-    if (result == GTK_RESPONSE_REJECT)
-        user_input = 0;
-    else
-    {
-        gtk_text_buffer_get_start_iter (buffer, &start);
-        gtk_text_buffer_get_end_iter (buffer, &end);
-        user_input = gtk_text_buffer_get_text (buffer,
-                                               &start, &end, FALSE);
-    }
-
-    gtk_widget_destroy (dialog);
-
-    return user_input;
-}
-
-
-/* =============================================================== */
-
-
-/*******************************************************
- * Assistant page prepare functions
- *******************************************************/
-void
-csv_fixed_trans_import_start_page_prepare (GtkAssistant *assistant,
-        gpointer user_data)
-{
-    gint num = gtk_assistant_get_current_page (assistant);
-    GtkWidget *page = gtk_assistant_get_nth_page (assistant, num);
-
-    /* Enable the Assistant Buttons */
-    gtk_assistant_set_page_complete (assistant, page, TRUE);
-}
-
-
-void
-csv_fixed_trans_import_file_page_prepare (GtkAssistant *assistant,
-                                        gpointer user_data)
-{
-    CsvFTImportInfo *info = (CsvFTImportInfo*)user_data;
-    gint num = gtk_assistant_get_current_page (assistant);
-    GtkWidget *page = gtk_assistant_get_nth_page (assistant, num);
-
-    info->header_rows = 0;
-
-    /* Set the default directory */
-    if (info->starting_dir)
-        gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER(info->file_chooser), info->starting_dir);
-
-    /* Disable the Forward Assistant Button */
-    gtk_assistant_set_page_complete (assistant, page, FALSE);
-}
-
-
-void
-csv_fixed_trans_import_view_page_prepare (GtkAssistant *assistant,
-        gpointer user_data)
-{
-    CsvFTImportInfo *info = (CsvFTImportInfo*)user_data;
-
-    gtk_list_store_clear (info->store);
-
-    if (csv_fixed_trans_import_read_file (info->file_name, info->regexp->str, info->store, 11 ) == MATCH_FOUND)
-    {
-        /* Reset Header spin to 1 */
-        if (info->header_rows != 1)
-            gtk_spin_button_set_value (GTK_SPIN_BUTTON(info->header_row_spin), 1);
-        csv_fixed_trans_import_hrows_cb (info->header_row_spin, info);
-        gtk_widget_set_sensitive (info->header_row_spin, TRUE);
-    }
-    else
-        gtk_widget_set_sensitive (info->header_row_spin, FALSE);
-}
-
-
-void
-csv_fixed_trans_import_finish_page_prepare (GtkAssistant *assistant,
-        gpointer user_data)
-{
-    CsvFTImportInfo *info = (CsvFTImportInfo*)user_data;
-    gint num = gtk_assistant_get_current_page (assistant);
-    GtkWidget *page = gtk_assistant_get_nth_page (assistant, num);
-    gchar *text;
-    gboolean test_ok = FALSE;
-
-    /* Set Finish page text */
-    if (csv_fixed_trans_test_one_line (info))
-    {
-        /* Before creating accounts, if this is a new book, tell user they can
-         * specify book options, since they affect how transactions are created */
-        if (info->new_book)
-            text = g_strdup_printf (gettext (new_book_finish_tree_string), info->file_name);
-        else
-            text = g_strdup_printf (gettext (finish_tree_string), info->file_name);
-        test_ok = TRUE;
-    }
-    else
-        text = g_strdup (_("Date or Number format is incorrect, press 'back' to correct"));
-
-    gtk_label_set_text (GTK_LABEL(info->finish_label), text);
-    g_free (text);
-
-    /* Save the Window size and directory */
-    gnc_set_default_directory (GNC_PREFS_GROUP, info->starting_dir);
-
-    /* Enable the Assistant Buttons */
-    gtk_assistant_set_page_complete (assistant, page, test_ok);
-}
-
-
-void
-csv_fixed_trans_import_summary_page_prepare (GtkAssistant *assistant,
-        gpointer user_data)
-{
-    CsvFTImportInfo *info = (CsvFTImportInfo*)user_data;
-    gchar *text, *errtext, *mtext;
-
-    /* Before creating accounts, if this is a new book, let user specify
-     * book options, since they affect how transactions are created */
-    if (info->new_book)
-        info->new_book = gnc_new_book_option_display (info->window);
-
-    if (g_strcmp0 (info->error, "") != 0)
-    {
-        GtkTextBuffer *buffer;
-
-        buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(info->summary_error_view));
-        text = g_strdup_printf (gettext ("Import completed but with warnings!\n\nThe number of Transactions added was %u and "
-                                        "%u were duplicates.\n\nSee below for warnings..."), info->num_new, info->num_duplicates);
-        errtext = g_strdup_printf ("%s", info->error);
-        gtk_text_buffer_set_text (buffer, errtext, -1);
-        g_free (errtext);
-        g_free (info->error);
-    }
-    else
-        text = g_strdup_printf (gettext ("Import completed successfully!\n\nThe number of Transactions added was %u and "
-                                        "%u were duplicates.\n"), info->num_new, info->num_duplicates);
-
-    mtext = g_strdup_printf ("<span size=\"medium\"><b>%s</b></span>", text);
-    gtk_label_set_markup (GTK_LABEL(info->summary_label), mtext);
-
-    g_free (text);
-    g_free (mtext);
-}
-
-
-void
-csv_fixed_trans_import_assistant_prepare (GtkAssistant *assistant, GtkWidget *page,
-                              gpointer user_data)
-{
-    gint currentpage = gtk_assistant_get_current_page (assistant);
-
-    switch (currentpage)
-    {
-    case 0:
-        /* Current page is Import Start page */
-        csv_fixed_trans_import_start_page_prepare (assistant, user_data);
-        break;
-    case 1:
-        /* Current page is File select page */
-        csv_fixed_trans_import_file_page_prepare (assistant, user_data);
-        break;
-    case 2:
-        /* Current page is Inmport View page */
-        csv_fixed_trans_import_view_page_prepare (assistant, user_data);
-        break;
-    case 3:
-        /* Current page is Finish page */
-        csv_fixed_trans_import_finish_page_prepare (assistant, user_data);
-        break;
-    case 4:
-        /* Current page is Summary page */
-        csv_fixed_trans_import_summary_page_prepare (assistant, user_data);
-        break;
-    }
-}
-
-
-/*******************************************************
- * Assistant call back functions
- *******************************************************/
-static void
-csv_fixed_trans_import_assistant_destroy_cb (GtkObject *object, gpointer user_data)
-{
-    CsvFTImportInfo *info = (CsvFTImportInfo*)user_data;
-    gnc_unregister_gui_component_by_data (ASSISTANT_CSV_FIXED_TRANS_IMPORT_CM_CLASS, info);
-    g_free (info);
-}
-
-void
-csv_fixed_trans_import_assistant_cancel (GtkAssistant *assistant, gpointer user_data)
-{
-    CsvFTImportInfo *info = (CsvFTImportInfo*)user_data;
-    gnc_close_gui_component_by_data (ASSISTANT_CSV_FIXED_TRANS_IMPORT_CM_CLASS, info);
-}
-
-void
-csv_fixed_trans_import_assistant_close (GtkAssistant *assistant, gpointer user_data)
-{
-    CsvFTImportInfo *info = (CsvFTImportInfo*)user_data;
-    gnc_close_gui_component_by_data (ASSISTANT_CSV_FIXED_TRANS_IMPORT_CM_CLASS, info);
-}
-
-void
-csv_fixed_trans_import_assistant_finish (GtkAssistant *assistant, gpointer user_data)
-{
-    CsvFTImportInfo *info = (CsvFTImportInfo*)user_data;
-
-    gtk_list_store_clear (info->store);
-    csv_fixed_trans_import_read_file (info->file_name, info->regexp->str, info->store, 0 );
-    csv_fixed_trans_import (info);
-}
-
-static void
-csv_fixed_trans_import_close_handler (gpointer user_data)
-{
-    CsvFTImportInfo *info = (CsvFTImportInfo*)user_data;
-
-    g_free (info->starting_dir);
-    g_free (info->file_name);
-    g_string_free (info->regexp, TRUE);
-
-    gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(info->window));
-    gtk_widget_destroy (info->window);
-}
-
-/*******************************************************
- * Create the Assistant
- *******************************************************/
-static GtkWidget *
-csv_fixed_trans_import_assistant_create (CsvFTImportInfo *info)
-{
-    GtkBuilder *builder;
-    GtkWidget *window;
-    GtkWidget *box, *h_box;
-    GtkWidget *button;
-    GtkCellRenderer *renderer;
-    GtkTreeViewColumn *column;
-    gchar *mnemonic_desc = NULL;
-    gint i;
-    GtkContainer *date_format_container, *currency_format_container;
-
-    builder = gtk_builder_new();
-    gnc_builder_add_from_file (builder, "assistant-csv-fixed-trans-import.glade", "num_hrows_adj");
-    gnc_builder_add_from_file (builder, "assistant-csv-fixed-trans-import.glade", "CSV Fixed Transaction Import Assistant");
-    window = GTK_WIDGET(gtk_builder_get_object (builder, "CSV Fixed Transaction Import Assistant"));
-    info->window = window;
-
-    /* Load default settings */
-    load_settings (info);
-
-    /* Enable buttons on all page. */
-    gtk_assistant_set_page_complete (GTK_ASSISTANT(window),
-                                     GTK_WIDGET(gtk_builder_get_object(builder, "start_page")),
-                                     TRUE);
-    gtk_assistant_set_page_complete (GTK_ASSISTANT(window),
-                                     GTK_WIDGET(gtk_builder_get_object(builder, "file_page")),
-                                     FALSE);
-    gtk_assistant_set_page_complete (GTK_ASSISTANT(window),
-                                     GTK_WIDGET(gtk_builder_get_object(builder, "import_view_page")),
-                                     TRUE);
-    gtk_assistant_set_page_complete (GTK_ASSISTANT(window),
-                                     GTK_WIDGET(gtk_builder_get_object(builder, "finish_page")),
-                                     FALSE);
-    gtk_assistant_set_page_complete (GTK_ASSISTANT(window),
-                                     GTK_WIDGET(gtk_builder_get_object(builder, "summary_page")),
-                                     TRUE);
-
-    /* Start Page */
-
-    /* File chooser Page */
-    info->file_chooser = gtk_file_chooser_widget_new (GTK_FILE_CHOOSER_ACTION_OPEN);
-    g_signal_connect (G_OBJECT(info->file_chooser), "file-activated",
-                      G_CALLBACK(csv_fixed_trans_import_file_chooser_confirm_cb), info);
-    button = gtk_button_new_from_stock (GTK_STOCK_OK);
-    gtk_widget_set_size_request (button, 100, -1);
-    gtk_widget_show (button);
-    h_box = gtk_hbox_new (TRUE, 0);
-    gtk_box_pack_start (GTK_BOX(h_box), button, FALSE, FALSE, 0);
-    gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER(info->file_chooser), h_box);
-    g_signal_connect (G_OBJECT(button), "clicked",
-                      G_CALLBACK(csv_fixed_trans_import_file_chooser_confirm_cb), info);
-
-    box = GTK_WIDGET(gtk_builder_get_object(builder, "file_page"));
-    gtk_box_pack_start (GTK_BOX(box), info->file_chooser, TRUE, TRUE, 6);
-    gtk_widget_show (info->file_chooser);
-
-    /* Preview Page */
-    info->header_row_spin = GTK_WIDGET(gtk_builder_get_object (builder, "num_hrows"));
-    info->tree_view = GTK_WIDGET(gtk_builder_get_object (builder, "treeview"));
-
-    /* Add in the date format combo box and hook it up to an event handler. */
-    info->date_format_combo = GTK_COMBO_BOX_TEXT(gtk_combo_box_text_new());
-    for (i = 0; i < num_date_formats; i++)
-    {
-        gtk_combo_box_text_append_text (info->date_format_combo, _(date_format_user[i]));
-    }
-    gtk_combo_box_set_active (GTK_COMBO_BOX(info->date_format_combo), 0);
-    g_signal_connect (G_OBJECT(info->date_format_combo), "changed",
-                      G_CALLBACK(date_format_selected), (gpointer)info);
-
-    /* Add it to the assistant. */
-    date_format_container = GTK_CONTAINER(gtk_builder_get_object (builder, "date_format_container"));
-    gtk_container_add (date_format_container, GTK_WIDGET(info->date_format_combo));
-    gtk_widget_show_all (GTK_WIDGET(date_format_container));
-
-    /* Add in the currency format combo box and hook it up to an event handler. */
-    info->currency_format_combo = GTK_COMBO_BOX_TEXT(gtk_combo_box_text_new());
-    for (i = 0; i < num_currency_formats; i++)
-    {
-        gtk_combo_box_text_append_text (info->currency_format_combo, _(currency_format_user[i]));
-    }
-    /* Default will the locale */
-    gtk_combo_box_set_active (GTK_COMBO_BOX(info->currency_format_combo), 0);
-    g_signal_connect (G_OBJECT(info->currency_format_combo), "changed",
-                      G_CALLBACK(currency_format_selected), (gpointer)info);
-
-    /* Add it to the assistant. */
-    currency_format_container = GTK_CONTAINER(gtk_builder_get_object (builder, "currency_format_container"));
-    gtk_container_add (currency_format_container, GTK_WIDGET(info->currency_format_combo));
-    gtk_widget_show_all (GTK_WIDGET(currency_format_container));
-
-    /* Comma Separated file default */
-    info->regexp = g_string_new ("");
-    create_regex (info->regexp, ",");
-
-    /* create model and bind to view */
-    info->store = gtk_list_store_new (FTN_COLUMNS,
-                                      G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
-                                      G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
-                                      G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
-                                      G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
-
-    gtk_tree_view_set_model (GTK_TREE_VIEW(info->tree_view), GTK_TREE_MODEL(info->store));
-
-#define CREATE_COLUMN(description,column_id) \
-  renderer = gtk_cell_renderer_text_new (); \
-  mnemonic_desc = mnemonic_escape (_(description)); \
-  column = gtk_tree_view_column_new_with_attributes (mnemonic_desc, renderer, "text", column_id, NULL); \
-  gtk_tree_view_column_add_attribute (column, renderer, "background", FTROW_COLOR); \
-  gtk_tree_view_column_set_resizable (column, TRUE); \
-  gtk_tree_view_append_column (GTK_TREE_VIEW(info->tree_view), column); \
-  g_free (mnemonic_desc);
-    CREATE_COLUMN ("date", FTDATE);
-    CREATE_COLUMN ("type", FTTYPE);
-    CREATE_COLUMN ("sdate", FTSDATE);
-    CREATE_COLUMN ("acct_name", FTACCT_NAME);
-    CREATE_COLUMN ("number", FTNUMBER);
-    CREATE_COLUMN ("description", FTDESCRIPTION);
-    CREATE_COLUMN ("notes", FTNOTES);
-    CREATE_COLUMN ("memo", FTMEMO);
-    CREATE_COLUMN ("full_cat_name", FTFULL_CAT_NAME);
-    CREATE_COLUMN ("cat_name", FTCAT_NAME);
-    CREATE_COLUMN ("row_type", FTRTYPE);
-    CREATE_COLUMN ("action", FTACTION);
-    CREATE_COLUMN ("reconcile", FTRECONCILE);
-    CREATE_COLUMN ("amount_with_sym", FTAMOUNT_WITH_SYM);
-    CREATE_COLUMN ("commoditym", FTCOMMODITYM);
-    CREATE_COLUMN ("commodityn", FTCOMMODITYN);
-    CREATE_COLUMN ("amount_num", FTAMOUNT_NUM);
-    CREATE_COLUMN ("rate", FTRATE);
-
-    // Enable Horizontal and vertical grid lines
-    gtk_tree_view_set_grid_lines (GTK_TREE_VIEW(info->tree_view), GTK_TREE_VIEW_GRID_LINES_BOTH);
-
-    /* Finish Page */
-    info->finish_label = GTK_WIDGET(gtk_builder_get_object (builder, "finish_label"));
-    info->progressbar = GTK_WIDGET(gtk_builder_get_object (builder, "progressbar"));
-
-    /* Summary Page */
-    info->summary_label = GTK_WIDGET(gtk_builder_get_object (builder, "summary_label"));
-    info->summary_error_view = GTK_WIDGET(gtk_builder_get_object (builder, "summary_error_view"));
-
-    g_signal_connect (G_OBJECT(window), "destroy",
-                      G_CALLBACK(csv_fixed_trans_import_assistant_destroy_cb), info);
-
-    gnc_restore_window_size (GNC_PREFS_GROUP, GTK_WINDOW(info->window));
-
-    gtk_builder_connect_signals (builder, info);
-    g_object_unref (G_OBJECT(builder));
-    return window;
-}
-
-
-/********************************************************************\
- * gnc_file_csv_account_import                                      *
- * opens up a assistant to import accounts.                         *
- *                                                                  *
- * Args:   import_type                                              *
- * Return: nothing                                                  *
-\********************************************************************/
-void
-gnc_file_csv_fixed_trans_import(void)
-{
-    CsvFTImportInfo *info;
-
-    info = g_new0 (CsvFTImportInfo, 1);
-
-    /* In order to trigger a book options display on the creation of a new book,
-     * we need to detect when we are dealing with a new book. */
-    info->new_book = gnc_is_new_book();
-
-    csv_fixed_trans_import_assistant_create (info);
-
-    gnc_register_gui_component (ASSISTANT_CSV_FIXED_TRANS_IMPORT_CM_CLASS,
-                                NULL, csv_fixed_trans_import_close_handler,
-                                info);
-
-    gtk_widget_show_all (info->window);
-
-    gnc_window_adjust_for_screen (GTK_WINDOW(info->window));
-}
diff --git a/src/import-export/csv-imp/assistant-csv-fixed-trans-import.glade b/src/import-export/csv-imp/assistant-csv-fixed-trans-import.glade
deleted file mode 100644
index bbb5058..0000000
--- a/src/import-export/csv-imp/assistant-csv-fixed-trans-import.glade
+++ /dev/null
@@ -1,502 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<interface>
-  <requires lib="gtk+" version="2.16"/>
-  <!-- interface-naming-policy project-wide -->
-  <object class="GtkAdjustment" id="num_hrows_adj">
-    <property name="upper">100</property>
-    <property name="step_increment">1</property>
-    <property name="page_increment">10</property>
-  </object>
-  <object class="GtkAssistant" id="CSV Fixed Transaction Import Assistant">
-    <property name="can_focus">False</property>
-    <property name="border_width">12</property>
-    <property name="title" translatable="yes">CSV Fixed Transaction Import Assistant</property>
-    <property name="default_width">400</property>
-    <property name="default_height">500</property>
-    <signal name="close" handler="csv_fixed_trans_import_assistant_close" swapped="no"/>
-    <signal name="apply" handler="csv_fixed_trans_import_assistant_finish" swapped="no"/>
-    <signal name="prepare" handler="csv_fixed_trans_import_assistant_prepare" swapped="no"/>
-    <signal name="cancel" handler="csv_fixed_trans_import_assistant_cancel" swapped="no"/>
-    <child>
-      <object class="GtkVBox" id="start_page">
-        <property name="visible">True</property>
-        <property name="can_focus">False</property>
-        <property name="border_width">12</property>
-        <child>
-          <object class="GtkLabel" id="start_label">
-            <property name="visible">True</property>
-            <property name="can_focus">False</property>
-            <property name="label" translatable="yes">
-This assistant will help you import transactions from an Exported CSV Transaction file.
-
-The file must be in the same format as that exported as this is a fixed format import which can be seen by looking at a file created by using the Export Transactions to CSV menu option.
-
-All Accounts, Securites and Currencies must exist. If this is to be a new file, this can be achieved by using the Export Accounts menu option which creates a new file based on the existing accounts.
-
-Transactions to import are compared to existing and newly added transactions by the following fields: date, account, description, notes, number and value.
-If the same transaction appears more than once on a day it is deemed to be a duplicate with a list of duplicate entries provided on the summary page with any errors. 
-
-This operation is not reversable, so make sure you have a working backup.
-
-Click on 'Forward' to proceed or 'Cancel' to Abort Import.
-</property>
-            <property name="justify">center</property>
-            <property name="wrap">True</property>
-          </object>
-          <packing>
-            <property name="expand">False</property>
-            <property name="fill">False</property>
-            <property name="position">0</property>
-          </packing>
-        </child>
-      </object>
-      <packing>
-        <property name="page_type">intro</property>
-        <property name="title" translatable="yes">Fixed Format Transaction Import Assistant</property>
-        <property name="complete">True</property>
-      </packing>
-    </child>
-    <child>
-      <object class="GtkVBox" id="file_page">
-        <property name="visible">True</property>
-        <property name="can_focus">False</property>
-        <property name="border_width">12</property>
-        <child>
-          <object class="GtkLabel" id="label4">
-            <property name="visible">True</property>
-            <property name="can_focus">False</property>
-            <property name="label" translatable="yes">
-Enter file name and location for the Import...
-</property>
-            <property name="wrap">True</property>
-          </object>
-          <packing>
-            <property name="expand">False</property>
-            <property name="fill">False</property>
-            <property name="position">0</property>
-          </packing>
-        </child>
-      </object>
-      <packing>
-        <property name="title" translatable="yes">Choose File to Import</property>
-      </packing>
-    </child>
-    <child>
-      <object class="GtkVBox" id="import_view_page">
-        <property name="visible">True</property>
-        <property name="can_focus">False</property>
-        <property name="border_width">12</property>
-        <child>
-          <object class="GtkHBox" id="hbox1">
-            <property name="visible">True</property>
-            <property name="can_focus">False</property>
-            <property name="spacing">15</property>
-            <child>
-              <object class="GtkFrame" id="frame1">
-                <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <property name="label_xalign">0</property>
-                <property name="shadow_type">none</property>
-                <child>
-                  <object class="GtkAlignment" id="date_format_container">
-                    <property name="visible">True</property>
-                    <property name="can_focus">False</property>
-                    <property name="left_padding">12</property>
-                  </object>
-                </child>
-                <child type="label">
-                  <object class="GtkLabel" id="label1">
-                    <property name="visible">True</property>
-                    <property name="can_focus">False</property>
-                    <property name="label" translatable="yes">Date Format</property>
-                    <property name="use_markup">True</property>
-                  </object>
-                </child>
-              </object>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-                <property name="position">0</property>
-              </packing>
-            </child>
-            <child>
-              <object class="GtkFrame" id="frame2">
-                <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <property name="label_xalign">0</property>
-                <property name="shadow_type">none</property>
-                <child>
-                  <object class="GtkAlignment" id="currency_format_container">
-                    <property name="visible">True</property>
-                    <property name="can_focus">False</property>
-                    <property name="left_padding">12</property>
-                  </object>
-                </child>
-                <child type="label">
-                  <object class="GtkLabel" id="label2">
-                    <property name="visible">True</property>
-                    <property name="can_focus">False</property>
-                    <property name="label" translatable="yes">Currency Format</property>
-                    <property name="use_markup">True</property>
-                  </object>
-                </child>
-              </object>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-                <property name="position">1</property>
-              </packing>
-            </child>
-            <child>
-              <object class="GtkFrame" id="frame3">
-                <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <property name="label_xalign">0</property>
-                <property name="shadow_type">none</property>
-                <child>
-                  <object class="GtkAlignment" id="alignment1">
-                    <property name="visible">True</property>
-                    <property name="can_focus">False</property>
-                    <property name="left_padding">12</property>
-                    <child>
-                      <object class="GtkHBox" id="hbox3">
-                        <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <child>
-                          <object class="GtkSpinButton" id="num_hrows">
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="editable">False</property>
-                            <property name="invisible_char">●</property>
-                            <property name="invisible_char_set">True</property>
-                            <property name="primary_icon_activatable">False</property>
-                            <property name="secondary_icon_activatable">False</property>
-                            <property name="primary_icon_sensitive">True</property>
-                            <property name="secondary_icon_sensitive">True</property>
-                            <property name="adjustment">num_hrows_adj</property>
-                            <signal name="value-changed" handler="csv_fixed_trans_import_hrows_cb" swapped="no"/>
-                          </object>
-                          <packing>
-                            <property name="expand">False</property>
-                            <property name="fill">False</property>
-                            <property name="position">0</property>
-                          </packing>
-                        </child>
-                      </object>
-                    </child>
-                  </object>
-                </child>
-                <child type="label">
-                  <object class="GtkLabel" id="label5">
-                    <property name="visible">True</property>
-                    <property name="can_focus">False</property>
-                    <property name="label" translatable="yes">Number of rows for the Header</property>
-                    <property name="use_markup">True</property>
-                  </object>
-                </child>
-              </object>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-                <property name="position">2</property>
-              </packing>
-            </child>
-          </object>
-          <packing>
-            <property name="expand">False</property>
-            <property name="fill">False</property>
-            <property name="position">0</property>
-          </packing>
-        </child>
-        <child>
-          <object class="GtkFrame" id="separator_frame">
-            <property name="visible">True</property>
-            <property name="can_focus">False</property>
-            <property name="label_xalign">0</property>
-            <property name="shadow_type">in</property>
-            <child>
-              <object class="GtkTable" id="table4">
-                <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <property name="border_width">12</property>
-                <property name="n_rows">2</property>
-                <property name="n_columns">3</property>
-                <child>
-                  <object class="GtkRadioButton" id="radio_comma">
-                    <property name="label" translatable="yes">Comma Separated</property>
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="receives_default">False</property>
-                    <property name="active">True</property>
-                    <property name="draw_indicator">True</property>
-                    <signal name="toggled" handler="csv_fixed_trans_import_sep_cb" swapped="no"/>
-                  </object>
-                </child>
-                <child>
-                  <object class="GtkRadioButton" id="radio_semi">
-                    <property name="label" translatable="yes">Semicolon Separated</property>
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="receives_default">False</property>
-                    <property name="draw_indicator">True</property>
-                    <property name="group">radio_comma</property>
-                    <signal name="toggled" handler="csv_fixed_trans_import_sep_cb" swapped="no"/>
-                  </object>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="right_attach">2</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkRadioButton" id="radio_custom">
-                    <property name="label" translatable="yes">Custom regular Expression</property>
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="receives_default">False</property>
-                    <property name="draw_indicator">True</property>
-                    <property name="group">radio_comma</property>
-                    <signal name="toggled" handler="csv_fixed_trans_import_sep_cb" swapped="no"/>
-                  </object>
-                  <packing>
-                    <property name="top_attach">1</property>
-                    <property name="bottom_attach">2</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkRadioButton" id="radio_colon">
-                    <property name="label" translatable="yes">Colon Separated</property>
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="receives_default">False</property>
-                    <property name="draw_indicator">True</property>
-                    <property name="group">radio_comma</property>
-                    <signal name="toggled" handler="csv_fixed_trans_import_sep_cb" swapped="no"/>
-                  </object>
-                  <packing>
-                    <property name="left_attach">2</property>
-                    <property name="right_attach">3</property>
-                  </packing>
-                </child>
-              </object>
-            </child>
-            <child type="label">
-              <object class="GtkLabel" id="label6">
-                <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <property name="label" translatable="yes">Select Separator Type</property>
-                <property name="use_markup">True</property>
-              </object>
-            </child>
-          </object>
-          <packing>
-            <property name="expand">False</property>
-            <property name="fill">False</property>
-            <property name="padding">7</property>
-            <property name="position">1</property>
-          </packing>
-        </child>
-        <child>
-          <object class="GtkFrame" id="preview_frame">
-            <property name="visible">True</property>
-            <property name="can_focus">False</property>
-            <property name="label_xalign">0</property>
-            <property name="shadow_type">in</property>
-            <child>
-              <object class="GtkScrolledWindow" id="scroll_window">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="border_width">6</property>
-                <property name="hscrollbar_policy">automatic</property>
-                <property name="vscrollbar_policy">automatic</property>
-                <child>
-                  <object class="GtkTreeView" id="treeview">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                  </object>
-                </child>
-              </object>
-            </child>
-            <child type="label">
-              <object class="GtkLabel" id="label8">
-                <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <property name="label" translatable="yes">Preview</property>
-                <property name="use_markup">True</property>
-              </object>
-            </child>
-          </object>
-          <packing>
-            <property name="expand">True</property>
-            <property name="fill">True</property>
-            <property name="position">2</property>
-          </packing>
-        </child>
-      </object>
-      <packing>
-        <property name="title" translatable="yes">Import Transaction Preview, first 10 rows only</property>
-        <property name="complete">True</property>
-      </packing>
-    </child>
-    <child>
-      <object class="GtkVBox" id="finish_page">
-        <property name="visible">True</property>
-        <property name="can_focus">False</property>
-        <child>
-          <object class="GtkAlignment" id="alignment2">
-            <property name="visible">True</property>
-            <property name="can_focus">False</property>
-            <child>
-              <object class="GtkLabel" id="finish_label">
-                <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <property name="label" translatable="yes">Press Apply to create export file.
-Cancel to abort.</property>
-                <property name="justify">center</property>
-                <property name="wrap">True</property>
-              </object>
-            </child>
-          </object>
-          <packing>
-            <property name="expand">True</property>
-            <property name="fill">False</property>
-            <property name="position">0</property>
-          </packing>
-        </child>
-        <child>
-          <object class="GtkAlignment" id="alignment3">
-            <property name="visible">True</property>
-            <property name="can_focus">False</property>
-            <child>
-              <object class="GtkHBox" id="hbox2">
-                <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <child>
-                  <object class="GtkLabel" id="label3">
-                    <property name="visible">True</property>
-                    <property name="can_focus">False</property>
-                  </object>
-                  <packing>
-                    <property name="expand">True</property>
-                    <property name="fill">False</property>
-                    <property name="position">0</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkVBox" id="vbox1">
-                    <property name="visible">True</property>
-                    <property name="can_focus">False</property>
-                    <child>
-                      <object class="GtkLabel" id="label9">
-                        <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <property name="label" translatable="yes">Progress</property>
-                        <attributes>
-                          <attribute name="weight" value="semibold"/>
-                        </attributes>
-                      </object>
-                      <packing>
-                        <property name="expand">True</property>
-                        <property name="fill">True</property>
-                        <property name="padding">10</property>
-                        <property name="position">0</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <object class="GtkProgressBar" id="progressbar">
-                        <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <property name="show_text">True</property>
-                        <property name="text" translatable="yes"> </property>
-                      </object>
-                      <packing>
-                        <property name="expand">True</property>
-                        <property name="fill">False</property>
-                        <property name="position">1</property>
-                      </packing>
-                    </child>
-                  </object>
-                  <packing>
-                    <property name="expand">True</property>
-                    <property name="fill">True</property>
-                    <property name="position">2</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkLabel" id="label7">
-                    <property name="visible">True</property>
-                    <property name="can_focus">False</property>
-                  </object>
-                  <packing>
-                    <property name="expand">True</property>
-                    <property name="fill">False</property>
-                    <property name="position">3</property>
-                  </packing>
-                </child>
-              </object>
-            </child>
-          </object>
-          <packing>
-            <property name="expand">True</property>
-            <property name="fill">False</property>
-            <property name="position">1</property>
-          </packing>
-        </child>
-      </object>
-      <packing>
-        <property name="page_type">confirm</property>
-        <property name="title" translatable="yes">Import Transactions Now</property>
-        <property name="complete">True</property>
-      </packing>
-    </child>
-    <child>
-      <object class="GtkVBox" id="summary_page">
-        <property name="visible">True</property>
-        <property name="can_focus">False</property>
-        <property name="border_width">12</property>
-        <child>
-          <object class="GtkLabel" id="summary_label">
-            <property name="visible">True</property>
-            <property name="can_focus">False</property>
-            <property name="label" translatable="yes">label</property>
-            <property name="use_markup">True</property>
-            <property name="justify">center</property>
-            <property name="wrap">True</property>
-          </object>
-          <packing>
-            <property name="expand">True</property>
-            <property name="fill">True</property>
-            <property name="position">0</property>
-          </packing>
-        </child>
-        <child>
-          <object class="GtkScrolledWindow" id="summary_error_scrolledwindow">
-            <property name="visible">True</property>
-            <property name="can_focus">False</property>
-            <property name="border_width">1</property>
-            <property name="hscrollbar_policy">automatic</property>
-            <property name="vscrollbar_policy">automatic</property>
-            <property name="shadow_type">etched-in</property>
-            <child>
-              <object class="GtkTextView" id="summary_error_view">
-                <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <property name="left_margin">2</property>
-                <property name="right_margin">2</property>
-              </object>
-            </child>
-          </object>
-          <packing>
-            <property name="expand">True</property>
-            <property name="fill">True</property>
-            <property name="position">1</property>
-          </packing>
-        </child>
-      </object>
-      <packing>
-        <property name="page_type">summary</property>
-        <property name="title" translatable="yes">Import Summary</property>
-        <property name="complete">True</property>
-      </packing>
-    </child>
-  </object>
-</interface>
diff --git a/src/import-export/csv-imp/assistant-csv-fixed-trans-import.h b/src/import-export/csv-imp/assistant-csv-fixed-trans-import.h
deleted file mode 100644
index 727346c..0000000
--- a/src/import-export/csv-imp/assistant-csv-fixed-trans-import.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/*******************************************************************\
- * assistant-csv-fixed-trans-import.h -- An assistant for importing *
- *                                       set format transction file.*
- *                                                                  *
- * Copyright (C) 2014 Robert Fewell                                 *
- *                                                                  *
- * 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 2 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, contact:                        *
- *                                                                  *
- * Free Software Foundation           Voice:  +1-617-542-5942       *
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
- * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
-\********************************************************************/
-/** @file assistant-csv-fixed-trans-import.h
-    @brief CSV Import Assistant
-    @author Copyright (c) 2011 Robert Fewell
-*/
-#ifndef GNC_ASSISTANT_CSV_FIXED_IMPORT_H
-#define GNC_ASSISTANT_CSV_FIXED_IMPORT_H
-
-// Account tree model
-enum fixed_trans_import_model_columns
-{
-   FTDATE, FTTYPE, FTSDATE, FTACCT_NAME, FTNUMBER, FTDESCRIPTION, FTNOTES, FTMEMO, FTFULL_CAT_NAME,
-   FTCAT_NAME, FTRTYPE, FTACTION, FTRECONCILE, FTAMOUNT_WITH_SYM, FTCOMMODITYM, FTCOMMODITYN,
-   FTAMOUNT_NUM, FTRATE, FTROW_COLOR, FTN_COLUMNS
-};
-
-typedef struct
-{
-    GtkWidget       *window;
-    GtkWidget       *assistant;
-
-    GtkWidget       *file_chooser;           /**< The File Chooser widget on File page */
-    GtkWidget       *tree_view;              /**< The Preview Treeview */
-    GtkListStore    *store;                  /**< The Liststore of imported data */
-    GString         *regexp;                 /**< The Regular expression string */
-    GtkWidget       *header_row_spin;        /**< The Number of header rows widget */
-    GtkComboBoxText *date_format_combo;      /**< The Combo Text widget for selecting the date format */
-    GtkComboBoxText *currency_format_combo;  /**< The Combo Text widget for selecting the currency format */
-    GtkWidget       *progressbar;            /**< The Progress widget on the Finish page */
-    GtkWidget       *finish_label;           /**< The Label widget on the Finish page */
-    GtkWidget       *summary_label;          /**< The Label widget on the Summary page */
-    GtkWidget       *summary_error_view;     /**< The Text view widget on the Summary page */
-
-    gchar           *starting_dir;           /**< The starting directory where import file is */
-    gchar           *file_name;              /**< The File name to import */
-    gchar           *error;                  /**< The Error Text */
-
-    gint             header_rows;            /**< The Number of header rows, usually defaults to 1 */
-    gint             num_new;                /**< The Number of new transactions imported */
-    gint             num_duplicates;         /**< The Number of duplicate transactions not imported */
-    gint             date_format;            /**< The format of the text in the date columns from date_format_internal. */
-    gint             currency_format;        /**< The currency format, 0 for locale, 1 for comma dec and 2 for period */
-    gboolean         new_book;               /**< Are we importing into a new book?; if yes, call book options */
-} CsvFTImportInfo;
-
-
-/** The gnc_file_csv_fixed_trans_import() will let the user import
- *  transactions from a delimited fixed format file.
- */
-void gnc_file_csv_fixed_trans_import (void);
-#endif
diff --git a/src/import-export/csv-imp/csv-fixed-trans-import.cpp b/src/import-export/csv-imp/csv-fixed-trans-import.cpp
deleted file mode 100644
index a6762e5..0000000
--- a/src/import-export/csv-imp/csv-fixed-trans-import.cpp
+++ /dev/null
@@ -1,725 +0,0 @@
-/*******************************************************************\
- * csv-fixed-trans-import.c -- Fixed Format Transaction importing   *
- *                                           from file.             *
- *                                                                  *
- * Copyright (C) 2014 Robert Fewell                                 *
- *                                                                  *
- * Based on code from bi_import written by Sebastian Held  and      *
- * Mike Evans.                                                      *
- *                                                                  *
- * 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 2 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, contact:                        *
- *                                                                  *
- * Free Software Foundation           Voice:  +1-617-542-5942       *
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
- * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
-\********************************************************************/
-
-#include <guid.hpp>
-
-extern "C"
-{
-#include "config.h"
-
-#include <glib.h>
-#include <glib/gi18n.h>
-#include <glib/gstdio.h>
-
-#include "gnc-ui-util.h"
-#include <regex.h>
-#include "Account.h"
-#include "gnc-component-manager.h"
-#include "csv-fixed-trans-import.h"
-#include "gnc-ui-util.h"
-#include "engine-helpers.h"
-#include "gnc-gdate-utils.h"
-}
-
-#include "gnc-trans-props.hpp"
-/* This static indicates the debugging module that this .o belongs to. */
-static QofLogModule log_module = GNC_MOD_ASSISTANT;
-
-
-/* This helper function takes a regexp match and fills the model */
-static void
-fill_model_with_match (GMatchInfo *match_info,
-               const gchar *match_name,
-               GtkListStore *store,
-               GtkTreeIter *iterptr,
-               gint column)
-{
-    gchar *temp;
-
-    if (!match_info || !match_name)
-        return;
-
-    temp = g_match_info_fetch_named (match_info, match_name);
-    if (temp)
-    {
-        g_strstrip (temp);
-        if (g_str_has_prefix (temp, "\""))
-        {
-            if (strlen (temp) >= 2)
-            {
-                gchar *toptail = g_strndup (temp + 1, strlen (temp)-2);
-                gchar **parts = g_strsplit (toptail, "\"\"", -1);
-                temp = g_strjoinv ("\"", parts);
-                g_strfreev (parts);
-                g_free (toptail);
-            }
-        }
-        gtk_list_store_set (store, iterptr, column, temp, -1);
-        g_free (temp);
-    }
-}
-
-/*******************************************************
- * csv_fixed_trans_import_read_file
- *
- * Parse the file for a correctly formatted file
- *******************************************************/
-csv_fixed_trans_import_result
-csv_fixed_trans_import_read_file (const gchar *filename, const gchar *parser_regexp,
-                      GtkListStore *store, guint max_rows)
-{
-    gchar      *locale_cont, *contents;
-    GMatchInfo *match_info = NULL;
-    GRegex     *regexpat = NULL;
-    GError     *err;
-    guint       row = 0;
-    gboolean   match_found = FALSE;
-
-    // model
-    GtkTreeIter iter;
-
-    if (!g_file_get_contents (filename, &locale_cont, NULL, NULL))
-    {
-        //gnc_error_dialog( 0, _("File %s cannot be opened."), filename);
-        return RESULT_OPEN_FAILED;
-    }
-
-    contents = g_locale_to_utf8 (locale_cont, -1, NULL, NULL, NULL);
-    g_free (locale_cont);
-
-    // compile the regular expression and check for errors
-    err = NULL;
-    regexpat =
-        g_regex_new (parser_regexp, G_REGEX_OPTIMIZE, static_cast<GRegexMatchFlags>(0), &err);
-    if (err != NULL)
-    {
-        GtkWidget *dialog;
-        gchar *errmsg;
-
-        errmsg = g_strdup_printf (_("Error in regular expression '%s':\n%s"),
-                                  parser_regexp, err->message);
-        g_error_free (err);
-
-        dialog = gtk_message_dialog_new (NULL,
-                                         GTK_DIALOG_MODAL,
-                                         GTK_MESSAGE_ERROR,
-                                         GTK_BUTTONS_OK, "%s", errmsg);
-        gtk_dialog_run (GTK_DIALOG (dialog));
-        gtk_widget_destroy (dialog);
-        g_free (errmsg);
-        g_free (contents);
-
-        return RESULT_ERROR_IN_REGEXP;
-    }
-
-    g_regex_match (regexpat, contents, static_cast<GRegexMatchFlags>(0), &match_info);
-    while (g_match_info_matches (match_info))
-    {
-        match_found = TRUE;
-        // fill in the values
-        gtk_list_store_append (store, &iter);
-        fill_model_with_match (match_info, "date", store, &iter, FTDATE);
-        fill_model_with_match (match_info, "type", store, &iter, FTTYPE);
-        fill_model_with_match (match_info, "sdate", store, &iter, FTSDATE);
-        fill_model_with_match (match_info, "acct_name", store, &iter, FTACCT_NAME);
-        fill_model_with_match (match_info, "number", store, &iter, FTNUMBER);
-        fill_model_with_match (match_info, "description", store, &iter, FTDESCRIPTION);
-        fill_model_with_match (match_info, "notes", store, &iter, FTNOTES);
-        fill_model_with_match (match_info, "memo", store, &iter, FTMEMO);
-        fill_model_with_match (match_info, "full_cat_name", store, &iter, FTFULL_CAT_NAME);
-        fill_model_with_match (match_info, "cat_name", store, &iter, FTCAT_NAME);
-        fill_model_with_match (match_info, "row_type", store, &iter, FTRTYPE);
-        fill_model_with_match (match_info, "action", store, &iter, FTACTION);
-        fill_model_with_match (match_info, "reconcile", store, &iter, FTRECONCILE);
-        fill_model_with_match (match_info, "amount_with_sym", store, &iter, FTAMOUNT_WITH_SYM);
-        fill_model_with_match (match_info, "commoditym", store, &iter, FTCOMMODITYM);
-        fill_model_with_match (match_info, "commodityn", store, &iter, FTCOMMODITYN);
-        fill_model_with_match (match_info, "amount_num", store, &iter, FTAMOUNT_NUM);
-        fill_model_with_match (match_info, "rate", store, &iter, FTRATE);
-        gtk_list_store_set (store, &iter, FTROW_COLOR, NULL, -1);
-
-        row++;
-        if (row == max_rows)
-            break;
-        g_match_info_next (match_info, &err);
-    }
-
-    g_match_info_free (match_info);
-    g_regex_unref (regexpat);
-    g_free (contents);
-
-    if (err != NULL)
-    {
-        g_printerr ("Error while matching: %s\n", err->message);
-        g_error_free (err);
-    }
-
-    if (match_found == TRUE)
-        return MATCH_FOUND;
-    else
-        return RESULT_OK;
-}
-
-
-/*******************************************************
- * save_error_text
- *
- * Add error text to existing errors
- *******************************************************/
-static void
-save_error_text (CsvFTImportInfo *info, gint row, gchar *etext)
-{
-    gchar *current_error_text;
-    gchar *text;
-
-    current_error_text = g_strdup (info->error);
-
-    if (g_strcmp0 (info->error, "") != 0)
-        g_free (info->error);
-
-    text = g_strdup_printf (gettext("Row %u, %s\n"), row + 1, etext);
-    info->error = g_strconcat (current_error_text, text, NULL);
-    g_free (text);
-    g_free (current_error_text);
-}
-
-
-/*******************************************************
- * parse_number_string
- *
- * Parse the number string and return a gnc_number
- *******************************************************/
-static gboolean
-parse_number_string (CsvFTImportInfo *info, const gchar *num_string, gnc_numeric *ret_num)
-{
-    char       *endptr, *str_num;
-    gboolean    valid = FALSE;
-    gchar      *result = NULL;
-    gnc_numeric num;
-
-    str_num = g_strdup (num_string);
-
-    /* Currency format */
-    switch (info->currency_format)
-    {
-        case 0:
-            /* Currency locale */
-            valid = xaccParseAmount (str_num, TRUE, &num, &endptr);
-            break;
-        case 1:
-            /* Currency decimal period */
-            valid = xaccParseAmountExtended (str_num, TRUE, '-', '.', ',', "\003\003", "$+", &num, &endptr);
-            break;
-        case 2:
-            /* Currency decimal comma */
-            valid = xaccParseAmountExtended (str_num, TRUE, '-', ',', '.', "\003\003", "$+", &num, &endptr);
-            break;
-    }
-
-    // xaccParseAmountExtended does not recognise trailing '-' numbers, lets scan string for any
-    result = g_strrstr (num_string, "-");
-    if ((result != NULL) && !gnc_numeric_negative_p (num))
-        num = gnc_numeric_neg (num);
-
-    *ret_num = num;
-    g_free (str_num);
-
-    return valid;
-}
-
-
-/*******************************************************
- * csv_fixed_trans_test_one_line
- *
- * Test a Transaction for valid date and split for valid number
- *******************************************************/
-gboolean
-csv_fixed_trans_test_one_line (CsvFTImportInfo *info)
-{
-    gboolean     valid;
-    GtkTreeIter  iter;
-    gchar       *date, *row_type, *amount_num;
-    gnc_numeric  amount;
-    gint         row;
-    gboolean     trans_found = FALSE;
-    gboolean     split_found = FALSE;
-    gboolean     date_ok = FALSE;
-    gboolean     num_ok = FALSE;
-
-    row = info->header_rows;
-    valid = gtk_tree_model_iter_nth_child (GTK_TREE_MODEL(info->store), &iter, NULL, row);
-
-    while (valid)
-    {
-        /* Walk through the list, reading each row */
-        gtk_tree_model_get (GTK_TREE_MODEL (info->store), &iter,
-                            FTDATE, &date,
-                            FTRTYPE, &row_type,
-                            FTAMOUNT_NUM, &amount_num, -1);
-
-        if (g_strcmp0 (row_type, "T") == 0) // We have the Transaction line
-        {
-            try
-            {
-                date_ok = TRUE;
-                parse_date (date, info->date_format);
-            }
-            catch (...)
-            {
-                date_ok = FALSE;
-            }
-            trans_found = TRUE;
-        }
-
-        if (g_strcmp0 (row_type, "S") == 0) // We have the split line
-        {
-            if (g_strcmp0 (amount_num, "") != 0) // test for valid to number
-            {
-                if (parse_number_string (info, amount_num, &amount))
-                    num_ok = TRUE;
-                else
-                    num_ok = FALSE;
-            }
-            else
-                num_ok = FALSE;
-
-            split_found = TRUE;
-        }
-
-    /* free resources */
-    g_free (date);
-    g_free (row_type);
-    g_free (amount_num);
-
-    if ((trans_found == TRUE) && (split_found == TRUE))
-        break;
-
-    valid = gtk_tree_model_iter_next (GTK_TREE_MODEL(info->store), &iter);
-    row++;
-    }
-
-    if ((date_ok == TRUE) && (num_ok == TRUE))
-       return TRUE;
-    else
-       return FALSE;
-}
-
-
-/*******************************************************
- * check_for_existing_trans
- *
- * Check if the Transaction allready exists
- *******************************************************/
-static gboolean
-check_for_existing_trans (CsvFTImportInfo *info, Account *acc, Transaction *trans, gnc_numeric value)
-{
-    Query       *q;
-    GSList      *p1, *p2;
-    GList       *splits;
-    QofBook     *book;
-    GDate        tdate;
-    time64       time_val_start;
-    time64       time_val_end;
-    gboolean     ret = FALSE;
-    const gchar *notes, *desc, *num;
-
-    q = qof_query_create_for (GNC_ID_SPLIT);
-    book = gnc_get_current_book();
-    qof_query_set_book (q, book);
-
-    /* Sort by transaction date */
-    p1 = g_slist_prepend (NULL, gpointer(TRANS_DATE_POSTED));
-    p1 = g_slist_prepend (p1, gpointer(SPLIT_TRANS));
-    p2 = g_slist_prepend (NULL, gpointer(QUERY_DEFAULT_SORT));
-    qof_query_set_sort_order (q, p1, p2, NULL);
-
-    xaccQueryAddSingleAccountMatch (q, acc, QOF_QUERY_AND);
-
-    tdate = xaccTransGetDatePostedGDate (trans);
-    time_val_start = gnc_time64_get_day_start_gdate (&tdate);
-    time_val_end = gnc_time64_get_day_end_gdate (&tdate);
-    xaccQueryAddDateMatchTT (q, TRUE, time_val_start, TRUE, time_val_end, QOF_QUERY_AND);
-
-    desc = xaccTransGetDescription (trans) ? xaccTransGetDescription (trans) : "" ;
-    notes = xaccTransGetNotes (trans) ? xaccTransGetNotes (trans) : "" ;
-    num = xaccTransGetNum (trans) ? xaccTransGetNum (trans) : "" ;
-
-    xaccQueryAddNumberMatch (q, num, TRUE, FALSE, QOF_COMPARE_EQUAL, QOF_QUERY_AND);
-    xaccQueryAddDescriptionMatch (q, desc, TRUE, FALSE, QOF_COMPARE_EQUAL, QOF_QUERY_AND);
-    xaccQueryAddNotesMatch (q, notes, TRUE, FALSE, QOF_COMPARE_EQUAL, QOF_QUERY_AND);
-
-    if (gnc_numeric_negative_p (value))
-        xaccQueryAddValueMatch (q, value, QOF_NUMERIC_MATCH_CREDIT,
-                               QOF_COMPARE_EQUAL, QOF_QUERY_AND);
-    else
-        xaccQueryAddValueMatch (q, value, QOF_NUMERIC_MATCH_DEBIT,
-                               QOF_COMPARE_EQUAL, QOF_QUERY_AND);
-
-    // The returned list is managed internally by QofQuery, Do not free.
-    splits = qof_query_run (q);
-
-    if (g_list_length (splits) != 0)
-        ret = TRUE;
-
-    // Compare transaction types, for A/Payable/Recievable
-    if (g_list_length (splits) == 1)
-    {
-        Split *split;
-        GList *first_split = g_list_first (splits);
-        split = static_cast<Split*>(first_split->data);
-        if (xaccTransGetTxnType (trans) == xaccTransGetTxnType (xaccSplitGetParent (split)))
-            ret = TRUE;
-        else
-            ret = FALSE;
-    }
-
-    qof_query_destroy (q);
-    return ret;
-}
-
-
-/*******************************************************
- * csv_fixed_trans_import
- *
- * Parse the liststore for new Transactions
- *******************************************************/
-void
-csv_fixed_trans_import (CsvFTImportInfo *info)
-{
-    QofBook             *book;
-    Account             *root;
-    gboolean             valid;
-    GtkTreeIter          iter;
-    gchar               *date, *type, *sdate, *acct_name, *number, *description, *notes, *memo;
-    gchar               *full_cat_name, *cat_name, *row_type, *action, *reconcile;
-    gchar               *amount_with_sym, *commoditym, *commodityn, *amount_num, *rate;
-    gchar               *void_reason;
-    gboolean             void_trans = FALSE;
-    gnc_commodity       *trans_commodity, *split_commodity;
-    gnc_commodity_table *table;
-    Transaction         *new_trans = NULL, *prev_trans = NULL;
-    gboolean             split_check = FALSE;
-    gdouble              row, trow, max_row;
-    GtkProgressBar      *progress;
-    gdouble              percent = 0.0;
-
-    ENTER("");
-    book = gnc_get_current_book();
-    root = gnc_book_get_root_account (book);
-    table = gnc_commodity_table_get_table (book);
-
-    info->num_new = 0;
-    info->num_duplicates = 0;
-    void_reason = g_strdup (" ");
-
-    /* Move to the first valid entry in store */
-    row = info->header_rows;
-    valid = gtk_tree_model_iter_nth_child (GTK_TREE_MODEL(info->store), &iter, NULL, row);
-
-    max_row = gtk_tree_model_iter_n_children (GTK_TREE_MODEL(info->store), NULL);
-
-    progress = GTK_PROGRESS_BAR(info->progressbar);
-
-    gnc_suspend_gui_refresh ();
-
-    while (valid)
-    {
-        gchar *message = g_strdup_printf (gettext("%.0f%% Complete"), percent * 100);
-        percent = (row / max_row);
-        gtk_progress_bar_set_fraction (progress, percent);
-        gtk_progress_bar_set_text (progress, message);
-        g_free (message);
-
-        while (gtk_events_pending ())
-           gtk_main_iteration ();
-
-        /* Walk through the list, reading each row */
-        gtk_tree_model_get (GTK_TREE_MODEL(info->store), &iter,
-                            FTDATE, &date,
-                            FTTYPE, &type,
-                            FTSDATE, &sdate,
-                            FTACCT_NAME, &acct_name,
-                            FTNUMBER, &number,
-                            FTDESCRIPTION, &description,
-                            FTNOTES, &notes,
-                            FTMEMO, &memo,
-                            FTFULL_CAT_NAME, &full_cat_name,
-                            FTCAT_NAME, &cat_name,
-                            FTRTYPE, &row_type,
-                            FTACTION, &action,
-                            FTRECONCILE, &reconcile,
-                            FTAMOUNT_WITH_SYM, &amount_with_sym,
-                            FTCOMMODITYM, &commoditym,
-                            FTCOMMODITYN, &commodityn,
-                            FTAMOUNT_NUM, &amount_num,
-                            FTRATE, &rate, -1);
-
-        if (g_strcmp0 (row_type, "T") == 0) // We have the Transaction line
-        {
-            gboolean trans_error = FALSE;
-
-            new_trans = NULL; // Reset new_trans to NULL
-            trow = row; //record the row the Transaction was on
-
-            time64 tdate = 0;
-            time64 idate = 0;
-            try
-            {
-                tdate = parse_date (date, info->date_format);
-                if (g_strcmp0 (type, "I") == 0) // Invoice Transaction Type
-                    idate = parse_date (sdate, info->date_format);
-            }
-            catch (...)
-            {
-                save_error_text (info, row, _("Date or invoice date is invalid for Transaction"));
-                trans_error = TRUE;
-            }
-
-            trans_commodity = gnc_commodity_table_lookup (table, commodityn, commoditym);
-            if (!trans_commodity) // invalid commodity
-            {
-                save_error_text (info, row, _("Commodity is invalid for Transaction"));
-                trans_error = TRUE;
-            }
-
-            if (trans_error == FALSE) // Create Transaction
-            {
-                new_trans = xaccMallocTransaction (book);
-
-                if (prev_trans != NULL)
-                {
-                    if (void_trans) // Test to Void Transaction
-                        xaccTransVoid (prev_trans, void_reason);
-                    xaccTransCommitEdit (prev_trans); // commit the previous transaction
-                    info->num_new = info->num_new + 1;
-                    void_trans = FALSE;
-                }
-
-                xaccTransBeginEdit (new_trans);
-                xaccTransSetCurrency (new_trans, trans_commodity);
-                xaccTransSetDatePostedSecsNormalized (new_trans, tdate);
-                if (g_strcmp0 (description, "") != 0)
-                    xaccTransSetDescription (new_trans, description);
-                if (g_strcmp0 (notes, "") != 0)
-                    xaccTransSetNotes (new_trans, notes);
-                if (g_strcmp0 (number, "") != 0)
-                    xaccTransSetNum (new_trans, number);
-
-                if (g_strcmp0 (type, "I") == 0) // Invoice Transaction Type
-                {
-                    Timespec ts;
-                    timespecFromTime64 (&ts, idate);
-                    xaccTransSetTxnType (new_trans, 'I');
-                    xaccTransSetDateDueTS (new_trans, &ts);
-                }
-                if (g_strcmp0 (type, "P") == 0) // Payment Transaction Type
-                    xaccTransSetTxnType (new_trans, 'P');
-                if (g_strcmp0 (type, "L") == 0) // Lot Link Transaction Type
-                    xaccTransSetTxnType (new_trans, 'L');
- 
-                prev_trans = new_trans;
-
-                split_check = FALSE; // Reset split_check
-            }
-        }
-
-        if ((g_strcmp0 (row_type, "S") == 0) && (new_trans != NULL)) // We have a Split line
-        {
-            Account    *acct;
-            Split      *split;
-            gnc_numeric amount, value, price_rate;
-            char        rec;
-            char       *endptr, *str_num, *str_rate;
-            gboolean    split_error = FALSE;
-            gboolean    num_error = FALSE;
-            gboolean    rate_error = FALSE;
-            gboolean    type_deb;
-
-            split_commodity = gnc_commodity_table_lookup (table, commodityn, commoditym);
-
-            acct = gnc_account_lookup_by_full_name (root, full_cat_name);
-            if (!acct) // invalid account
-            {
-                save_error_text (info, row, _("Account is invalid for Split"));
-                split_error = TRUE;
-            }
-
-            if (!split_commodity) // invalid commodity
-            {
-                save_error_text (info, row, _("Commodity is invalid for Split"));
-                split_error = TRUE;
-            }
-
-            if (!gnc_commodity_equal (split_commodity, xaccAccountGetCommodity (acct))) // non matching commodity
-            {
-                save_error_text (info, row, _("Commodity does not match account for Split"));
-                split_error = TRUE;
-            }
-
-            time64 rdate = 0;
-            try
-            {
-                if (g_strcmp0 (reconcile, _("y")) == 0) // Reconciled
-                    rdate = parse_date (sdate, info->date_format);
-            }
-            catch (...)
-            {
-                save_error_text (info, row, _("Reconcile date is invalid for Split"));
-                split_error = TRUE;
-            }
-
-            // Lets get some numbers
-            num_error = parse_number_string (info, amount_num, &amount);
-            rate_error = parse_number_string (info, rate, &price_rate);
-
-            if (!num_error) // invalid amount
-            {
-                save_error_text (info, row, _("Numeric Amount is invalid for Split"));
-                split_error = TRUE;
-            }
-
-            if (!rate_error) // invalid rate/price
-            {
-                save_error_text (info, row, _("Numeric Rate/Price is invalid for Split"));
-                split_error = TRUE;
-            }
-
-            if (split_error == FALSE)
-                value = gnc_numeric_mul (amount, price_rate, GNC_DENOM_AUTO, GNC_HOW_RND_ROUND);
-
-            // Check to see if Transaction allready exists
-            if ((acct != NULL) && (split_check == FALSE)) // we only check first split
-            {
-                gboolean ret_error = check_for_existing_trans (info, acct, new_trans, value);
-                if (ret_error)
-                {
-                    const gchar *desc, *notes;
-                    gchar       *error_text;
-
-                    desc = xaccTransGetDescription (new_trans) ? xaccTransGetDescription (new_trans) : "" ;
-                    notes = xaccTransGetNotes (new_trans) ? xaccTransGetNotes (new_trans) : "" ;
-
-                    error_text = g_strdup_printf (gettext (
-                                "Transaction already Exists, Description was '%s' and Notes was '%s', Skipping"),
-                                 desc, notes);
-
-                    save_error_text (info, trow, error_text);
-                    info->num_duplicates = info->num_duplicates + 1;
-                    g_free (error_text);
-                    split_error = TRUE;
-                }
-                split_check = TRUE;
-            }
-
-            if (split_error == FALSE)
-            {
-                split = xaccMallocSplit (book);
-                xaccSplitSetAccount (split, acct);
-                xaccSplitSetParent (split, new_trans);
-                if (g_strcmp0 (memo, "") != 0)
-                    xaccSplitSetMemo (split, memo);
-                if (g_strcmp0 (action, "") != 0)
-                    xaccSplitSetAction (split, action);
-
-                if (g_strcmp0 (reconcile, _("n")) == 0) // Normal
-                    rec = 'n';
-                else if (g_strcmp0 (reconcile, _("c")) == 0) // Cleared
-                    rec = 'c';
-                else if (g_strcmp0 (reconcile, _("y")) == 0) // Reconciled
-                {
-                    Timespec ts;
-                    timespecFromTime64 (&ts, rdate);
-                    xaccSplitSetDateReconciledTS (split, &ts);
-                    rec = 'y';
-                }
-                else if (g_strcmp0 (reconcile, _("f")) == 0) // Frozen
-                    rec = 'f';
-                else if (g_strcmp0 (reconcile, _("v")) == 0) // Void 
-                {
-                    g_free (void_reason);
-                    void_reason = g_strdup (notes);
-                    void_trans = TRUE;
-                    rec = 'v';
-                }
-                else /* default: */
-                {
-                    save_error_text (info, row, _("Unrecognised Reconcile flag, using default for Split"));
-                    rec = 'n';
-                }
-                xaccSplitSetReconcile (split, rec);
-
-                // Set Amount and Value entries
-                xaccSplitSetAmount (split, amount);
-                xaccSplitSetValue (split, value);
-            }
-            else
-            {
-                 // We have an Error, lets destroy the Started Transaction
-                 xaccTransDestroy (new_trans);
-                 xaccTransCommitEdit (new_trans);
-                 new_trans = NULL;
-                 prev_trans = NULL;
-            }
-        }
-
-        valid = gtk_tree_model_iter_next (GTK_TREE_MODEL(info->store), &iter);
-        row++;
-
-        /* free resources */
-        g_free (date);
-        g_free (type);
-        g_free (sdate);
-        g_free (acct_name);
-        g_free (number);
-        g_free (description);
-        g_free (notes);
-        g_free (memo);
-        g_free (full_cat_name);
-        g_free (cat_name);
-        g_free (row_type);
-        g_free (action);
-        g_free (reconcile);
-        g_free (amount_with_sym);
-        g_free (commoditym);
-        g_free (commodityn);
-        g_free (amount_num);
-        g_free (rate);
-    }
-
-    if (new_trans != NULL)
-    {
-        if (void_trans) // Test to Void Transaction
-            xaccTransVoid (new_trans, void_reason);
-        xaccTransCommitEdit (new_trans); // commit the last new Transaction
-        info->num_new = info->num_new + 1;
-    }
-    g_free (void_reason);
-
-    gnc_resume_gui_refresh ();
-
-    LEAVE("");
-}
diff --git a/src/import-export/csv-imp/csv-fixed-trans-import.h b/src/import-export/csv-imp/csv-fixed-trans-import.h
deleted file mode 100644
index 69fafaa..0000000
--- a/src/import-export/csv-imp/csv-fixed-trans-import.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*******************************************************************\
- * csv-fixed-trans-import.h -- Fixed Format Transaction importing   *
- *                                           from file.             *
- *                                                                  *
- * Copyright (C) 2014 Robert Fewell                                 *
- *                                                                  *
- * Based on code from bi_import written by Sebastian Held  and      *
- * Mike Evans.                                                      *
- *                                                                  *
- * 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 2 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, contact:                        *
- *                                                                  *
- * Free Software Foundation           Voice:  +1-617-542-5942       *
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
- * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
-\********************************************************************/
-
-#ifndef CSV_FIXED_TRANS_IMPORT_H
-#define CSV_FIXED_TRANS_IMPORT_H
-
-#include <glib.h>
-#include <gtk/gtk.h>
-
-#include "assistant-csv-fixed-trans-import.h"
-
-enum _csv_fixed_trans_import_result
-{
-    RESULT_OK,
-    RESULT_OPEN_FAILED,
-    RESULT_ERROR_IN_REGEXP,
-    MATCH_FOUND,
-};
-typedef enum _csv_fixed_trans_import_result csv_fixed_trans_import_result;
-
-csv_fixed_trans_import_result
-csv_fixed_trans_import_read_file (const gchar *filename, const gchar *parser_regexp, GtkListStore *store, guint max_rows );
-
-gboolean csv_fixed_trans_test_one_line (CsvFTImportInfo *info);
-
-void csv_fixed_trans_import (CsvFTImportInfo *info);
-
-#endif /* CSV_FIXED_TRANS_IMPORT_H */
-
diff --git a/src/import-export/csv-imp/gnc-plugin-csv-import-ui.xml b/src/import-export/csv-imp/gnc-plugin-csv-import-ui.xml
index 1e372ac..c119a64 100644
--- a/src/import-export/csv-imp/gnc-plugin-csv-import-ui.xml
+++ b/src/import-export/csv-imp/gnc-plugin-csv-import-ui.xml
@@ -5,7 +5,6 @@
       	<placeholder name="FileImportPlaceholder">
       	   <menuitem name="FileCsvImportAccounts" action="CsvImportAccountAction"/>
       	   <menuitem name="FileCsvImportTrans" action="CsvImportTransAction"/>
-      	   <menuitem name="FileCsvImportFixedTrans" action="CsvImportFixedTransAction"/>
       	</placeholder>
       </menu>
     </menu>
diff --git a/src/import-export/csv-imp/gnc-plugin-csv-import.c b/src/import-export/csv-imp/gnc-plugin-csv-import.c
index 2e47348..2b46afb 100644
--- a/src/import-export/csv-imp/gnc-plugin-csv-import.c
+++ b/src/import-export/csv-imp/gnc-plugin-csv-import.c
@@ -29,7 +29,6 @@
 #include "gnc-plugin-manager.h"
 
 #include "assistant-csv-account-import.h"
-#include "assistant-csv-fixed-trans-import.h"
 #include "assistant-csv-trans-import.h"
 
 static void gnc_plugin_csv_import_class_init (GncPluginCsvImportClass *klass);
@@ -39,7 +38,6 @@ static void gnc_plugin_csv_import_finalize (GObject *object);
 /* Command callbacks */
 static void gnc_plugin_csv_import_tree_cmd (GtkAction *action, GncMainWindowActionData *data);
 static void gnc_plugin_csv_import_trans_cmd (GtkAction *action, GncMainWindowActionData *data);
-static void gnc_plugin_csv_import_fixed_trans_cmd (GtkAction *action, GncMainWindowActionData *data);
 
 #define PLUGIN_ACTIONS_NAME "gnc-plugin-csv-import-actions"
 #define PLUGIN_UI_FILENAME  "gnc-plugin-csv-import-ui.xml"
@@ -56,11 +54,6 @@ static GtkActionEntry gnc_plugin_actions [] =
         N_("Import Transactions from a CSV file"),
         G_CALLBACK (gnc_plugin_csv_import_trans_cmd)
     },
-    {
-        "CsvImportFixedTransAction", GTK_STOCK_CONVERT, N_("Import Transactions in a _Fixed Format CSV file..."), NULL,
-        N_("Import Transactions in a Fixed Format CSV file"),
-        G_CALLBACK (gnc_plugin_csv_import_fixed_trans_cmd)
-    },
 };
 static guint gnc_plugin_n_actions = G_N_ELEMENTS (gnc_plugin_actions);
 
@@ -164,13 +157,6 @@ gnc_plugin_csv_import_trans_cmd (GtkAction *action,
     gnc_file_csv_trans_import ();
 }
 
-static void
-gnc_plugin_csv_import_fixed_trans_cmd (GtkAction *action,
-                                 GncMainWindowActionData *data)
-{
-    gnc_file_csv_fixed_trans_import ();
-}
-
 /************************************************************
  *                    Plugin Bootstrapping                   *
  ************************************************************/

commit a0320d3a213cf7908adb312fa7328de96f996493
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Tue Dec 13 12:22:11 2016 +0100

    Allow user to only select column_types that make sense in the current context
    
    The context being either multi-split or two-split. There's no use
    in selecting a Transaction ID column in two-split mode for example.
    Similarly none of the 'Transfer xzy' types will be used in
    multi-split mode.

diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index 53e0828..034c35d 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -229,25 +229,21 @@ csv_import_trans_load_settings (CsvImportTrans *info)
     }
 
     // Set start row
-    info->parse_data->skip_start_lines = info->settings_data.header_rows;
     GtkAdjustment *adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(info->start_row_spin));
     gtk_adjustment_set_upper (adj, info->parse_data->parsed_lines.size());
     gtk_spin_button_set_value (GTK_SPIN_BUTTON(info->start_row_spin),
             info->settings_data.header_rows);
 
     // Set end row
-    info->parse_data->skip_end_lines = info->settings_data.footer_rows;
     adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(info->end_row_spin));
     gtk_adjustment_set_upper (adj, info->parse_data->parsed_lines.size());
     gtk_spin_button_set_value (GTK_SPIN_BUTTON(info->end_row_spin),
             info->settings_data.footer_rows);
 
     // Set Alternate rows
-    info->parse_data->skip_alt_lines = info->settings_data.skip_alt_rows;
     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->skip_rows), info->settings_data.skip_alt_rows);
 
     // Set Multi-split indicator
-    info->parse_data->multi_split = info->settings_data.multi_split;
     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->multi_split_cbutton), info->settings_data.multi_split);
 
     // Set Import Format
@@ -818,7 +814,9 @@ void csv_import_trans_multisplit_cb (GtkWidget *checkbox, gpointer user_data)
     CsvImportTrans *info = (CsvImportTrans*) user_data;
 
     /* Set the skip_alt_lines variable */
-    info->parse_data->multi_split = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(checkbox));
+    auto multi_split = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(checkbox));
+    info->parse_data->set_multi_split (multi_split);
+    gnc_csv_preview_update_assist (info);
 }
 
 
@@ -1289,7 +1287,7 @@ static void column_type_changed (GtkCellRenderer* renderer, gchar* path,
     g_object_get (renderer, "model", &model, "text-column", &textColumn, NULL);
     gtk_tree_model_get (model, new_text_iter,
             textColumn, &new_text,
-            1, &new_col_type,            // Invisible column in the combobox' model containing the colum type
+            1, &new_col_type,            // Invisible column in the combobox' model containing the column type
             -1);
 
     /* Get an iterator for the first (and only) row. */
@@ -1615,9 +1613,7 @@ bool get_list_of_accounts (CsvImportTrans* info, GtkTreeModel *store)
 }
 
 
-/* Loads the preview's data into its data treeview. not_empty is true
- * when the data treeview already contains data, false otherwise
- * (e.g. the first time this function is called on a preview).
+/* Loads the preview's data into its data treeview.
  *
  * @param info The data being previewed
  */
@@ -1717,11 +1713,17 @@ static void gnc_csv_preview_update_assist (CsvImportTrans* info)
     auto combostore = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
     for (auto col_type : gnc_csv_col_type_strs)
     {
-        GtkTreeIter iter;
-        gtk_list_store_append (combostore, &iter);
-        gtk_list_store_set (combostore, &iter, 0, _(col_type.second),
-                                               1, static_cast<int>(col_type.first),
-                                               -1);
+        /* Only add column types that make sense in
+         * the chosen import mode (multi-split vs two-split).
+         */
+        if (sanitize_trans_prop(col_type.first, info->parse_data->multi_split) == col_type.first)
+        {
+            GtkTreeIter iter;
+            gtk_list_store_append (combostore, &iter);
+            gtk_list_store_set (combostore, &iter, 0, _(col_type.second),
+                                                   1, static_cast<int>(col_type.first),
+                                                   -1);
+        }
     }
 
     /* Insert columns into the data and column type treeviews. */
diff --git a/src/import-export/csv-imp/gnc-csv-trans-settings.cpp b/src/import-export/csv-imp/gnc-csv-trans-settings.cpp
index 1c6f288..500a96e 100644
--- a/src/import-export/csv-imp/gnc-csv-trans-settings.cpp
+++ b/src/import-export/csv-imp/gnc-csv-trans-settings.cpp
@@ -57,6 +57,7 @@ const std::string no_settings{N_("No Settings")};
 #define CSV_COL_TYPES    "ColumnTypes"
 #define CSV_COL_WIDTHS   "ColumnWidths"
 
+G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT;
 
 /**************************************************
  * find
@@ -200,7 +201,16 @@ CsvTransSettings::load (const std::string& group)
         auto col_types_it = std::find_if (gnc_csv_col_type_strs.begin(),
                 gnc_csv_col_type_strs.end(), test_prop_type_str (col_types_str[i]));
         if (col_types_it != gnc_csv_col_type_strs.end())
-            column_types.push_back(col_types_it->first);
+        {
+            /* Found a valid column type. Now check whether it is allowed
+             * in the selected mode (two-split vs multi-split) */
+            auto prop = sanitize_trans_prop (col_types_it->first, multi_split);
+                column_types.push_back(prop);
+            if (prop != col_types_it->first)
+                PWARN("Found column type '%s', but this is blacklisted when multi-split mode is %s. "
+                        "Inserting column type 'NONE' instead'.",
+                        col_types_it->second, multi_split ? "enabled" : "disabled");
+        }
     }
     if (col_types_str)
         g_strfreev (col_types_str);
diff --git a/src/import-export/csv-imp/gnc-trans-props.cpp b/src/import-export/csv-imp/gnc-trans-props.cpp
index 801d9ff..6567cf9 100644
--- a/src/import-export/csv-imp/gnc-trans-props.cpp
+++ b/src/import-export/csv-imp/gnc-trans-props.cpp
@@ -70,6 +70,29 @@ std::map<GncTransPropType, const char*> gnc_csv_col_type_strs = {
         { GncTransPropType::TREC_DATE, N_("Transfer Reconcile Date") }
 };
 
+/* Below two vectors define which properties the user *can't* select
+ * in two-split or multi-split mode (mostly because they don't make
+ * sense in that context).
+ */
+std::vector<GncTransPropType> twosplit_blacklist = {
+        GncTransPropType::UNIQUE_ID };
+std::vector<GncTransPropType> multisplit_blacklist = {
+        GncTransPropType::TACTION,
+        GncTransPropType::TACCOUNT,
+        GncTransPropType::TMEMO,
+        GncTransPropType::TREC_STATE,
+        GncTransPropType::TREC_DATE
+};
+
+GncTransPropType sanitize_trans_prop (GncTransPropType prop, bool multi_split)
+{
+    auto bl = multi_split ? multisplit_blacklist : twosplit_blacklist;
+    if (std::find(bl.begin(), bl.end(), prop) == bl.end())
+        return prop;
+    else
+        return GncTransPropType::NONE;
+}
+
 /* Regular expressions used to parse dates per date format */
 const char* date_regex[] = {
                              "(?:"                                   // either y-m-d
diff --git a/src/import-export/csv-imp/gnc-trans-props.hpp b/src/import-export/csv-imp/gnc-trans-props.hpp
index bdc2655..45145a8 100644
--- a/src/import-export/csv-imp/gnc-trans-props.hpp
+++ b/src/import-export/csv-imp/gnc-trans-props.hpp
@@ -79,7 +79,6 @@ enum class GncTransPropType {
  *  changes to enum class GncTransPropType ! */
 extern std::map<GncTransPropType, const char*> gnc_csv_col_type_strs;
 
-
 /** Functor to check if the above map has an element of which
  *  the value equals name. To be used with std::find_if.
  */
@@ -94,6 +93,15 @@ private:
     const char *m_name;
 };
 
+/** Some properties only make sense in a multi-split context.
+ *  Inversely some also only make sense in a two-split context.
+ *  Below function will test a property against a given context
+ *  and will return that property if it makes sense
+ *  or GncTransPropType::NONE if not.
+ */
+GncTransPropType sanitize_trans_prop (GncTransPropType prop, bool multi_split);
+
+
 time64 parse_date (const std::string &date_str, int format);
 
 struct GncPreTrans
diff --git a/src/import-export/csv-imp/gnc-tx-import.cpp b/src/import-export/csv-imp/gnc-tx-import.cpp
index 7d8487c..d07bb5c 100644
--- a/src/import-export/csv-imp/gnc-tx-import.cpp
+++ b/src/import-export/csv-imp/gnc-tx-import.cpp
@@ -57,7 +57,6 @@ const gchar* currency_format_user[] = {N_("Locale"),
                                       };
 
 
-
 /** Constructor for GncTxImport.
  * @return Pointer to a new GncCSvParseData
  */
@@ -132,6 +131,24 @@ void GncTxImport::convert_encoding (const std::string& encoding)
         tokenizer->encoding(encoding);
 }
 
+/** Toggles the multi-split state of the importer and will subsequently
+ *  sanitize the column_types list. All types that don't make sense
+ *  in the new state are reset to type GncTransPropType::NONE.
+ * @param multi_split_val Boolean value with desired state (multi-split
+ * vs two-split).
+ */
+void GncTxImport::set_multi_split (bool multi_split_val)
+{
+    multi_split = multi_split_val;
+    for (auto col_it = column_types.begin(); col_it != column_types.end();
+            col_it++)
+    {
+        auto san_prop = sanitize_trans_prop (*col_it, multi_split);
+        if (san_prop != *col_it)
+            *col_it = san_prop;
+    }
+}
+
 /** Loads a file into a GncTxImport. This is the first function
  * that must be called after creating a new GncTxImport. As long as
  * this function didn't run successfully, the importer can't proceed.
diff --git a/src/import-export/csv-imp/gnc-tx-import.hpp b/src/import-export/csv-imp/gnc-tx-import.hpp
index c3e89e2..ab36c37 100644
--- a/src/import-export/csv-imp/gnc-tx-import.hpp
+++ b/src/import-export/csv-imp/gnc-tx-import.hpp
@@ -98,6 +98,8 @@ public:
     void load_file (const std::string& filename);
     void convert_encoding (const std::string& encoding);
 
+    void set_multi_split (bool multi_split_val);
+
     void tokenize (bool guessColTypes);
 
     /** This function will attempt to convert all tokenized lines into

commit 0b345d471e3a052600e0f6bf86ed0626d96597e9
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Tue Dec 13 10:09:01 2016 +0100

    Drop balance column from csv importer
    
    In its current state it will only work for a very restricted context.
    However there's no code to validate this context so the importer will
    happily produce wrong results in all other contexts.
    A query on the mailing list didn't return any interest in this feature
    so instead of fixing it I'd rather drop it to keep the code clean.

diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index 8a2721e..53e0828 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -1418,7 +1418,6 @@ bool preview_settings_valid (CsvImportTrans* info)
     int weight = 0;
     int oweight = 0;
     bool valid = true;
-    bool havebalance = false;
     /* ctstore contains the actual strings appearing in the column types treeview. */
     GtkTreeModel* ctstore = gtk_tree_view_get_model (info->ctreeview);
     /* datastore contains the actual strings appearing in the preview treeview. */
@@ -1459,10 +1458,6 @@ bool preview_settings_valid (CsvImportTrans* info)
         case GncTransPropType::DESCRIPTION:
             weight = weight + 100;
             break;
-
-        case GncTransPropType::BALANCE:
-            havebalance = true;
-            /* No break */
         case GncTransPropType::DEPOSIT:
         case GncTransPropType::WITHDRAWAL:
             weight = weight + 10;
@@ -1493,13 +1488,6 @@ bool preview_settings_valid (CsvImportTrans* info)
         g_free (prevstr);
     }
 
-    if (havebalance && (info->home_account_number > 1))
-    {
-        info->error_text = _("There are problems with the import settings!\nIf you have a Balance column "
-                             "and an Account column there must be only one account listed...");
-        return false;
-    }
-
     if ((oweight > 0) && (oweight < 99))
     {
         info->error_text = _("There are problems with the import settings!\nIf you have an Other Memo column "
diff --git a/src/import-export/csv-imp/gnc-trans-props.cpp b/src/import-export/csv-imp/gnc-trans-props.cpp
index 3f354b0..801d9ff 100644
--- a/src/import-export/csv-imp/gnc-trans-props.cpp
+++ b/src/import-export/csv-imp/gnc-trans-props.cpp
@@ -59,7 +59,6 @@ std::map<GncTransPropType, const char*> gnc_csv_col_type_strs = {
         { GncTransPropType::ACCOUNT, N_("Account") },
         { GncTransPropType::DEPOSIT, N_("Deposit") },
         { GncTransPropType::WITHDRAWAL, N_("Withdrawal") },
-        { GncTransPropType::BALANCE, N_("Balance") },
         { GncTransPropType::PRICE, N_("Price") },
         { GncTransPropType::MEMO, N_("Memo") },
         { GncTransPropType::REC_STATE, N_("Reconciled") },
@@ -434,9 +433,6 @@ void GncPreSplit::set_property (GncTransPropType prop_type, const std::string& v
                 m_tmemo = boost::none;
             break;
 
-        case GncTransPropType::BALANCE:
-            m_balance = parse_amount (value, m_currency_format); // Will throw if parsing fails
-            break;
         case GncTransPropType::DEPOSIT:
             m_deposit = parse_amount (value, m_currency_format); // Will throw if parsing fails
             break;
@@ -477,8 +473,8 @@ std::string GncPreSplit::verify_essentials (void)
 {
     auto err_msg = std::string();
     /* Make sure this split has the minimum required set of properties defined. */
-    if (!m_deposit && !m_withdrawal && !m_balance)
-        err_msg = _("No balance, deposit, or withdrawal column.");
+    if (!m_deposit && !m_withdrawal)
+        err_msg = _("No deposit or withdrawal column.");
 
     if (m_rec_state && *m_rec_state == YREC && !m_rec_date)
     {
@@ -563,10 +559,10 @@ static void trans_add_split (Transaction* trans, Account* account, gnc_numeric a
 
 }
 
-boost::optional<gnc_numeric> GncPreSplit::create_split (Transaction* trans)
+void GncPreSplit::create_split (Transaction* trans)
 {
     if (created)
-        return boost::none;
+        return;
 
     /* Gently refuse to create the split if the basics are not set correctly
      * This should have been tested before calling this function though!
@@ -574,8 +570,8 @@ boost::optional<gnc_numeric> GncPreSplit::create_split (Transaction* trans)
     auto check = verify_essentials();
     if (!check.empty())
     {
-        PWARN ("Refusing to create split because essentials not set properly: %s", check.c_str());
-        return boost::none;
+        PWARN ("Not creating split because essentials not set properly: %s", check.c_str());
+        return;
     }
 
     Account *account = nullptr;
@@ -618,11 +614,5 @@ boost::optional<gnc_numeric> GncPreSplit::create_split (Transaction* trans)
         trans_add_split (trans, taccount, gnc_numeric_neg(amount), m_taction, m_tmemo, m_trec_state, m_trec_date, inv_price);
     }
 
-
     created = true;
-
-    if (amount_set)
-        return boost::none;
-    else
-        return m_balance;
 }
diff --git a/src/import-export/csv-imp/gnc-trans-props.hpp b/src/import-export/csv-imp/gnc-trans-props.hpp
index 99d5f39..bdc2655 100644
--- a/src/import-export/csv-imp/gnc-trans-props.hpp
+++ b/src/import-export/csv-imp/gnc-trans-props.hpp
@@ -61,7 +61,6 @@ enum class GncTransPropType {
     ACCOUNT,
     DEPOSIT,
     WITHDRAWAL,
-    BALANCE,
     PRICE,
     MEMO,
     REC_STATE,
@@ -142,7 +141,7 @@ public:
         m_currency_format{currency_format}{};
     void set_property (GncTransPropType prop_type, const std::string& value);
     std::string verify_essentials (void);
-    boost::optional<gnc_numeric> create_split(Transaction* trans);
+    void create_split(Transaction* trans);
 
     Account* get_account () { if (m_account) return *m_account; else return nullptr; }
     void set_account (Account* acct) { if (acct) m_account = acct; else m_account = boost::none; }
@@ -154,7 +153,6 @@ private:
     boost::optional<Account*> m_account;
     boost::optional<gnc_numeric> m_deposit;
     boost::optional<gnc_numeric> m_withdrawal;
-    boost::optional<gnc_numeric> m_balance;
     boost::optional<gnc_numeric> m_price;
     boost::optional<std::string> m_memo;
     boost::optional<char> m_rec_state;
diff --git a/src/import-export/csv-imp/gnc-tx-import.cpp b/src/import-export/csv-imp/gnc-tx-import.cpp
index ae61112..7d8487c 100644
--- a/src/import-export/csv-imp/gnc-tx-import.cpp
+++ b/src/import-export/csv-imp/gnc-tx-import.cpp
@@ -206,7 +206,7 @@ void GncTxImport::tokenize (bool guessColTypes)
 /** Checks whether the parsed line contains all essential properties.
  * Essential properties are
  * - "Date"
- * - at least one of "Balance", "Deposit", or "Withdrawal"
+ * - at least one of "Deposit", or "Withdrawal"
  * - "Account"
  * Note account isn't checked for here as this has been done before
  * @param parsed_line The line we are checking
@@ -284,12 +284,7 @@ std::shared_ptr<DraftTransaction> GncTxImport::trans_properties_to_trans (std::v
     if (!trans)
         return nullptr;
 
-    auto balance = split_props->create_split(trans);
-    if (balance)
-    {
-        current_draft->balance_set = true;
-        current_draft->balance = *balance;
-    }
+    split_props->create_split(trans);
 
     /* Only return the draft transaction if we really created a new one
      * The return value will be added to a list for further processing,
@@ -298,56 +293,6 @@ std::shared_ptr<DraftTransaction> GncTxImport::trans_properties_to_trans (std::v
     return created_trans ? current_draft : nullptr;
 }
 
-void GncTxImport::adjust_balances (void)
-{
-    Split      *split, *tsplit;
-
-    /* balance_offset is how much the balance currently in the account
-     * differs from what it will be after the transactions are
-     * imported. This will be sum of all the previous transactions for
-     * any given transaction. */
-    auto balance_offset = gnc_numeric_zero();
-    for (auto trans_iter : transactions)
-    {
-        auto trans_line = trans_iter.second;
-        if (trans_line->balance_set)
-        {
-            time64 date = xaccTransGetDate (trans_line->trans);
-            /* Find what the balance should be by adding the offset to the actual balance. */
-            gnc_numeric existing_balance = gnc_numeric_add (balance_offset,
-                                           xaccAccountGetBalanceAsOfDate (base_account, date),
-                                           xaccAccountGetCommoditySCU (base_account),
-                                           GNC_HOW_RND_ROUND_HALF_UP);
-
-            /* The amount of the transaction is the difference between the new and existing balance. */
-            gnc_numeric amount = gnc_numeric_sub (trans_line->balance,
-                                                 existing_balance,
-                                                 xaccAccountGetCommoditySCU (base_account),
-                                                 GNC_HOW_RND_ROUND_HALF_UP);
-
-            // Find home account split
-            split  = xaccTransFindSplitByAccount (trans_line->trans, base_account);
-            xaccSplitSetAmount (split, amount);
-            xaccSplitSetValue (split, amount);
-
-            // If we have two splits, change other side
-            if (xaccTransCountSplits (trans_line->trans) == 2)
-            {
-                tsplit = xaccSplitGetOtherSplit (split);
-                xaccSplitSetAmount (split, amount);
-                xaccSplitSetValue (split, gnc_numeric_neg (amount));
-            }
-
-            /* This new transaction needs to be added to the balance offset. */
-            balance_offset = gnc_numeric_add (balance_offset,
-                                             amount,
-                                             xaccAccountGetCommoditySCU (base_account),
-                                             GNC_HOW_RND_ROUND_HALF_UP);
-        }
-    }
-
-}
-
 void GncTxImport::create_transaction (std::vector<parse_line_t>::iterator& parsed_line)
 {
     StrVec line;
@@ -443,9 +388,6 @@ void GncTxImport::create_transaction (std::vector<parse_line_t>::iterator& parse
         trans_properties_verify_essentials (parsed_line);
 
         /* If all went well, add this transaction to the list. */
-        /* We want to keep the transactions sorted by date in case we have
-         * to calculate the transaction's amount based on the user provided balances.
-         * The multimap should deal with this for us. */
         auto draft_trans = trans_properties_to_trans (parsed_line);
         if (draft_trans)
         {
@@ -522,10 +464,6 @@ void GncTxImport::create_transactions (Account* account,
             continue;
         }
     }
-
-    if (std::find(column_types.begin(),column_types.end(), GncTransPropType::BALANCE) !=
-        column_types.end()) // This is only used if we have one home account
-        adjust_balances ();
 }
 
 

commit 847b140b34bd5a4f613eb8edc7537ebb2bb20db3
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sun Dec 11 23:15:34 2016 +0100

    Adapt csv export format to new csv importer capabilities

diff --git a/src/import-export/csv-exp/csv-transactions-export.c b/src/import-export/csv-exp/csv-transactions-export.c
index d7ca029..038b929 100644
--- a/src/import-export/csv-exp/csv-transactions-export.c
+++ b/src/import-export/csv-exp/csv-transactions-export.c
@@ -51,10 +51,6 @@ static QofLogModule log_module = GNC_MOD_ASSISTANT;
 #endif
 
 
-enum GncCsvLineType {TRANS_SIMPLE,
-                     TRANS_COMPLEX,
-                     SPLIT_LINE};
-
 /*******************************************************************/
 
 /*******************************************************
@@ -99,7 +95,7 @@ gchar *csv_txn_test_field_string (CsvExportInfo *info, const gchar *string_in)
     g_strfreev (parts);
 
     /* Check for separator string and \n and " in field,
-       if so quote field if not allready quoted */
+       if so quote field if not already quoted */
     if (g_strrstr (string_parts, info->separator_str) != NULL)
         need_quote = TRUE;
     if (g_strrstr (string_parts, "\n") != NULL)
@@ -118,86 +114,45 @@ gchar *csv_txn_test_field_string (CsvExportInfo *info, const gchar *string_in)
 
 /******************** Helper functions *********************/
 
-// Transaction line starts with Date
+// Transaction Date
 static gchar*
-begin_trans_string (Transaction *trans, CsvExportInfo *info)
+add_date (gchar *so_far, Transaction *trans, CsvExportInfo *info)
 {
     gchar *date = qof_print_date (xaccTransGetDate (trans));
-    gchar *result = g_strconcat (info->end_sep, date, info->mid_sep, NULL);
+    gchar *result = g_strconcat (so_far, info->end_sep, date, info->mid_sep, NULL);
     g_free (date);
+    g_free (so_far);
     return result;
 }
 
 
-// Split line start
-static gchar*
-begin_split_string (Transaction *trans, Split *split, gboolean t_void, CsvExportInfo *info)
-{
-    const gchar *str_rec_date;
-    const gchar *start;
-    gchar       *conv;
-    gchar       *result;
-    Timespec     ts = {0,0};
-
-    if (xaccSplitGetReconcile (split) == YREC)
-    {
-        xaccSplitGetDateReconciledTS (split, &ts);
-        str_rec_date = gnc_print_date (ts);
-    }
-    else
-        str_rec_date = "";
-
-    if (t_void)
-    {
-        start = xaccTransGetVoidReason (trans) ? xaccTransGetVoidReason (trans) : "" ;
-        conv = csv_txn_test_field_string (info, start);
-        result = g_strconcat (info->end_sep, info->mid_sep, info->mid_sep, str_rec_date,
-                              info->mid_sep, info->mid_sep, info->mid_sep, info->mid_sep, conv, info->mid_sep, NULL);
-        g_free (conv);
-    }
-    else
-         result = g_strconcat (info->end_sep, info->mid_sep, info->mid_sep, str_rec_date,
-                               info->mid_sep, info->mid_sep, info->mid_sep, info->mid_sep, info->mid_sep, NULL);
-
-    return result;
-}
-
-
-// Transaction Type
+// Transaction GUID
 static gchar*
-add_type (gchar *so_far, Transaction *trans, CsvExportInfo *info)
+add_guid (gchar *so_far, Transaction *trans, CsvExportInfo *info)
 {
-    gchar       *result;
-    char         type;
-    static char  ss[2];
-
-    type = xaccTransGetTxnType (trans);
+    gchar *result;
+    gchar *guid;
 
-    if (type == TXN_TYPE_NONE)
-        type = ' ';
-    ss[0] = type;
-    ss[1] = '\0';
-    result = g_strconcat (so_far, ss, info->mid_sep, NULL);
+    guid = guid_to_string (xaccTransGetGUID (trans));
+    result = g_strconcat (so_far, guid, info->mid_sep, NULL);
+    g_free (guid);
     g_free (so_far);
     return result;
 }
 
-// Second Date
+// Reconcile Date
 static gchar*
-add_second_date (gchar *so_far, Transaction *trans, CsvExportInfo *info)
+add_reconcile_date (gchar *so_far, Split *split, CsvExportInfo *info)
 {
-    gchar       *result;
-    const gchar *second_date;
-    char         type;
-    Timespec     ts = {0,0};
-
-    type = xaccTransGetTxnType (trans);
+    gchar *result;
 
-    if (type == TXN_TYPE_INVOICE)
+    if (xaccSplitGetReconcile (split) == YREC)
     {
-        xaccTransGetDateDueTS (trans, &ts);
-        second_date = gnc_print_date (ts);
-        result = g_strconcat (so_far, second_date, info->mid_sep, NULL);
+        Timespec     ts = {0,0};
+        const gchar *str_rec_date;
+        xaccSplitGetDateReconciledTS (split, &ts);
+        str_rec_date = gnc_print_date (ts);
+        result = g_strconcat (so_far, str_rec_date, info->mid_sep, NULL);
     }
     else
         result = g_strconcat (so_far, info->mid_sep, NULL);
@@ -208,30 +163,17 @@ add_second_date (gchar *so_far, Transaction *trans, CsvExportInfo *info)
 
 // Account Name short or Long
 static gchar*
-add_account_name (gchar *so_far, Account *acc, Split *split, gboolean full, CsvExportInfo *info)
+add_account_name (gchar *so_far, Split *split, gboolean full, CsvExportInfo *info)
 {
     gchar       *name = NULL;
     gchar       *conv;
     gchar       *result;
-    Account     *account = NULL;
 
-    if (split == NULL)
-    {
-        if (acc == NULL)
-            name = g_strdup (" ");
-        else
-            account = acc;
-    }
+    Account     *account = xaccSplitGetAccount (split);
+    if (full)
+        name = gnc_account_get_full_name (account);
     else
-        account = xaccSplitGetAccount (split);
-
-    if (account != NULL)
-    {
-        if (full)
-            name = gnc_account_get_full_name (account);
-        else
-            name = g_strdup (xaccAccountGetName (account));
-    }
+        name = g_strdup (xaccAccountGetName (account));
     conv = csv_txn_test_field_string (info, name);
     result = g_strconcat (so_far, conv, info->mid_sep, NULL);
     g_free (name);
@@ -288,6 +230,26 @@ add_notes (gchar *so_far, Transaction *trans, CsvExportInfo *info)
     return result;
 }
 
+// Void reason
+static gchar*
+add_void_reason (gchar *so_far, Transaction *trans, CsvExportInfo *info)
+{
+    gchar       *result;
+
+    if (xaccTransGetVoidStatus (trans))
+    {
+        const gchar *void_reason = xaccTransGetVoidReason (trans);
+        gchar *conv = csv_txn_test_field_string (info, void_reason);
+        result = g_strconcat (so_far, conv, info->mid_sep, NULL);
+        g_free (conv);
+    }
+    else
+        result = g_strconcat (so_far, info->mid_sep, NULL);
+
+    g_free (so_far);
+    return result;
+}
+
 // Memo
 static gchar*
 add_memo (gchar *so_far, Split *split, CsvExportInfo *info)
@@ -325,38 +287,14 @@ add_category (gchar *so_far, Split *split, gboolean full, CsvExportInfo *info)
     return result;
 }
 
-// Line Type
-static gchar*
-add_line_type (gchar *so_far, gint line_type, CsvExportInfo *info)
-{
-    gchar *result;
-
-    if (line_type == SPLIT_LINE)
-        result = g_strconcat (so_far, "S", info->mid_sep, NULL);
-    else
-        result = g_strconcat (so_far, "T", info->mid_sep, NULL);
-
-    g_free (so_far);
-    return result;
-}
-
 // Action
 static gchar*
-add_action (gchar *so_far, Split *split, gint line_type, CsvExportInfo *info)
+add_action (gchar *so_far, Split *split, CsvExportInfo *info)
 {
-    const gchar *action;
-    gchar       *conv;
-    gchar       *result;
-
-    if ((line_type == TRANS_COMPLEX)||(line_type == TRANS_SIMPLE))
-        result = g_strconcat (so_far, "", info->mid_sep, NULL);
-    else
-    {
-        action = xaccSplitGetAction (split);
-        conv = csv_txn_test_field_string (info, action);
-        result = g_strconcat (so_far, conv, info->mid_sep, NULL);
-        g_free (conv);
-    }
+    const gchar *action = xaccSplitGetAction (split);
+    gchar *conv = csv_txn_test_field_string (info, action);
+    gchar *result = g_strconcat (so_far, conv, info->mid_sep, NULL);
+    g_free (conv);
     g_free (so_far);
     return result;
 }
@@ -377,18 +315,15 @@ add_reconcile (gchar *so_far, Split *split, CsvExportInfo *info)
     return result;
 }
 
-// Commodity Mnemonic
+// Transaction commodity
 static gchar*
-add_comm_mnemonic (gchar *so_far, Transaction *trans, Split *split, CsvExportInfo *info)
+add_commodity (gchar *so_far, Transaction *trans, CsvExportInfo *info)
 {
     const gchar *comm_m;
     gchar       *conv;
     gchar       *result;
 
-    if (split == NULL)
-        comm_m = gnc_commodity_get_mnemonic (xaccTransGetCurrency (trans));
-    else
-        comm_m = gnc_commodity_get_mnemonic (xaccAccountGetCommodity (xaccSplitGetAccount(split)));
+    comm_m = gnc_commodity_get_unique_name (xaccTransGetCurrency (trans));
 
     conv = csv_txn_test_field_string (info, comm_m);
     result = g_strconcat (so_far, conv, info->mid_sep, NULL);
@@ -397,56 +332,21 @@ add_comm_mnemonic (gchar *so_far, Transaction *trans, Split *split, CsvExportInf
     return result;
 }
 
-// Commodity Namespace
-static gchar*
-add_comm_namespace (gchar *so_far, Transaction *trans, Split *split, CsvExportInfo *info)
-{
-    const gchar *comm_n;
-    gchar       *conv;
-    gchar       *result;
-
-    if (split == NULL)
-        comm_n = gnc_commodity_get_namespace (xaccTransGetCurrency (trans));
-    else
-        comm_n = gnc_commodity_get_namespace (xaccAccountGetCommodity (xaccSplitGetAccount(split)));
-
-    conv = csv_txn_test_field_string (info, comm_n);
-    result = g_strconcat (so_far, conv, info->mid_sep, NULL);
-    g_free (conv);
-    g_free (so_far);
-    return result;
-}
-
 // Amount with Symbol or not
 static gchar*
-add_amount (gchar *so_far, Split *split, gboolean t_void, gboolean symbol, gint line_type, CsvExportInfo *info)
+add_amount (gchar *so_far, Split *split, gboolean t_void, gboolean symbol, CsvExportInfo *info)
 {
     const gchar *amt;
     gchar       *conv;
     gchar       *result;
 
-    if (line_type == TRANS_COMPLEX)
-        result = g_strconcat (so_far, "", info->mid_sep, NULL);
+    if (t_void)
+        amt = xaccPrintAmount (xaccSplitVoidFormerAmount (split), gnc_split_amount_print_info (split, symbol));
     else
-    {
-        if (symbol)
-        {
-            if (t_void)
-                amt = xaccPrintAmount (gnc_numeric_zero(), gnc_split_amount_print_info (split, TRUE));
-            else
-                amt = xaccPrintAmount (xaccSplitGetAmount (split), gnc_split_amount_print_info (split, TRUE));
-        }
-        else
-        {
-            if (t_void)
-                amt = xaccPrintAmount (xaccSplitVoidFormerAmount (split), gnc_split_amount_print_info (split, FALSE));
-            else
-                amt = xaccPrintAmount (xaccSplitGetAmount (split), gnc_split_amount_print_info (split, FALSE));
-        }
-        conv = csv_txn_test_field_string (info, amt);
-        result = g_strconcat (so_far, conv, info->mid_sep, NULL);
-        g_free (conv);
-    }
+        amt = xaccPrintAmount (xaccSplitGetAmount (split), gnc_split_amount_print_info (split, symbol));
+    conv = csv_txn_test_field_string (info, amt);
+    result = g_strconcat (so_far, conv, info->mid_sep, NULL);
+    g_free (conv);
     g_free (so_far);
     return result;
 }
@@ -494,16 +394,6 @@ add_price (gchar *so_far, Split *split, gboolean t_void, CsvExportInfo *info)
     return result;
 }
 
-// Transaction End of Line
-static gchar*
-add_trans_eol (gchar *so_far, CsvExportInfo *info)
-{
-    gchar *result = g_strconcat (so_far, "", info->mid_sep, "", info->end_sep, EOLSTR, NULL);
-
-    g_free (so_far);
-    return result;
-}
-
 /******************************************************************************/
 
 static gchar*
@@ -511,61 +401,57 @@ make_simple_trans_line (Account *acc, Transaction *trans, Split *split, CsvExpor
 {
     gboolean t_void = xaccTransGetVoidStatus (trans);
 
-    gchar *result = begin_trans_string (trans, info);
-    result = add_account_name (result, acc, NULL, TRUE, info);
-    result = add_number (result, trans, info);
-    result = add_description (result, trans, info);
-    result = add_category (result, split, TRUE, info);
-    result = add_reconcile (result, split, info);
-    result = add_amount (result, split, t_void, TRUE, TRANS_SIMPLE, info);
-    result = add_amount (result, split, t_void, FALSE, TRANS_SIMPLE, info);
-    result = add_rate (result, split, t_void, info);
-    return result;
+    gchar *exp_line = g_strdup("");
+    exp_line = add_date (exp_line, trans, info);
+    exp_line = add_account_name (exp_line, split, TRUE, info);
+    exp_line = add_number (exp_line, trans, info);
+    exp_line = add_description (exp_line, trans, info);
+    exp_line = add_category (exp_line, split, TRUE, info);
+    exp_line = add_reconcile (exp_line, split, info);
+    exp_line = add_amount (exp_line, split, t_void, TRUE, info);
+    exp_line = add_amount (exp_line, split, t_void, FALSE, info);
+    exp_line = add_rate (exp_line, split, t_void, info);
+    return exp_line;
 }
 
 static gchar*
-make_complex_trans_line (Account *acc, Transaction *trans, Split *split, CsvExportInfo *info)
+make_split_part (gchar* exp_line, Split *split, gboolean t_void, CsvExportInfo *info)
 {
-    gboolean t_void = xaccTransGetVoidStatus (trans);
+    exp_line = add_action (exp_line, split, info);
+    exp_line = add_memo (exp_line, split, info);
+    exp_line = add_account_name (exp_line, split, TRUE, info);
+    exp_line = add_account_name (exp_line, split, FALSE, info);
+    exp_line = add_amount (exp_line, split, t_void, TRUE, info);
+    exp_line = add_amount (exp_line, split, t_void, FALSE, info);
+    exp_line = add_reconcile (exp_line, split, info);
+    exp_line = add_reconcile_date (exp_line, split, info);
+    exp_line = add_price (exp_line, split, t_void, info);
+    return exp_line;
+}
 
-    gchar *result = begin_trans_string (trans, info);
-    result = add_type (result, trans, info);
-    result = add_second_date (result, trans, info);
-    result = add_account_name (result, acc, NULL, FALSE, info);
-    result = add_number (result, trans, info);
-    result = add_description (result, trans, info);
-    result = add_notes (result, trans, info);
-    result = add_memo (result, split, info);
-    result = add_category (result, split, TRUE, info);
-    result = add_category (result, split, FALSE, info);
-    result = add_line_type (result, TRANS_COMPLEX, info);
-    result = add_action (result,split, TRANS_COMPLEX, info);
-    result = add_reconcile (result, split, info);
-    result = add_amount (result, split, t_void, TRUE, TRANS_COMPLEX, info);
-    result = add_comm_mnemonic (result, trans, NULL, info);
-    result = add_comm_namespace (result, trans, NULL, info);
-    result = add_trans_eol (result, info);
-    return result;
+static gchar*
+make_complex_trans_line (Account *acc, Transaction *trans, Split *split, CsvExportInfo *info)
+{
+    gchar *exp_line = g_strdup("");
+    exp_line = add_date (exp_line, trans, info);
+    exp_line = add_guid (exp_line, trans, info);
+    exp_line = add_number (exp_line, trans, info);
+    exp_line = add_description (exp_line, trans, info);
+    exp_line = add_notes (exp_line, trans, info);
+    exp_line = add_commodity (exp_line, trans, info);
+    exp_line = add_void_reason (exp_line, trans, info);
+    return make_split_part (exp_line, split, xaccTransGetVoidStatus (trans), info);
 }
 
 static gchar*
 make_complex_split_line (Transaction *trans, Split *split, CsvExportInfo *info)
 {
-    gboolean t_void = xaccTransGetVoidStatus (trans);
-
-    gchar *result = begin_split_string (trans, split, t_void, info);
-    result = add_memo (result, split, info);
-    result = add_account_name (result, NULL, split, TRUE, info);
-    result = add_account_name (result, NULL, split, FALSE, info);
-    result = add_line_type (result, SPLIT_LINE, info);
-    result = add_action (result,split, SPLIT_LINE, info);
-    result = add_reconcile (result, split, info);
-    result = add_amount (result, split, t_void, TRUE, SPLIT_LINE, info);
-    result = add_comm_mnemonic (result, trans, split, info);
-    result = add_comm_namespace (result, trans, split, info);
-    result = add_amount (result, split, t_void, FALSE, SPLIT_LINE, info);
-    result = add_price (result, split, t_void, info);
-    return result;
+    /* Pure split lines don't have any transaction information,
+     * so start with empty fields for all transaction columns.
+     */
+    gchar *result = g_strconcat (info->end_sep, info->mid_sep, info->mid_sep, info->mid_sep,
+            info->mid_sep, info->mid_sep, info->mid_sep, info->mid_sep, NULL);
+    return make_split_part (result, split, xaccTransGetVoidStatus (trans), info);
 }
 
 
@@ -657,13 +543,17 @@ void account_splits (CsvExportInfo *info, Account *acc, FILE *fh )
         {
             t_split = node->data;
 
+            // base split is already written on the trans_line
+            if (split != t_split)
+            {
             // Complex Split Line.
-            line = make_complex_split_line (trans, t_split, info);
+                line = make_complex_split_line (trans, t_split, info);
 
-            if (!write_line_to_file (fh, line))
-                info->failed = TRUE;
+                if (!write_line_to_file (fh, line))
+                    info->failed = TRUE;
 
-            g_free (line);
+                g_free (line);
+            }
 
             cnt++;
             node = node->next;
@@ -724,14 +614,14 @@ void csv_transactions_export (CsvExportInfo *info)
         }
         else
         {
-            header = g_strconcat (info->end_sep, _("Date"), info->mid_sep, _("Transaction Type"), info->mid_sep, _("Second Date"),
-                                  info->mid_sep, _("Account Name"), info->mid_sep, (num_action ? _("Transaction Number") : _("Number")),
-                                  info->mid_sep, _("Description"), info->mid_sep, _("Notes"), info->mid_sep, _("Memo"),
-                                  info->mid_sep, _("Full Category Path"), info->mid_sep, _("Category"), info->mid_sep, _("Row Type"),
-                                  info->mid_sep, (num_action ? _("Number/Action") : _("Action")),
-                                  info->mid_sep, _("Reconcile"), info->mid_sep, _("Amount With Sym"),
-                                  info->mid_sep, _("Commodity Mnemonic"), info->mid_sep, _("Commodity Namespace"),
-                                  info->mid_sep, _("Amount Num."), info->mid_sep, _("Rate/Price"),
+            header = g_strconcat (info->end_sep, _("Date"), info->mid_sep, _("Transaction ID"),
+                                  info->mid_sep, (num_action ? _("Transaction Number") : _("Number")),
+                                  info->mid_sep, _("Description"), info->mid_sep, _("Notes"),
+                                  info->mid_sep, _("Commodity/Currency"), info->mid_sep, _("Void Reason"),
+                                  info->mid_sep, (num_action ? _("Number/Action") : _("Action")), info->mid_sep, _("Memo"),
+                                  info->mid_sep, _("Full Account Name"), info->mid_sep, _("Account Name"),
+                                  info->mid_sep, _("Amount With Sym"), info->mid_sep, _("Amount Num."),
+                                  info->mid_sep, _("Reconcile"), info->mid_sep, _("Reconcile Date"), info->mid_sep, _("Rate/Price"),
                                   info->end_sep, EOLSTR, NULL);
         }
         DEBUG("Header String: %s", header);

commit 35ba4ec92fab052e72e08c86dad0a3d0b2fe2335
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sun Dec 11 19:16:33 2016 +0100

    Extend csv importer to be able to import multi-currency and stock transactions

diff --git a/src/import-export/csv-imp/gnc-trans-props.cpp b/src/import-export/csv-imp/gnc-trans-props.cpp
index 8eb6895..3f354b0 100644
--- a/src/import-export/csv-imp/gnc-trans-props.cpp
+++ b/src/import-export/csv-imp/gnc-trans-props.cpp
@@ -26,6 +26,7 @@ extern "C" {
 #include <windows.h>
 #endif
 
+#include <glib.h>
 #include <glib/gi18n.h>
 
 #include "engine-helpers.h"
@@ -33,6 +34,7 @@ extern "C" {
 #include "gnc-ui-util.h"
 #include "Account.h"
 #include "Transaction.h"
+#include "gnc-pricedb.h"
 
 }
 
@@ -51,12 +53,14 @@ std::map<GncTransPropType, const char*> gnc_csv_col_type_strs = {
         { GncTransPropType::NUM, N_("Num") },
         { GncTransPropType::DESCRIPTION, N_("Description") },
         { GncTransPropType::NOTES, N_("Notes") },
+        { GncTransPropType::COMMODITY, N_("Transaction Commodity") },
         { GncTransPropType::VOID_REASON, N_("Void Reason") },
         { GncTransPropType::ACTION, N_("Action") },
         { GncTransPropType::ACCOUNT, N_("Account") },
         { GncTransPropType::DEPOSIT, N_("Deposit") },
         { GncTransPropType::WITHDRAWAL, N_("Withdrawal") },
         { GncTransPropType::BALANCE, N_("Balance") },
+        { GncTransPropType::PRICE, N_("Price") },
         { GncTransPropType::MEMO, N_("Memo") },
         { GncTransPropType::REC_STATE, N_("Reconciled") },
         { GncTransPropType::REC_DATE, N_("Reconcile Date") },
@@ -229,10 +233,57 @@ static char parse_reconciled (const std::string& reconcile)
         throw std::invalid_argument ("String can't be parsed into a valid reconcile state.");
 }
 
+static gnc_commodity* parse_commodity (const std::string& comm_str)
+{
+    if (comm_str.empty())
+        return nullptr;
+
+    auto table = gnc_commodity_table_get_table (gnc_get_current_book());
+    gnc_commodity* comm = nullptr;
+
+    /* First try commodity as a unique name. */
+    if (comm_str.find("::"))
+        comm = gnc_commodity_table_lookup_unique (table, comm_str.c_str());
+
+    /* Then try mnemonic in the currency namespace */
+    if (!comm)
+        comm = gnc_commodity_table_lookup (table,
+                GNC_COMMODITY_NS_CURRENCY, comm_str.c_str());
+
+    if (!comm)
+    {
+        /* If that fails try mnemonic in all other namespaces */
+        auto namespaces = gnc_commodity_table_get_namespaces(table);
+        for (auto ns = namespaces; ns; ns = ns->next)
+        {
+            gchar* ns_str = (gchar*)ns->data;
+            if (g_utf8_collate(ns_str, GNC_COMMODITY_NS_CURRENCY) == 0)
+                continue;
+
+            comm = gnc_commodity_table_lookup (table,
+                    ns_str, comm_str.c_str());
+            if (comm)
+                break;
+        }
+    }
+
+    if (!comm)
+        throw std::invalid_argument ("String can't be parsed into a valid commodity.");
+    else
+        return comm;
+}
+
 void GncPreTrans::set_property (GncTransPropType prop_type, const std::string& value)
 {
     switch (prop_type)
     {
+        case GncTransPropType::UNIQUE_ID:
+            if (!value.empty())
+                m_differ = value;
+            else
+                m_differ = boost::none;
+            break;
+
         case GncTransPropType::DATE:
             m_date = parse_date (value, m_date_format); // Throws if parsing fails
             break;
@@ -258,11 +309,8 @@ void GncPreTrans::set_property (GncTransPropType prop_type, const std::string& v
                 m_notes = boost::none;
             break;
 
-        case GncTransPropType::UNIQUE_ID:
-            if (!value.empty())
-                m_differ = value;
-            else
-                m_differ = boost::none;
+        case GncTransPropType::COMMODITY:
+            m_commodity = parse_commodity (value); // Throws if parsing fails
             break;
 
         case GncTransPropType::VOID_REASON:
@@ -306,8 +354,7 @@ Transaction* GncPreTrans::create_trans (QofBook* book, gnc_commodity* currency)
 
     auto trans = xaccMallocTransaction (book);
     xaccTransBeginEdit (trans);
-    xaccTransSetCurrency (trans, currency);
-
+    xaccTransSetCurrency (trans, m_commodity ? *m_commodity : currency);
     xaccTransSetDatePostedSecsNormalized (trans, *m_date);
 
     if (m_num)
@@ -319,6 +366,7 @@ Transaction* GncPreTrans::create_trans (QofBook* book, gnc_commodity* currency)
     if (m_notes)
         xaccTransSetNotes (trans, m_notes->c_str());
 
+
     created = true;
     return trans;
 }
@@ -328,10 +376,13 @@ bool GncPreTrans::is_part_of (std::shared_ptr<GncPreTrans> parent)
     if (!parent)
         return false;
 
-    return (!m_date || m_date == parent->m_date) &&
+    return (!m_differ || m_differ == parent->m_differ) &&
+            (!m_date || m_date == parent->m_date) &&
+            (!m_num || m_num == parent->m_num) &&
             (!m_desc || m_desc == parent->m_desc) &&
             (!m_notes || m_notes == parent->m_notes) &&
-            (!m_differ || m_differ == parent->m_differ);
+            (!m_commodity || m_commodity == parent->m_commodity) &&
+            (!m_void_reason || m_void_reason == parent->m_void_reason);
 }
 
 void GncPreSplit::set_property (GncTransPropType prop_type, const std::string& value)
@@ -393,6 +444,10 @@ void GncPreSplit::set_property (GncTransPropType prop_type, const std::string& v
             m_withdrawal = parse_amount (value, m_currency_format); // Will throw if parsing fails
             break;
 
+        case GncTransPropType::PRICE:
+            m_price = parse_amount (value, m_currency_format); // Will throw if parsing fails
+            break;
+
         case GncTransPropType::REC_STATE:
             m_rec_state = parse_reconciled (value); // Throws if parsing fails
             break;
@@ -452,14 +507,48 @@ static void trans_add_split (Transaction* trans, Account* account, gnc_numeric a
                             const boost::optional<std::string>& action,
                             const boost::optional<std::string>& memo,
                             const boost::optional<char>& rec_state,
-                            const boost::optional<time64> rec_date)
+                            const boost::optional<time64> rec_date,
+                            const boost::optional<gnc_numeric> price)
 {
-    auto book = xaccTransGetBook (trans);
+    QofBook* book = xaccTransGetBook (trans);
     auto split = xaccMallocSplit (book);
     xaccSplitSetAccount (split, account);
     xaccSplitSetParent (split, trans);
     xaccSplitSetAmount (split, amount);
-    xaccSplitSetValue (split, amount);
+    auto trans_curr = xaccTransGetCurrency(trans);
+    auto acct_comm = xaccAccountGetCommodity(account);
+    if (gnc_commodity_equiv(trans_curr, acct_comm))
+        xaccSplitSetValue (split, amount);
+    else if (price)
+    {
+        gnc_numeric value = gnc_numeric_mul (amount, *price, GNC_DENOM_AUTO, GNC_HOW_RND_ROUND);
+        xaccSplitSetValue (split, value);
+    }
+    else
+    {
+        auto tts = xaccTransRetDatePostedTS (trans);
+        /* Import data didn't specify price, let's lookup the nearest in time */
+        auto nprice = gnc_pricedb_lookup_nearest_in_time(gnc_pricedb_get_db(book),
+                acct_comm, trans_curr, tts);
+        if (!nprice)
+        {
+            PWARN("No price found, using a price of 1.");
+            xaccSplitSetValue (split, amount);
+        }
+        else
+        {
+            /* Found a usable price. Let's check if the conversion direction is right */
+            gnc_numeric rate = {0, 1};
+            if (gnc_commodity_equiv(gnc_price_get_currency(nprice), trans_curr))
+                rate = gnc_price_get_value(nprice);
+            else
+                rate = gnc_numeric_invert(gnc_price_get_value(nprice));
+
+            gnc_numeric value = gnc_numeric_mul (amount, rate, GNC_DENOM_AUTO, GNC_HOW_RND_ROUND);
+            xaccSplitSetValue (split, value);
+        }
+    }
+
     if (memo)
         xaccSplitSetMemo (split, memo->c_str());
     /* Note, this function assumes the num/action switch is done at a higher level
@@ -516,13 +605,18 @@ boost::optional<gnc_numeric> GncPreSplit::create_split (Transaction* trans)
                 GNC_HOW_RND_ROUND_HALF_UP);
 
     /* Add a split with the cumulative amount value. */
-    trans_add_split (trans, account, amount, m_action, m_memo, m_rec_state, m_rec_date);
+    trans_add_split (trans, account, amount, m_action, m_memo, m_rec_state, m_rec_date, m_price);
 
     if (taccount)
+    {
         /* Note: the current importer assumes at most 2 splits. This means the second split amount
          * will be the negative of the the first split amount.
          */
-        trans_add_split (trans, taccount, gnc_numeric_neg(amount), m_taction, m_tmemo, m_trec_state, m_trec_date);
+        auto inv_price = m_price;
+        if (inv_price)
+            inv_price = gnc_numeric_invert(*inv_price);
+        trans_add_split (trans, taccount, gnc_numeric_neg(amount), m_taction, m_tmemo, m_trec_state, m_trec_date, inv_price);
+    }
 
 
     created = true;
diff --git a/src/import-export/csv-imp/gnc-trans-props.hpp b/src/import-export/csv-imp/gnc-trans-props.hpp
index 169470a..99d5f39 100644
--- a/src/import-export/csv-imp/gnc-trans-props.hpp
+++ b/src/import-export/csv-imp/gnc-trans-props.hpp
@@ -34,6 +34,7 @@ extern "C" {
 
 #include "Account.h"
 #include "Transaction.h"
+#include "gnc-commodity.h"
 }
 
 #include <string>
@@ -52,6 +53,7 @@ enum class GncTransPropType {
     NUM,
     DESCRIPTION,
     NOTES,
+    COMMODITY,
     VOID_REASON,
     TRANS_PROPS = VOID_REASON,
 
@@ -60,6 +62,7 @@ enum class GncTransPropType {
     DEPOSIT,
     WITHDRAWAL,
     BALANCE,
+    PRICE,
     MEMO,
     REC_STATE,
     REC_DATE,
@@ -127,6 +130,7 @@ private:
     boost::optional<std::string> m_num;
     boost::optional<std::string> m_desc;
     boost::optional<std::string> m_notes;
+    boost::optional<gnc_commodity*> m_commodity;
     boost::optional<std::string> m_void_reason;
     bool created = false;
 };
@@ -151,6 +155,7 @@ private:
     boost::optional<gnc_numeric> m_deposit;
     boost::optional<gnc_numeric> m_withdrawal;
     boost::optional<gnc_numeric> m_balance;
+    boost::optional<gnc_numeric> m_price;
     boost::optional<std::string> m_memo;
     boost::optional<char> m_rec_state;
     boost::optional<time64> m_rec_date;

commit 49bbbca1d514b32b8bd493a203e753b14a8ee7d7
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sun Dec 11 16:54:17 2016 +0100

    Extend csv importer to be able to import voided transactions

diff --git a/src/import-export/csv-imp/gnc-trans-props.cpp b/src/import-export/csv-imp/gnc-trans-props.cpp
index 06e6f71..8eb6895 100644
--- a/src/import-export/csv-imp/gnc-trans-props.cpp
+++ b/src/import-export/csv-imp/gnc-trans-props.cpp
@@ -46,11 +46,12 @@ G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT;
 /* This map contains a set of strings representing the different column types. */
 std::map<GncTransPropType, const char*> gnc_csv_col_type_strs = {
         { GncTransPropType::NONE, N_("None") },
+        { GncTransPropType::UNIQUE_ID, N_("Transaction ID") },
         { GncTransPropType::DATE, N_("Date") },
         { GncTransPropType::NUM, N_("Num") },
         { GncTransPropType::DESCRIPTION, N_("Description") },
-        { GncTransPropType::UNIQUE_ID, N_("Transaction ID") },
         { GncTransPropType::NOTES, N_("Notes") },
+        { GncTransPropType::VOID_REASON, N_("Void Reason") },
         { GncTransPropType::ACTION, N_("Action") },
         { GncTransPropType::ACCOUNT, N_("Account") },
         { GncTransPropType::DEPOSIT, N_("Deposit") },
@@ -264,6 +265,13 @@ void GncPreTrans::set_property (GncTransPropType prop_type, const std::string& v
                 m_differ = boost::none;
             break;
 
+        case GncTransPropType::VOID_REASON:
+            if (!value.empty())
+                m_void_reason = value;
+            else
+                m_void_reason = boost::none;
+            break;
+
         default:
             /* Issue a warning for all other prop_types. */
             PWARN ("%d is an invalid property for a transaction", static_cast<int>(prop_type));
diff --git a/src/import-export/csv-imp/gnc-trans-props.hpp b/src/import-export/csv-imp/gnc-trans-props.hpp
index e77a9b7..169470a 100644
--- a/src/import-export/csv-imp/gnc-trans-props.hpp
+++ b/src/import-export/csv-imp/gnc-trans-props.hpp
@@ -47,12 +47,13 @@ extern "C" {
  * type. */
 enum class GncTransPropType {
     NONE,
+    UNIQUE_ID,
     DATE,
     NUM,
     DESCRIPTION,
     NOTES,
-    UNIQUE_ID,
-    TRANS_PROPS = UNIQUE_ID,
+    VOID_REASON,
+    TRANS_PROPS = VOID_REASON,
 
     ACTION,
     ACCOUNT,
@@ -117,14 +118,16 @@ public:
      *  @returns true if this object is considered to be part of the parent, false otherwise.
      */
     bool is_part_of (std::shared_ptr<GncPreTrans> parent);
+    boost::optional<std::string> get_void_reason() { return m_void_reason; }
 
 private:
     int m_date_format;
+    boost::optional<std::string> m_differ;
     boost::optional<time64> m_date;
     boost::optional<std::string> m_num;
     boost::optional<std::string> m_desc;
     boost::optional<std::string> m_notes;
-    boost::optional<std::string> m_differ;
+    boost::optional<std::string> m_void_reason;
     bool created = false;
 };
 
diff --git a/src/import-export/csv-imp/gnc-tx-import.cpp b/src/import-export/csv-imp/gnc-tx-import.cpp
index 60ae6f4..ae61112 100644
--- a/src/import-export/csv-imp/gnc-tx-import.cpp
+++ b/src/import-export/csv-imp/gnc-tx-import.cpp
@@ -259,7 +259,21 @@ std::shared_ptr<DraftTransaction> GncTxImport::trans_properties_to_trans (std::v
 
     if (trans)
     {
+        /* We're about to continue with a new transaction
+         * Time to do some closing actions on the previous one
+         */
+        if (current_draft && current_draft->void_reason)
+        {
+            /* The import data specifies this transaction was voided.
+             * So void the created transaction as well.
+             * Attention: this assumes the imported transaction was balanced.
+             * If not, this will cause an imbalance split to be added automatically!
+             */
+            xaccTransCommitEdit (current_draft->trans);
+            xaccTransVoid (current_draft->trans, current_draft->void_reason->c_str());
+        }
         current_draft = std::make_shared<DraftTransaction>(trans);
+        current_draft->void_reason = trans_props->get_void_reason();
         created_trans = true;
     }
     else if (multi_split)  // in multi_split mode create_trans will return a nullptr for all but the first split
diff --git a/src/import-export/csv-imp/gnc-tx-import.hpp b/src/import-export/csv-imp/gnc-tx-import.hpp
index 7d7eefc..c3e89e2 100644
--- a/src/import-export/csv-imp/gnc-tx-import.hpp
+++ b/src/import-export/csv-imp/gnc-tx-import.hpp
@@ -54,6 +54,7 @@ struct DraftTransaction
     Transaction* trans;
     gnc_numeric balance;  /**< The expected balance after this transaction takes place */
     bool balance_set;     /**< true if balance has been set from user data, false otherwise */
+    boost::optional<std::string> void_reason;
 };
 
 /* A set of currency formats that the user sees. */

commit 95d7e17c7bed77625a935b84527aba5cc37f595b
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sun Dec 11 16:16:21 2016 +0100

    Extend csv importer to be able to import reconcile states
    
    A detail worth noting: contrary to most other date fields
    an empty value for a reconcile date is allowed if the reconcile
    field is not set to 'y' (reconciled).

diff --git a/src/import-export/csv-imp/gnc-trans-props.cpp b/src/import-export/csv-imp/gnc-trans-props.cpp
index e61afc8..06e6f71 100644
--- a/src/import-export/csv-imp/gnc-trans-props.cpp
+++ b/src/import-export/csv-imp/gnc-trans-props.cpp
@@ -57,9 +57,13 @@ std::map<GncTransPropType, const char*> gnc_csv_col_type_strs = {
         { GncTransPropType::WITHDRAWAL, N_("Withdrawal") },
         { GncTransPropType::BALANCE, N_("Balance") },
         { GncTransPropType::MEMO, N_("Memo") },
+        { GncTransPropType::REC_STATE, N_("Reconciled") },
+        { GncTransPropType::REC_DATE, N_("Reconcile Date") },
         { GncTransPropType::TACTION, N_("Transfer Action") },
         { GncTransPropType::TACCOUNT, N_("Transfer Account") },
-        { GncTransPropType::TMEMO, N_("Transfer Memo") }
+        { GncTransPropType::TMEMO, N_("Transfer Memo") },
+        { GncTransPropType::TREC_STATE, N_("Transfer Reconciled") },
+        { GncTransPropType::TREC_DATE, N_("Transfer Reconcile Date") }
 };
 
 /* Regular expressions used to parse dates per date format */
@@ -208,6 +212,21 @@ static boost::optional<gnc_numeric> parse_amount (const std::string &str, int cu
     return val;
 }
 
+static char parse_reconciled (const std::string& reconcile)
+{
+    if (g_strcmp0 (reconcile.c_str(), _("n")) == 0) // Not reconciled
+        return NREC;
+    else if (g_strcmp0 (reconcile.c_str(), _("c")) == 0) // Cleared
+        return CREC;
+    else if (g_strcmp0 (reconcile.c_str(), _("y")) == 0) // Reconciled
+        return YREC;
+    else if (g_strcmp0 (reconcile.c_str(), _("f")) == 0) // Frozen
+        return FREC;
+    else if (g_strcmp0 (reconcile.c_str(), _("v")) == 0) // Voided will be handled at the transaction level
+        return NREC;                                      // so return not reconciled here
+    else
+        throw std::invalid_argument ("String can't be parsed into a valid reconcile state.");
+}
 
 void GncPreTrans::set_property (GncTransPropType prop_type, const std::string& value)
 {
@@ -366,6 +385,23 @@ void GncPreSplit::set_property (GncTransPropType prop_type, const std::string& v
             m_withdrawal = parse_amount (value, m_currency_format); // Will throw if parsing fails
             break;
 
+        case GncTransPropType::REC_STATE:
+            m_rec_state = parse_reconciled (value); // Throws if parsing fails
+            break;
+
+        case GncTransPropType::TREC_STATE:
+            m_trec_state = parse_reconciled (value); // Throws if parsing fails
+            break;
+
+        case GncTransPropType::REC_DATE:
+            if (!value.empty())
+                m_rec_date = parse_date (value, m_date_format); // Throws if parsing fails
+            break;
+
+        case GncTransPropType::TREC_DATE:
+            m_trec_date = parse_date (value, m_date_format); // Throws if parsing fails
+            break;
+
         default:
             /* Issue a warning for all other prop_types. */
             PWARN ("%d is an invalid property for a split", static_cast<int>(prop_type));
@@ -376,11 +412,26 @@ void GncPreSplit::set_property (GncTransPropType prop_type, const std::string& v
 
 std::string GncPreSplit::verify_essentials (void)
 {
+    auto err_msg = std::string();
     /* Make sure this split has the minimum required set of properties defined. */
     if (!m_deposit && !m_withdrawal && !m_balance)
-        return _("No balance, deposit, or withdrawal column.");
-    else
-        return std::string();
+        err_msg = _("No balance, deposit, or withdrawal column.");
+
+    if (m_rec_state && *m_rec_state == YREC && !m_rec_date)
+    {
+        if (!err_msg.empty())
+            err_msg += "\n";
+        err_msg += _("Split is reconciled but reconcile date column is missing or invalid.");
+    }
+
+    if (m_trec_state && *m_trec_state == YREC && !m_trec_date)
+    {
+        if (!err_msg.empty())
+            err_msg += "\n";
+        err_msg += _("Transfer split is reconciled but transfer reconcile date column is missing or invalid.");
+    }
+
+    return err_msg;
 }
 
 /** Adds a split to a transaction.
@@ -389,9 +440,11 @@ std::string GncPreSplit::verify_essentials (void)
  * @param book The book where the split should be stored
  * @param amount The amount of the split
  */
-static void trans_add_split (Transaction* trans, Account* account,
-                            gnc_numeric amount, const boost::optional<std::string>& action,
-                            const boost::optional<std::string>& memo)
+static void trans_add_split (Transaction* trans, Account* account, gnc_numeric amount,
+                            const boost::optional<std::string>& action,
+                            const boost::optional<std::string>& memo,
+                            const boost::optional<char>& rec_state,
+                            const boost::optional<time64> rec_date)
 {
     auto book = xaccTransGetBook (trans);
     auto split = xaccMallocSplit (book);
@@ -405,6 +458,12 @@ static void trans_add_split (Transaction* trans, Account* account,
      * if needed by the book option */
     if (action)
         xaccSplitSetAction (split, action->c_str());
+
+    if (rec_state && *rec_state != ' ')
+        xaccSplitSetReconcile (split, *rec_state);
+    if (rec_state && *rec_state == YREC)
+        xaccSplitSetDateReconciledSecs (split, *rec_date);
+
 }
 
 boost::optional<gnc_numeric> GncPreSplit::create_split (Transaction* trans)
@@ -449,13 +508,13 @@ boost::optional<gnc_numeric> GncPreSplit::create_split (Transaction* trans)
                 GNC_HOW_RND_ROUND_HALF_UP);
 
     /* Add a split with the cumulative amount value. */
-    trans_add_split (trans, account, amount, m_action, m_memo);
+    trans_add_split (trans, account, amount, m_action, m_memo, m_rec_state, m_rec_date);
 
     if (taccount)
         /* Note: the current importer assumes at most 2 splits. This means the second split amount
          * will be the negative of the the first split amount.
          */
-        trans_add_split (trans, taccount, gnc_numeric_neg(amount), m_taction, m_tmemo);
+        trans_add_split (trans, taccount, gnc_numeric_neg(amount), m_taction, m_tmemo, m_trec_state, m_trec_date);
 
 
     created = true;
diff --git a/src/import-export/csv-imp/gnc-trans-props.hpp b/src/import-export/csv-imp/gnc-trans-props.hpp
index 41b4c7d..e77a9b7 100644
--- a/src/import-export/csv-imp/gnc-trans-props.hpp
+++ b/src/import-export/csv-imp/gnc-trans-props.hpp
@@ -60,9 +60,13 @@ enum class GncTransPropType {
     WITHDRAWAL,
     BALANCE,
     MEMO,
+    REC_STATE,
+    REC_DATE,
     TACTION,
     TACCOUNT,
     TMEMO,
+    TREC_STATE,
+    TREC_DATE,
     SPLIT_PROPS = TMEMO
 };
 
@@ -145,9 +149,13 @@ private:
     boost::optional<gnc_numeric> m_withdrawal;
     boost::optional<gnc_numeric> m_balance;
     boost::optional<std::string> m_memo;
+    boost::optional<char> m_rec_state;
+    boost::optional<time64> m_rec_date;
     boost::optional<std::string> m_taction;
     boost::optional<Account*> m_taccount;
     boost::optional<std::string> m_tmemo;
+    boost::optional<char> m_trec_state;
+    boost::optional<time64> m_trec_date;
     bool created = false;
 };
 
diff --git a/src/import-export/csv-imp/gnc-tx-import.cpp b/src/import-export/csv-imp/gnc-tx-import.cpp
index fd894aa..60ae6f4 100644
--- a/src/import-export/csv-imp/gnc-tx-import.cpp
+++ b/src/import-export/csv-imp/gnc-tx-import.cpp
@@ -226,7 +226,7 @@ static void trans_properties_verify_essentials (std::vector<parse_line_t>::itera
     error_message.clear();
     if (!trans_error.empty())
     {
-        error_message = trans_error;
+        error_message += trans_error;
         if (!split_error.empty())
             error_message += "\n";
     }

commit 2492931094a63b770e80c6745508ec5433cd4a6d
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sun Dec 11 16:15:38 2016 +0100

    Add additional check before creating splits or transactions
    
    This allows the code following the check to make certain assumptions about the state
    of the prop objects.

diff --git a/src/import-export/csv-imp/gnc-trans-props.cpp b/src/import-export/csv-imp/gnc-trans-props.cpp
index 9918258..e61afc8 100644
--- a/src/import-export/csv-imp/gnc-trans-props.cpp
+++ b/src/import-export/csv-imp/gnc-trans-props.cpp
@@ -267,6 +267,16 @@ Transaction* GncPreTrans::create_trans (QofBook* book, gnc_commodity* currency)
     if (created)
         return nullptr;
 
+    /* Gently refuse to create the transaction if the basics are not set correctly
+     * This should have been tested before calling this function though!
+     */
+    auto check = verify_essentials();
+    if (!check.empty())
+    {
+        PWARN ("Refusing to create transaction because essentials not set properly: %s", check.c_str());
+        return nullptr;
+    }
+
     auto trans = xaccMallocTransaction (book);
     xaccTransBeginEdit (trans);
     xaccTransSetCurrency (trans, currency);
@@ -402,6 +412,16 @@ boost::optional<gnc_numeric> GncPreSplit::create_split (Transaction* trans)
     if (created)
         return boost::none;
 
+    /* Gently refuse to create the split if the basics are not set correctly
+     * This should have been tested before calling this function though!
+     */
+    auto check = verify_essentials();
+    if (!check.empty())
+    {
+        PWARN ("Refusing to create split because essentials not set properly: %s", check.c_str());
+        return boost::none;
+    }
+
     Account *account = nullptr;
     Account *taccount = nullptr;
     bool amount_set = false;

commit d2597ef1e292cd67c315c04a01d282ab2374b1ba
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sun Dec 11 16:02:19 2016 +0100

    Defer testing boost:optionals until really needed
    Similarly only lookup book when really needed

diff --git a/src/import-export/csv-imp/gnc-trans-props.cpp b/src/import-export/csv-imp/gnc-trans-props.cpp
index df2781a..9918258 100644
--- a/src/import-export/csv-imp/gnc-trans-props.cpp
+++ b/src/import-export/csv-imp/gnc-trans-props.cpp
@@ -379,20 +379,22 @@ std::string GncPreSplit::verify_essentials (void)
  * @param book The book where the split should be stored
  * @param amount The amount of the split
  */
-static void trans_add_split (Transaction* trans, Account* account, QofBook* book,
-                            gnc_numeric amount, const std::string& action, const std::string& memo)
+static void trans_add_split (Transaction* trans, Account* account,
+                            gnc_numeric amount, const boost::optional<std::string>& action,
+                            const boost::optional<std::string>& memo)
 {
+    auto book = xaccTransGetBook (trans);
     auto split = xaccMallocSplit (book);
     xaccSplitSetAccount (split, account);
     xaccSplitSetParent (split, trans);
     xaccSplitSetAmount (split, amount);
     xaccSplitSetValue (split, amount);
-    if (!memo.empty())
-        xaccSplitSetMemo (split, memo.c_str());
+    if (memo)
+        xaccSplitSetMemo (split, memo->c_str());
     /* Note, this function assumes the num/action switch is done at a higher level
      * if needed by the book option */
-    if (!action.empty())
-        xaccSplitSetAction (split, action.c_str());
+    if (action)
+        xaccSplitSetAction (split, action->c_str());
 }
 
 boost::optional<gnc_numeric> GncPreSplit::create_split (Transaction* trans)
@@ -400,11 +402,6 @@ boost::optional<gnc_numeric> GncPreSplit::create_split (Transaction* trans)
     if (created)
         return boost::none;
 
-    auto book = xaccTransGetBook (trans);
-    std::string action;
-    std::string taction;
-    std::string memo;
-    std::string tmemo;
     Account *account = nullptr;
     Account *taccount = nullptr;
     bool amount_set = false;
@@ -412,18 +409,10 @@ boost::optional<gnc_numeric> GncPreSplit::create_split (Transaction* trans)
     gnc_numeric withdrawal = { 0, 1 };
     gnc_numeric amount = { 0, 1 };
 
-    if (m_taction)
-        taction = *m_taction;
-    if (m_action)
-        action = *m_action;
     if (m_account)
         account = *m_account;
     if (m_taccount)
         taccount = *m_taccount;
-    if (m_memo)
-        memo = *m_memo;
-    if (m_tmemo)
-        tmemo = *m_tmemo;
     if (m_deposit)
     {
         deposit = *m_deposit;
@@ -440,13 +429,13 @@ boost::optional<gnc_numeric> GncPreSplit::create_split (Transaction* trans)
                 GNC_HOW_RND_ROUND_HALF_UP);
 
     /* Add a split with the cumulative amount value. */
-    trans_add_split (trans, account, book, amount, action, memo);
+    trans_add_split (trans, account, amount, m_action, m_memo);
 
     if (taccount)
         /* Note: the current importer assumes at most 2 splits. This means the second split amount
          * will be the negative of the the first split amount.
          */
-        trans_add_split (trans, taccount, book, gnc_numeric_neg(amount), taction, tmemo);
+        trans_add_split (trans, taccount, gnc_numeric_neg(amount), m_taction, m_tmemo);
 
 
     created = true;

commit 848c7b8f8ec00b2862ca4a80208030342fe38040
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sun Dec 11 14:54:48 2016 +0100

    Create trans/split props only when needed and have them keep their own value of date/currency format

diff --git a/src/import-export/csv-imp/gnc-trans-props.cpp b/src/import-export/csv-imp/gnc-trans-props.cpp
index 54a534d..df2781a 100644
--- a/src/import-export/csv-imp/gnc-trans-props.cpp
+++ b/src/import-export/csv-imp/gnc-trans-props.cpp
@@ -209,12 +209,12 @@ static boost::optional<gnc_numeric> parse_amount (const std::string &str, int cu
 }
 
 
-void GncPreTrans::set_property (GncTransPropType prop_type, const std::string& value, int date_format)
+void GncPreTrans::set_property (GncTransPropType prop_type, const std::string& value)
 {
     switch (prop_type)
     {
         case GncTransPropType::DATE:
-            m_date = parse_date (value.c_str(), date_format); // Throws if parsing fails
+            m_date = parse_date (value, m_date_format); // Throws if parsing fails
             break;
 
         case GncTransPropType::NUM:
@@ -297,7 +297,7 @@ bool GncPreTrans::is_part_of (std::shared_ptr<GncPreTrans> parent)
             (!m_differ || m_differ == parent->m_differ);
 }
 
-void GncPreSplit::set_property (GncTransPropType prop_type, const std::string& value, int currency_format)
+void GncPreSplit::set_property (GncTransPropType prop_type, const std::string& value)
 {
     Account *acct = nullptr;
     switch (prop_type)
@@ -347,13 +347,13 @@ void GncPreSplit::set_property (GncTransPropType prop_type, const std::string& v
             break;
 
         case GncTransPropType::BALANCE:
-            m_balance = parse_amount (value, currency_format); // Will throw if parsing fails
+            m_balance = parse_amount (value, m_currency_format); // Will throw if parsing fails
             break;
         case GncTransPropType::DEPOSIT:
-            m_deposit = parse_amount (value, currency_format); // Will throw if parsing fails
+            m_deposit = parse_amount (value, m_currency_format); // Will throw if parsing fails
             break;
         case GncTransPropType::WITHDRAWAL:
-            m_withdrawal = parse_amount (value, currency_format); // Will throw if parsing fails
+            m_withdrawal = parse_amount (value, m_currency_format); // Will throw if parsing fails
             break;
 
         default:
diff --git a/src/import-export/csv-imp/gnc-trans-props.hpp b/src/import-export/csv-imp/gnc-trans-props.hpp
index 253144c..41b4c7d 100644
--- a/src/import-export/csv-imp/gnc-trans-props.hpp
+++ b/src/import-export/csv-imp/gnc-trans-props.hpp
@@ -92,7 +92,9 @@ time64 parse_date (const std::string &date_str, int format);
 struct GncPreTrans
 {
 public:
-    void set_property (GncTransPropType prop_type, const std::string& value, int date_format = 0);
+    GncPreTrans(int date_format) : m_date_format{date_format} {};
+
+    void set_property (GncTransPropType prop_type, const std::string& value);
     std::string verify_essentials (void);
     Transaction *create_trans (QofBook* book, gnc_commodity* currency);
 
@@ -113,6 +115,7 @@ public:
     bool is_part_of (std::shared_ptr<GncPreTrans> parent);
 
 private:
+    int m_date_format;
     boost::optional<time64> m_date;
     boost::optional<std::string> m_num;
     boost::optional<std::string> m_desc;
@@ -124,7 +127,9 @@ private:
 struct GncPreSplit
 {
 public:
-    void set_property (GncTransPropType prop_type, const std::string& value, int currency_format = 0);
+    GncPreSplit (int date_format, int currency_format) : m_date_format{date_format},
+        m_currency_format{currency_format}{};
+    void set_property (GncTransPropType prop_type, const std::string& value);
     std::string verify_essentials (void);
     boost::optional<gnc_numeric> create_split(Transaction* trans);
 
@@ -132,6 +137,8 @@ public:
     void set_account (Account* acct) { if (acct) m_account = acct; else m_account = boost::none; }
 
 private:
+    int m_date_format;
+    int m_currency_format;
     boost::optional<std::string> m_action;
     boost::optional<Account*> m_account;
     boost::optional<gnc_numeric> m_deposit;
diff --git a/src/import-export/csv-imp/gnc-tx-import.cpp b/src/import-export/csv-imp/gnc-tx-import.cpp
index e35172a..fd894aa 100644
--- a/src/import-export/csv-imp/gnc-tx-import.cpp
+++ b/src/import-export/csv-imp/gnc-tx-import.cpp
@@ -174,7 +174,7 @@ void GncTxImport::tokenize (bool guessColTypes)
     for (auto tokenized_line : tokenizer->get_tokens())
     {
         parsed_lines.push_back (std::make_tuple (tokenized_line, std::string(),
-                std::make_shared<GncPreTrans>(), std::make_shared<GncPreSplit>()));
+                nullptr, nullptr));
         auto length = tokenized_line.size();
         if (length > max_cols)
             max_cols = length;
@@ -338,9 +338,9 @@ void GncTxImport::create_transaction (std::vector<parse_line_t>::iterator& parse
 {
     StrVec line;
     std::string error_message;
-    std::shared_ptr<GncPreTrans> trans_props;
-    std::shared_ptr<GncPreSplit> split_props;
-    std::tie(line, error_message, trans_props, split_props) = *parsed_line;
+    auto trans_props = std::make_shared<GncPreTrans>(date_format);
+    auto split_props = std::make_shared<GncPreSplit>(date_format, currency_format);
+    std::tie(line, error_message, std::ignore, std::ignore) = *parsed_line;
     error_message.clear();
 
     /* Convert all tokens in this line into transaction/split properties. */
@@ -359,10 +359,10 @@ void GncTxImport::create_transaction (std::vector<parse_line_t>::iterator& parse
             {
                 if (multi_split && line_it->empty())
                     continue; // In multi-split mode, transaction properties can be empty
-                trans_props->set_property(*col_types_it, *line_it, date_format);
+                trans_props->set_property(*col_types_it, *line_it);
             }
             else
-                split_props->set_property(*col_types_it, *line_it, currency_format);
+                split_props->set_property(*col_types_it, *line_it);
         }
         catch (const std::exception& e)
         {
@@ -393,9 +393,12 @@ void GncTxImport::create_transaction (std::vector<parse_line_t>::iterator& parse
                 error_message = _("First line of this transaction has errors.");
         }
         else
+        {
+            std::get<2>(*parsed_line) = trans_props;
             /* This line starts a new transaction, set it as parent for
              * subsequent lines. */
             parent = trans_props;
+        }
     }
 
     if (!error_message.empty())
@@ -418,6 +421,7 @@ void GncTxImport::create_transaction (std::vector<parse_line_t>::iterator& parse
             throw std::invalid_argument(error_message);
         }
     }
+    std::get<3>(*parsed_line) = split_props;
 
     /* If column parsing was successful, convert trans properties into a draft transaction. */
     try

commit 443237f2b9057c654e73933af19eeb582b440750
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sat Jan 14 15:46:01 2017 +0100

    Extend csv importer to be able to import split action fields
    
    At the same time drop the exception handling for the num for action setting.
    The importer can't possibly get this right automatically in all cases. It's
    now up to the user to assign the right columns to the correct properties
    based on how he/she wants gnucash to store it internally.

diff --git a/src/import-export/csv-imp/gnc-trans-props.cpp b/src/import-export/csv-imp/gnc-trans-props.cpp
index 2e08dcb..54a534d 100644
--- a/src/import-export/csv-imp/gnc-trans-props.cpp
+++ b/src/import-export/csv-imp/gnc-trans-props.cpp
@@ -51,11 +51,13 @@ std::map<GncTransPropType, const char*> gnc_csv_col_type_strs = {
         { GncTransPropType::DESCRIPTION, N_("Description") },
         { GncTransPropType::UNIQUE_ID, N_("Transaction ID") },
         { GncTransPropType::NOTES, N_("Notes") },
+        { GncTransPropType::ACTION, N_("Action") },
         { GncTransPropType::ACCOUNT, N_("Account") },
         { GncTransPropType::DEPOSIT, N_("Deposit") },
         { GncTransPropType::WITHDRAWAL, N_("Withdrawal") },
         { GncTransPropType::BALANCE, N_("Balance") },
         { GncTransPropType::MEMO, N_("Memo") },
+        { GncTransPropType::TACTION, N_("Transfer Action") },
         { GncTransPropType::TACCOUNT, N_("Transfer Account") },
         { GncTransPropType::TMEMO, N_("Transfer Memo") }
 };
@@ -215,6 +217,13 @@ void GncPreTrans::set_property (GncTransPropType prop_type, const std::string& v
             m_date = parse_date (value.c_str(), date_format); // Throws if parsing fails
             break;
 
+        case GncTransPropType::NUM:
+            if (!value.empty())
+                m_num = value;
+            else
+                m_num = boost::none;
+            break;
+
         case GncTransPropType::DESCRIPTION:
             if (!value.empty())
                 m_desc = value;
@@ -264,6 +273,9 @@ Transaction* GncPreTrans::create_trans (QofBook* book, gnc_commodity* currency)
 
     xaccTransSetDatePostedSecsNormalized (trans, *m_date);
 
+    if (m_num)
+        xaccTransSetNum (trans, m_num->c_str());
+
     if (m_desc)
         xaccTransSetDescription (trans, m_desc->c_str());
 
@@ -290,6 +302,20 @@ void GncPreSplit::set_property (GncTransPropType prop_type, const std::string& v
     Account *acct = nullptr;
     switch (prop_type)
     {
+        case GncTransPropType::ACTION:
+            if (!value.empty())
+                m_action = value;
+            else
+                m_action = boost::none;
+            break;
+
+        case GncTransPropType::TACTION:
+            if (!value.empty())
+                m_taction = value;
+            else
+                m_taction = boost::none;
+            break;
+
         case GncTransPropType::ACCOUNT:
             acct = gnc_csv_account_map_search (value.c_str());
             if (acct)
@@ -320,13 +346,6 @@ void GncPreSplit::set_property (GncTransPropType prop_type, const std::string& v
                 m_tmemo = boost::none;
             break;
 
-        case GncTransPropType::NUM:
-            if (!value.empty())
-                m_num = value;
-            else
-                m_num = boost::none;
-            break;
-
         case GncTransPropType::BALANCE:
             m_balance = parse_amount (value, currency_format); // Will throw if parsing fails
             break;
@@ -361,7 +380,7 @@ std::string GncPreSplit::verify_essentials (void)
  * @param amount The amount of the split
  */
 static void trans_add_split (Transaction* trans, Account* account, QofBook* book,
-                            gnc_numeric amount, const std::string& num, const std::string& memo)
+                            gnc_numeric amount, const std::string& action, const std::string& memo)
 {
     auto split = xaccMallocSplit (book);
     xaccSplitSetAccount (split, account);
@@ -370,10 +389,10 @@ static void trans_add_split (Transaction* trans, Account* account, QofBook* book
     xaccSplitSetValue (split, amount);
     if (!memo.empty())
         xaccSplitSetMemo (split, memo.c_str());
-    /* set tran-num and/or split-action per book option
-     * note this function does nothing if num is NULL also */
-    if (!num.empty())
-        gnc_set_num_action (trans, split, num.c_str(), NULL);
+    /* Note, this function assumes the num/action switch is done at a higher level
+     * if needed by the book option */
+    if (!action.empty())
+        xaccSplitSetAction (split, action.c_str());
 }
 
 boost::optional<gnc_numeric> GncPreSplit::create_split (Transaction* trans)
@@ -382,7 +401,8 @@ boost::optional<gnc_numeric> GncPreSplit::create_split (Transaction* trans)
         return boost::none;
 
     auto book = xaccTransGetBook (trans);
-    std::string num;
+    std::string action;
+    std::string taction;
     std::string memo;
     std::string tmemo;
     Account *account = nullptr;
@@ -392,6 +412,10 @@ boost::optional<gnc_numeric> GncPreSplit::create_split (Transaction* trans)
     gnc_numeric withdrawal = { 0, 1 };
     gnc_numeric amount = { 0, 1 };
 
+    if (m_taction)
+        taction = *m_taction;
+    if (m_action)
+        action = *m_action;
     if (m_account)
         account = *m_account;
     if (m_taccount)
@@ -400,8 +424,6 @@ boost::optional<gnc_numeric> GncPreSplit::create_split (Transaction* trans)
         memo = *m_memo;
     if (m_tmemo)
         tmemo = *m_tmemo;
-    if (m_num)
-        num = *m_num;
     if (m_deposit)
     {
         deposit = *m_deposit;
@@ -418,14 +440,13 @@ boost::optional<gnc_numeric> GncPreSplit::create_split (Transaction* trans)
                 GNC_HOW_RND_ROUND_HALF_UP);
 
     /* Add a split with the cumulative amount value. */
-    trans_add_split (trans, account, book, amount, num, memo);
+    trans_add_split (trans, account, book, amount, action, memo);
 
     if (taccount)
         /* Note: the current importer assumes at most 2 splits. This means the second split amount
-         * will be the negative of the the first split amount. We also only set the num field once,
-         * for the first split.
+         * will be the negative of the the first split amount.
          */
-        trans_add_split (trans, taccount, book, gnc_numeric_neg(amount), "", tmemo);
+        trans_add_split (trans, taccount, book, gnc_numeric_neg(amount), taction, tmemo);
 
 
     created = true;
diff --git a/src/import-export/csv-imp/gnc-trans-props.hpp b/src/import-export/csv-imp/gnc-trans-props.hpp
index 84c7eb9..253144c 100644
--- a/src/import-export/csv-imp/gnc-trans-props.hpp
+++ b/src/import-export/csv-imp/gnc-trans-props.hpp
@@ -48,21 +48,19 @@ extern "C" {
 enum class GncTransPropType {
     NONE,
     DATE,
+    NUM,
     DESCRIPTION,
     NOTES,
     UNIQUE_ID,
     TRANS_PROPS = UNIQUE_ID,
 
-    // num is strictly speaking a trans prop and not a split prop
-    // however due to the num/action swap user option, it can only be
-    // set while creating splits...
-    NUM,
-
+    ACTION,
     ACCOUNT,
     DEPOSIT,
     WITHDRAWAL,
     BALANCE,
     MEMO,
+    TACTION,
     TACCOUNT,
     TMEMO,
     SPLIT_PROPS = TMEMO
@@ -116,6 +114,7 @@ public:
 
 private:
     boost::optional<time64> m_date;
+    boost::optional<std::string> m_num;
     boost::optional<std::string> m_desc;
     boost::optional<std::string> m_notes;
     boost::optional<std::string> m_differ;
@@ -133,18 +132,15 @@ public:
     void set_account (Account* acct) { if (acct) m_account = acct; else m_account = boost::none; }
 
 private:
+    boost::optional<std::string> m_action;
     boost::optional<Account*> m_account;
     boost::optional<gnc_numeric> m_deposit;
     boost::optional<gnc_numeric> m_withdrawal;
     boost::optional<gnc_numeric> m_balance;
     boost::optional<std::string> m_memo;
+    boost::optional<std::string> m_taction;
     boost::optional<Account*> m_taccount;
     boost::optional<std::string> m_tmemo;
-
-    // Strictly speaking num is a transaction property
-    // However due to the option to swap num and action fields
-    // This can only be set when splits are created
-    boost::optional<std::string> m_num;
     bool created = false;
 };
 

commit 5950b902a41237b30e6b559f3678361c662bd2fe
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sat Dec 10 18:34:30 2016 +0100

    Avoid assigning empty or null char*'s to a std::string
    
    Apparently this can cause segfaults

diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index 5ddb535..8a2721e 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -540,7 +540,9 @@ csv_import_trans_save_settings_cb (GtkWidget *button, CsvImportTrans *info)
             info->settings_data.separator[i] = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(info->sep_buttons[i]));
         }
         info->settings_data.custom = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(info->custom_cbutton));
-        info->settings_data.custom_entry = gtk_entry_get_text (GTK_ENTRY(info->custom_entry));
+        auto tmp_text = gtk_entry_get_text (GTK_ENTRY(info->custom_entry));
+        if (tmp_text && *tmp_text != '\0')
+            info->settings_data.custom_entry = tmp_text;
 
         /* This section deals with the combo's and character encoding */
         info->settings_data.date_active = gtk_combo_box_get_active (GTK_COMBO_BOX(info->date_format_combo));
diff --git a/src/import-export/csv-imp/gnc-csv-trans-settings.cpp b/src/import-export/csv-imp/gnc-csv-trans-settings.cpp
index f22c346..1c6f288 100644
--- a/src/import-export/csv-imp/gnc-csv-trans-settings.cpp
+++ b/src/import-export/csv-imp/gnc-csv-trans-settings.cpp
@@ -170,7 +170,8 @@ CsvTransSettings::load (const std::string& group)
     error |= handle_load_error (&key_error, group);
 
     gchar *key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_CUSTOM_ENTRY, &key_error);
-    custom_entry = key_char;
+    if (key_char && *key_char != '\0')
+        custom_entry = key_char;
     error |= handle_load_error (&key_error, group);
     if (key_char)
         g_free (key_char);
@@ -182,7 +183,10 @@ CsvTransSettings::load (const std::string& group)
     error |= handle_load_error (&key_error, group);
 
     key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_ENCODING, &key_error);
-    encoding = (key_error) ? "UTF-8" : key_char;
+    if (key_char && *key_char != '\0')
+        encoding = key_char;
+    else
+        "UTF-8";
     error |= handle_load_error (&key_error, group);
     if (key_char)
         g_free (key_char);

commit 6d304d3c3ee6b0a41c0c005bc3dfdad69a8976ae
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sat Dec 10 16:37:51 2016 +0100

    csv import prefs - store coltypes as strings
    
    Additionally use g_key_file_[gs]_<type>_list to store and retrieve coltypes and colwidths
    instead of storing/retrieving a raw string to parse afterwards

diff --git a/src/import-export/csv-imp/gnc-csv-trans-settings.cpp b/src/import-export/csv-imp/gnc-csv-trans-settings.cpp
index af80be5..f22c346 100644
--- a/src/import-export/csv-imp/gnc-csv-trans-settings.cpp
+++ b/src/import-export/csv-imp/gnc-csv-trans-settings.cpp
@@ -36,9 +36,6 @@ extern "C"
 #include "gnc-state.h"
 }
 
-#include <sstream>
-#include <boost/tokenizer.hpp>
-
 const std::string csv_group_prefix{"CSV - "};
 const std::string no_settings{N_("No Settings")};
 #define CSV_NAME         "Name"
@@ -190,35 +187,28 @@ CsvTransSettings::load (const std::string& group)
     if (key_char)
         g_free (key_char);
 
-    key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_COL_TYPES, &key_error);
-    error |= handle_load_error (&key_error, group);
-    auto col_types_str = std::string { key_char };
-    if (key_char)
-        g_free (key_char);
-
     column_types.clear();
-    if (!col_types_str.empty())
+    gsize list_len;
+    gchar** col_types_str = g_key_file_get_string_list (keyfile, group.c_str(), CSV_COL_TYPES,
+            &list_len, &key_error);
+    for (uint i = 0; i < list_len; i++)
     {
-        using Tokenizer = boost::tokenizer< boost::escaped_list_separator<char>>;
-        boost::escaped_list_separator<char> sep("\\", ",", "\"");
-        Tokenizer tok(col_types_str, sep);
-        for (auto col_type_str : tok)
-        {
-            auto col_type = std::stoi(col_type_str);
-            if (col_type >= static_cast<int>(GncTransPropType::NONE) &&
-                col_type <= static_cast<int>(GncTransPropType::SPLIT_PROPS))
-                column_types.push_back (static_cast<GncTransPropType>(col_type));
-            else
-                column_types.push_back (GncTransPropType::NONE);
-        }
+        auto col_types_it = std::find_if (gnc_csv_col_type_strs.begin(),
+                gnc_csv_col_type_strs.end(), test_prop_type_str (col_types_str[i]));
+        if (col_types_it != gnc_csv_col_type_strs.end())
+            column_types.push_back(col_types_it->first);
     }
+    if (col_types_str)
+        g_strfreev (col_types_str);
 
     column_widths.clear();
-    gsize list_len;
     gint *col_widths_int = g_key_file_get_integer_list (keyfile, group.c_str(), CSV_COL_WIDTHS,
             &list_len, &key_error);
     for (uint i = 0; i < list_len; i++)
-        column_widths.push_back(col_widths_int[i]);
+    {
+        if (col_widths_int[i] > 0)
+            column_widths.push_back(col_widths_int[i]);
+    }
     error |= handle_load_error (&key_error, group);
     if (col_widths_int)
         g_free (col_widths_int);
@@ -263,28 +253,27 @@ CsvTransSettings::save (const std::string& settings_name)
     g_key_file_set_integer (keyfile, group.c_str(), CSV_CURRENCY, currency_active);
     g_key_file_set_string (keyfile, group.c_str(), CSV_ENCODING, encoding.c_str());
 
-    std::stringstream ss;
+    std::vector<const char*> col_types_str;
     for (auto col_type : column_types)
-    {
-        if (!ss.str().empty())
-            ss << ",";
-        ss << static_cast<uint>(col_type);
-    }
+        col_types_str.push_back(gnc_csv_col_type_strs[col_type]);
 
-    g_key_file_set_string (keyfile, group.c_str(), CSV_COL_TYPES, ss.str().c_str());
+    if (!col_types_str.empty())
+        g_key_file_set_string_list (keyfile, group.c_str(), CSV_COL_TYPES,
+                col_types_str.data(), col_types_str.size());
 
-    if (!column_widths.emtpy())
+    if (!column_widths.empty())
         g_key_file_set_integer_list (keyfile, group.c_str(), CSV_COL_WIDTHS,
                 (gint*)(column_widths.data()), column_widths.size());
 
-    // Do a test read of column types
+    // Do a test read of encoding
     GError *key_error = nullptr;
     bool error = false;
-    auto col_types_val = g_key_file_get_string (keyfile, group.c_str(), CSV_COL_TYPES, &key_error);
-    auto test_string = std::string{col_types_val};
-    g_free (col_types_val);
+    auto enc_val = g_key_file_get_string (keyfile, group.c_str(), CSV_ENCODING, &key_error);
+    auto enc_str = std::string{enc_val};
+    if (enc_val)
+        g_free (enc_val);
 
-    if ((key_error) || (test_string != ss.str()))
+    if ((key_error) || (enc_str != encoding.c_str()))
     {
         if (key_error)
         {
@@ -292,7 +281,7 @@ CsvTransSettings::save (const std::string& settings_name)
             g_error_free (key_error);
         }
         else
-            g_warning ("Error comparing group %s key %s: '%s' and '%s'", group.c_str(), CSV_COL_TYPES, test_string.c_str(), group.c_str());
+            g_warning ("Error comparing group %s key %s: '%s' and '%s'", group.c_str(), CSV_COL_TYPES, enc_str.c_str(), group.c_str());
         error = true;
     }
     return error;
diff --git a/src/import-export/csv-imp/gnc-trans-props.hpp b/src/import-export/csv-imp/gnc-trans-props.hpp
index b18d671..84c7eb9 100644
--- a/src/import-export/csv-imp/gnc-trans-props.hpp
+++ b/src/import-export/csv-imp/gnc-trans-props.hpp
@@ -74,6 +74,21 @@ enum class GncTransPropType {
  *  changes to enum class GncTransPropType ! */
 extern std::map<GncTransPropType, const char*> gnc_csv_col_type_strs;
 
+
+/** Functor to check if the above map has an element of which
+ *  the value equals name. To be used with std::find_if.
+ */
+struct test_prop_type_str
+{
+    test_prop_type_str( const char* name ) : m_name(name) {}
+    bool operator()( const std::pair<GncTransPropType, const char*>& v ) const
+    {
+        return !g_strcmp0(v.second, m_name);
+    }
+private:
+    const char *m_name;
+};
+
 time64 parse_date (const std::string &date_str, int format);
 
 struct GncPreTrans
@@ -133,6 +148,4 @@ private:
     bool created = false;
 };
 
-
-
 #endif

commit 1fc4b3cd9b6c11015cd1d3b715aa57eebcec0468
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Thu Dec 8 18:07:12 2016 +0100

    Convert column_widths to a vector

diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index 277b133..5ddb535 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -284,8 +284,7 @@ csv_import_trans_load_settings (CsvImportTrans *info)
             // Handle column widths, only relevant if the file format is fixed width
             info->parse_data->file_format (GncImpFileFormat::FIXED_WIDTH);
             GncFwTokenizer *fwtok = dynamic_cast<GncFwTokenizer*>(info->parse_data->tokenizer.get());
-            if (info->settings_data.column_widths, NULL)
-                fwtok->cols_from_string (std::string(info->settings_data.column_widths));
+            fwtok->columns(info->settings_data.column_widths);
 
             info->parse_data->tokenize (false);
             gnc_csv_preview_update_assist (info);
@@ -575,12 +574,10 @@ csv_import_trans_save_settings_cb (GtkWidget *button, CsvImportTrans *info)
         g_list_free (columns);
 
         /* Save the column widths in fixed mode */
-        if (info->settings_data.csv_format)
-            info->settings_data.column_widths = "5,10,15";
-        else
+        if (!info->settings_data.csv_format)
         {
             GncFwTokenizer *fwtok = dynamic_cast<GncFwTokenizer*>(info->parse_data->tokenizer.get());
-            info->settings_data.column_widths = g_strdup (fwtok->cols_to_string().c_str());
+            info->settings_data.column_widths = fwtok->get_columns();
         }
 
         // Save the settings
diff --git a/src/import-export/csv-imp/gnc-csv-trans-settings.cpp b/src/import-export/csv-imp/gnc-csv-trans-settings.cpp
index 04c1113..af80be5 100644
--- a/src/import-export/csv-imp/gnc-csv-trans-settings.cpp
+++ b/src/import-export/csv-imp/gnc-csv-trans-settings.cpp
@@ -213,8 +213,15 @@ CsvTransSettings::load (const std::string& group)
         }
     }
 
-    column_widths = g_key_file_get_string (keyfile, group.c_str(), CSV_COL_WIDTHS, &key_error);
+    column_widths.clear();
+    gsize list_len;
+    gint *col_widths_int = g_key_file_get_integer_list (keyfile, group.c_str(), CSV_COL_WIDTHS,
+            &list_len, &key_error);
+    for (uint i = 0; i < list_len; i++)
+        column_widths.push_back(col_widths_int[i]);
     error |= handle_load_error (&key_error, group);
+    if (col_widths_int)
+        g_free (col_widths_int);
 
     return error;
 }
@@ -265,7 +272,10 @@ CsvTransSettings::save (const std::string& settings_name)
     }
 
     g_key_file_set_string (keyfile, group.c_str(), CSV_COL_TYPES, ss.str().c_str());
-    g_key_file_set_string (keyfile, group.c_str(), CSV_COL_WIDTHS, column_widths);
+
+    if (!column_widths.emtpy())
+        g_key_file_set_integer_list (keyfile, group.c_str(), CSV_COL_WIDTHS,
+                (gint*)(column_widths.data()), column_widths.size());
 
     // Do a test read of column types
     GError *key_error = nullptr;
diff --git a/src/import-export/csv-imp/gnc-csv-trans-settings.hpp b/src/import-export/csv-imp/gnc-csv-trans-settings.hpp
index 6a447c9..6d7218a 100644
--- a/src/import-export/csv-imp/gnc-csv-trans-settings.hpp
+++ b/src/import-export/csv-imp/gnc-csv-trans-settings.hpp
@@ -49,7 +49,7 @@ struct CsvTransSettings
     CsvTransSettings() : header_rows{0}, footer_rows{0}, csv_format (true),
                     skip_alt_rows (false), multi_split (false),
                     encoding {"UTF-8"}, custom {false}, custom_entry {nullptr},
-                    date_active {0}, currency_active {0}, column_widths{nullptr}
+                    date_active {0}, currency_active {0}
                     {
                         for (uint i = 0; i < SEP_NUM_OF_TYPES; i++)
                         {
@@ -99,7 +99,7 @@ std::string   custom_entry;                 // Custom Entry
 int           date_active;                  // Date Active id
 int           currency_active;              // Currency Active id
 std::vector<GncTransPropType>  column_types;// The Column types in order
-const gchar  *column_widths;                // The Column widths
+std::vector<uint> column_widths;            // The Column widths
 
 };
 
diff --git a/src/import-export/csv-imp/gnc-fw-tokenizer.cpp b/src/import-export/csv-imp/gnc-fw-tokenizer.cpp
index 4d85751..35f20b7 100644
--- a/src/import-export/csv-imp/gnc-fw-tokenizer.cpp
+++ b/src/import-export/csv-imp/gnc-fw-tokenizer.cpp
@@ -17,6 +17,12 @@ GncFwTokenizer::columns(const std::vector<uint>& cols)
     m_col_vec = cols;
 }
 
+std::vector<uint>
+GncFwTokenizer::get_columns()
+{
+    return m_col_vec;
+}
+
 
 bool GncFwTokenizer::col_can_delete (uint col_num)
 {
@@ -105,50 +111,6 @@ void GncFwTokenizer::col_split (uint col_num, uint position)
 }
 
 
-std::string GncFwTokenizer::cols_to_string()
-{
-    std::ostringstream colstream;
-    for (auto col_end : m_col_vec)
-        colstream<<col_end<<",";
-    std::string colstr = colstream.str();
-    if (!colstr.empty())
-        colstr.pop_back(); // Drop last ","
-    return colstr;
-}
-
-void GncFwTokenizer::cols_from_string(const std::string& col_str)
-{
-    // Clear existing columns first
-    columns();
-
-    /* Set an initial column as expected by the code below
-     * If no data is read yet, take a rather large value
-     * Otherwise take the size of the widest line in the data
-     */
-    if (m_longest_line != 0)
-        m_col_vec.push_back (m_longest_line);
-    else
-        m_col_vec.push_back (99999);
-
-    uint col = 0;
-    std::istringstream in_stream(col_str);
-    std::string col_end_str;
-    while (std::getline (in_stream, col_end_str, ','))
-    {
-        if (col_end_str.empty())
-            continue;  // Skip empty column positions
-
-        uint col_width = std::stoi (col_end_str);
-        col_split (col, col_width);
-        col++;
-    }
-
-    // If no data is loaded yet, remove last, unreasonably wide column
-    if (m_longest_line == 0)
-        m_col_vec.pop_back();
-}
-
-
 void GncFwTokenizer::load_file(const std::string& path)
 {
     GncTokenizer::load_file(path);
diff --git a/src/import-export/csv-imp/gnc-fw-tokenizer.hpp b/src/import-export/csv-imp/gnc-fw-tokenizer.hpp
index 520ae7a..0222180 100644
--- a/src/import-export/csv-imp/gnc-fw-tokenizer.hpp
+++ b/src/import-export/csv-imp/gnc-fw-tokenizer.hpp
@@ -58,6 +58,7 @@ public:
     ~GncFwTokenizer() = default;                                // destructor
 
     void columns(const std::vector<uint>& cols = std::vector<uint>());
+    std::vector<uint> get_columns();
     uint get_column (uint num);
 
     // Column manipulators
@@ -70,9 +71,6 @@ public:
     bool col_can_split (uint col_num, uint position);
     void col_split (uint col_num, uint position);
 
-    std::string cols_to_string();
-    void cols_from_string(const std::string& col_str);
-
     void load_file (const std::string& path) override;
     int  tokenize() override;
 

commit 9e70166b8ed27754a3af8db1a78de9a191080ac0
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Thu Dec 8 17:24:42 2016 +0100

    Drop superfluous code that sets the default in case of an error.
    
    The returned values in case of an error from the g_key_file_get* are
    the correct defaults in most cases already.
    In addition:
    - Reduce number of temporary variables
    - Fix a memory leak while testing the saved data

diff --git a/src/import-export/csv-imp/gnc-csv-trans-settings.cpp b/src/import-export/csv-imp/gnc-csv-trans-settings.cpp
index e0c0bf6..04c1113 100644
--- a/src/import-export/csv-imp/gnc-csv-trans-settings.cpp
+++ b/src/import-export/csv-imp/gnc-csv-trans-settings.cpp
@@ -135,78 +135,68 @@ handle_load_error (GError **key_error, const std::string& group)
 /**************************************************
  * load
  *
- * load the settings from a key file
+ * load the settings from a state key file
  **************************************************/
 bool
 CsvTransSettings::load (const std::string& group)
 {
-    GKeyFile   *keyfile;
-    gint        i;
-    GError     *key_error = NULL;
-    bool        key_boolean = false;
-    int         key_int = 0;
-    gchar      *key_char = nullptr;
-    bool        error = false;
-
-    // Get the Key file
-    keyfile = gnc_state_get_current ();
-
-    key_int = g_key_file_get_integer (keyfile, group.c_str(), CSV_SKIP_START, &key_error);
-    header_rows = (key_error) ? 0 : key_int;
+    GError *key_error = nullptr;
+    bool error = false;
+    auto keyfile = gnc_state_get_current ();
+
+    header_rows = g_key_file_get_integer (keyfile, group.c_str(), CSV_SKIP_START, &key_error);
     error |= handle_load_error (&key_error, group);
 
-    key_int = g_key_file_get_integer (keyfile, group.c_str(), CSV_SKIP_END, &key_error);
-    footer_rows = (key_error) ? 0 : key_int;
+    footer_rows = g_key_file_get_integer (keyfile, group.c_str(), CSV_SKIP_END, &key_error);
     error |= handle_load_error (&key_error, group);
 
-    key_boolean = g_key_file_get_boolean (keyfile, group.c_str(), CSV_ALT_ROWS, &key_error);
-    skip_alt_rows = (key_error) ? false : key_boolean;
+    skip_alt_rows = g_key_file_get_boolean (keyfile, group.c_str(), CSV_ALT_ROWS, &key_error);
     error |= handle_load_error (&key_error, group);
 
-    key_boolean = g_key_file_get_boolean (keyfile, group.c_str(), CSV_MULTI_SPLIT, &key_error);
-    multi_split = (key_error) ? false : key_boolean;
+    multi_split = g_key_file_get_boolean (keyfile, group.c_str(), CSV_MULTI_SPLIT, &key_error);
     error |= handle_load_error (&key_error, group);
 
-    key_boolean = g_key_file_get_boolean (keyfile, group.c_str(), CSV_FORMAT, &key_error);
-    csv_format = (key_error) ? true : key_boolean;
+    csv_format = g_key_file_get_boolean (keyfile, group.c_str(), CSV_FORMAT, &key_error);
+    if (key_error) csv_format = true; // default to true, but above command will return false in case of error
     error |= handle_load_error (&key_error, group);
 
-    for (i = 0; i < SEP_NUM_OF_TYPES; i++)
+    for (uint i = 0; i < SEP_NUM_OF_TYPES; i++)
     {
         gchar *sep;
         sep = g_strdup_printf ("%s%d", CSV_SEP, i);
-        key_boolean = g_key_file_get_boolean (keyfile, group.c_str(), sep, &key_error);
-        separator[i] = (key_error) ? false : key_boolean;
+        separator[i] = g_key_file_get_boolean (keyfile, group.c_str(), sep, &key_error);
         error |= handle_load_error (&key_error, group);
         g_free (sep);
     }
 
-    key_boolean = g_key_file_get_boolean (keyfile, group.c_str(), CSV_CUSTOM, &key_error);
-    custom = (key_error) ? false : key_boolean;
+    custom = g_key_file_get_boolean (keyfile, group.c_str(), CSV_CUSTOM, &key_error);
     error |= handle_load_error (&key_error, group);
 
-    custom_entry = std::string(g_key_file_get_string (keyfile, group.c_str(), CSV_CUSTOM_ENTRY, &key_error));
+    gchar *key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_CUSTOM_ENTRY, &key_error);
+    custom_entry = key_char;
     error |= handle_load_error (&key_error, group);
+    if (key_char)
+        g_free (key_char);
 
-    key_int = g_key_file_get_integer (keyfile, group.c_str(), CSV_DATE, &key_error);
-    date_active = (key_error) ? 0 : key_int;
+    date_active = g_key_file_get_integer (keyfile, group.c_str(), CSV_DATE, &key_error);
     error |= handle_load_error (&key_error, group);
 
-    key_int = g_key_file_get_integer (keyfile, group.c_str(), CSV_CURRENCY, &key_error);
-    currency_active = (key_error) ? 0 : key_int;
+    currency_active = g_key_file_get_integer (keyfile, group.c_str(), CSV_CURRENCY, &key_error);
     error |= handle_load_error (&key_error, group);
 
     key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_ENCODING, &key_error);
     encoding = (key_error) ? "UTF-8" : key_char;
     error |= handle_load_error (&key_error, group);
-    g_free (key_char);
+    if (key_char)
+        g_free (key_char);
 
-    column_types.clear();
     key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_COL_TYPES, &key_error);
     error |= handle_load_error (&key_error, group);
     auto col_types_str = std::string { key_char };
-    g_free (key_char);
+    if (key_char)
+        g_free (key_char);
 
+    column_types.clear();
     if (!col_types_str.empty())
     {
         using Tokenizer = boost::tokenizer< boost::escaped_list_separator<char>>;
@@ -280,7 +270,9 @@ CsvTransSettings::save (const std::string& settings_name)
     // Do a test read of column types
     GError *key_error = nullptr;
     bool error = false;
-    std::string test_string = g_key_file_get_string (keyfile, group.c_str(), CSV_COL_TYPES, &key_error);
+    auto col_types_val = g_key_file_get_string (keyfile, group.c_str(), CSV_COL_TYPES, &key_error);
+    auto test_string = std::string{col_types_val};
+    g_free (col_types_val);
 
     if ((key_error) || (test_string != ss.str()))
     {

commit e8d24e193b65c77c814a25ffd65934c9d30d7067
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Thu Dec 8 16:51:39 2016 +0100

    Use std::vector to store column types, and a few other data type changes

diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index 4806592..277b133 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -47,7 +47,6 @@ extern "C"
 #include "gnc-state.h"
 
 #include "assistant-csv-trans-import.h"
-#include "gnc-csv-trans-settings.hpp"
 
 #include "import-account-matcher.h"
 #include "import-main-matcher.h"
@@ -57,6 +56,7 @@ extern "C"
 #include "go-charmap-sel.h"
 }
 
+#include "gnc-csv-trans-settings.hpp"
 #include "gnc-tx-import.hpp"
 #include "gnc-fw-tokenizer.hpp"
 #include "gnc-csv-tokenizer.hpp"
@@ -261,7 +261,7 @@ csv_import_trans_load_settings (CsvImportTrans *info)
     info->parse_data->currency_format = info->settings_data.currency_active;
     gtk_combo_box_set_active (GTK_COMBO_BOX(info->currency_format_combo), info->settings_data.currency_active);
     info->parse_data->convert_encoding (info->settings_data.encoding);
-    go_charmap_sel_set_encoding (info->encselector, info->settings_data.encoding);
+    go_charmap_sel_set_encoding (info->encselector, info->settings_data.encoding.c_str());
 
     try
     {
@@ -273,7 +273,7 @@ csv_import_trans_load_settings (CsvImportTrans *info)
                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->sep_buttons[i]), info->settings_data.separator[i]);
             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->custom_cbutton), info->settings_data.custom);
             if (info->settings_data.custom)
-                gtk_entry_set_text (GTK_ENTRY(info->custom_entry), info->settings_data.custom_entry);
+                gtk_entry_set_text (GTK_ENTRY(info->custom_entry), info->settings_data.custom_entry.c_str());
             else
                 gtk_entry_set_text (GTK_ENTRY(info->custom_entry), "");
 
@@ -303,53 +303,43 @@ csv_import_trans_load_settings (CsvImportTrans *info)
         PWARN("Got an error during file loading");
     }
 
-    // This section deals with the column types
-    if (info->settings_data.column_types)
-    {
-        GtkTreeModel *ctstore;
-        GtkTreeIter   iter;
-        gchar       **columns;
-        uint          i;
-        bool          error = false;
-
-        columns = g_strsplit (info->settings_data.column_types, ",", -1);
-
-        // ctstore contains the column types and their (translated) string representation appearing in the column types treeview.
-        ctstore = gtk_tree_view_get_model (info->ctreeview);
+    // Set column types on the import data table to what's in the loaded preset
+    // Note we can only set types for actual columns in the data. If the loaded
+    // preset has more column types, these will be discarded.
+    // First get an iterator for the first (and only) row of ctstore, which
+    // contains the column types and their (translated) string representation
+    // appearing in the column types treeview.
+    GtkTreeModel *ctstore = gtk_tree_view_get_model (info->ctreeview);
+    gtk_tree_model_get_iter_first (ctstore, &iter);
 
-        // Get an iterator for the first (and only) row.
-        gtk_tree_model_get_iter_first (ctstore, &iter);
+    bool          error = false;
+    for (uint i=0; i < info->settings_data.column_types.size() && i < info->parse_data->column_types.size(); i++)
+    {
+        auto col_type = GncTransPropType::NONE;
+        auto saved_col_type = info->settings_data.column_types.at(i);
 
-        for (i=0; columns[i] != NULL && i < info->parse_data->column_types.size(); i++)
+        if (saved_col_type >= GncTransPropType::NONE &&
+            saved_col_type <= GncTransPropType::TMEMO)
         {
-            auto col_type = GncTransPropType::NONE;
-            int saved_col_type = atoi (columns[i]);
-
-            if (saved_col_type >= static_cast<int>(GncTransPropType::NONE) &&
-                saved_col_type <= static_cast<int>(GncTransPropType::TMEMO))
-            {
-                col_type = static_cast<GncTransPropType>(saved_col_type);
-                info->parse_data->column_types.at(i) = col_type;
-                /* Set the column type. Store is arranged so that every two
-                 * columns is a pair of
-                 * - the column type as a user visible (translated) string
-                 * - the internal type for this column
-                 * So ctstore looks like:
-                 * col_type_str 0, col_type, col_type_str 1, col_type 1, ..., col_type_str ncols, col_type ncols. */
-                if ((2 * i + 1) < static_cast<uint>(gtk_tree_model_get_n_columns (ctstore)))
-                    gtk_list_store_set (GTK_LIST_STORE(ctstore), &iter,
-                            2 * i, _(gnc_csv_col_type_strs[col_type]),
-                            2 * i + 1, col_type,
-                            -1);
-            }
-            else
-                error = true;
+            col_type = saved_col_type;
+            info->parse_data->column_types.at(i) = col_type;
+            /* Set the column type. Store is arranged so that every two
+             * columns is a pair of
+             * - the column type as a user visible (translated) string
+             * - the internal type for this column
+             * So ctstore looks like:
+             * col_type_str 0, col_type, col_type_str 1, col_type 1, ..., col_type_str ncols, col_type ncols. */
+            if ((2 * i + 1) < static_cast<uint>(gtk_tree_model_get_n_columns (ctstore)))
+                gtk_list_store_set (GTK_LIST_STORE(ctstore), &iter,
+                        2 * i, _(gnc_csv_col_type_strs[col_type]),
+                        2 * i + 1, col_type,
+                        -1);
         }
-        if (error)
-            gnc_error_dialog (NULL, "%s", _("There was a problem with the column types, please review."));
-
-        g_strfreev (columns);
+        else
+            error = true;
     }
+    if (error)
+        gnc_error_dialog (NULL, "%s", _("There was a problem with the column types, please review."));
 }
 
 
@@ -538,7 +528,6 @@ csv_import_trans_save_settings_cb (GtkWidget *button, CsvImportTrans *info)
         GList        *column;
         GtkTreeModel *ctstore;
         GtkTreeIter   iter;
-        gchar        *details = NULL;
 
         /* This section deals with the header and rows */
         info->settings_data.header_rows = gtk_spin_button_get_value (GTK_SPIN_BUTTON(info->start_row_spin));
@@ -567,6 +556,7 @@ csv_import_trans_save_settings_cb (GtkWidget *button, CsvImportTrans *info)
         // Get an iterator for the first (and only) row.
         gtk_tree_model_get_iter_first (ctstore, &iter);
 
+        info->settings_data.column_types.clear();
         for (column = columns, i = 0; column; column = g_list_next (column), i++)
         {
             auto col_type = GncTransPropType::NONE;
@@ -579,23 +569,11 @@ csv_import_trans_save_settings_cb (GtkWidget *button, CsvImportTrans *info)
              * So ctstore looks like:
              * col_type_str 0, col_type, col_type_str 1, col_type 1, ..., col_type_str ncols, col_type ncols. */
             gtk_tree_model_get (ctstore, &iter, 2 * i + 1, &col_type, -1);
+            info->settings_data.column_types.push_back(col_type);
 
-            col_type_str = g_strdup_printf ("%i", static_cast<int>(col_type));
-            if (!details)
-                details = col_type_str;
-            else
-            {
-                gchar *details_prev = details;
-                details = g_strjoin (",", details_prev, col_type_str, NULL);
-                g_free (details_prev);
-                g_free (col_type_str);
-            }
         }
         g_list_free (columns);
 
-        info->settings_data.column_types = g_strdup (details);
-        g_free (details);
-
         /* Save the column widths in fixed mode */
         if (info->settings_data.csv_format)
             info->settings_data.column_widths = "5,10,15";
diff --git a/src/import-export/csv-imp/gnc-csv-trans-settings.cpp b/src/import-export/csv-imp/gnc-csv-trans-settings.cpp
index 522d1b2..e0c0bf6 100644
--- a/src/import-export/csv-imp/gnc-csv-trans-settings.cpp
+++ b/src/import-export/csv-imp/gnc-csv-trans-settings.cpp
@@ -36,7 +36,11 @@ extern "C"
 #include "gnc-state.h"
 }
 
+#include <sstream>
+#include <boost/tokenizer.hpp>
+
 const std::string csv_group_prefix{"CSV - "};
+const std::string no_settings{N_("No Settings")};
 #define CSV_NAME         "Name"
 #define CSV_FORMAT       "CsvFormat"
 #define CSV_ALT_ROWS     "AltRows"
@@ -72,7 +76,7 @@ CsvTransSettings::find (GtkTreeModel *settings_store)
     // Append the default entry
     GtkTreeIter iter;
     gtk_list_store_append (GTK_LIST_STORE(settings_store), &iter);
-    gtk_list_store_set (GTK_LIST_STORE(settings_store), &iter, SET_GROUP, NULL, SET_NAME, _("No Settings"), -1);
+    gtk_list_store_set (GTK_LIST_STORE(settings_store), &iter, SET_GROUP, NULL, SET_NAME, _(no_settings.c_str()), -1);
 
     // Search all Groups in the state key file for ones starting with prefix
     GKeyFile   *keyfile = gnc_state_get_current ();
@@ -141,7 +145,7 @@ CsvTransSettings::load (const std::string& group)
     GError     *key_error = NULL;
     bool        key_boolean = false;
     int         key_int = 0;
-    gchar      *key_char = NULL;
+    gchar      *key_char = nullptr;
     bool        error = false;
 
     // Get the Key file
@@ -181,7 +185,7 @@ CsvTransSettings::load (const std::string& group)
     custom = (key_error) ? false : key_boolean;
     error |= handle_load_error (&key_error, group);
 
-    custom_entry = g_key_file_get_string (keyfile, group.c_str(), CSV_CUSTOM_ENTRY, &key_error);
+    custom_entry = std::string(g_key_file_get_string (keyfile, group.c_str(), CSV_CUSTOM_ENTRY, &key_error));
     error |= handle_load_error (&key_error, group);
 
     key_int = g_key_file_get_integer (keyfile, group.c_str(), CSV_DATE, &key_error);
@@ -193,16 +197,35 @@ CsvTransSettings::load (const std::string& group)
     error |= handle_load_error (&key_error, group);
 
     key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_ENCODING, &key_error);
-    encoding = g_strdup((key_error) ? "UTF-8" : key_char);
+    encoding = (key_error) ? "UTF-8" : key_char;
     error |= handle_load_error (&key_error, group);
+    g_free (key_char);
 
-    column_types = g_key_file_get_string (keyfile, group.c_str(), CSV_COL_TYPES, &key_error);
+    column_types.clear();
+    key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_COL_TYPES, &key_error);
     error |= handle_load_error (&key_error, group);
+    auto col_types_str = std::string { key_char };
+    g_free (key_char);
+
+    if (!col_types_str.empty())
+    {
+        using Tokenizer = boost::tokenizer< boost::escaped_list_separator<char>>;
+        boost::escaped_list_separator<char> sep("\\", ",", "\"");
+        Tokenizer tok(col_types_str, sep);
+        for (auto col_type_str : tok)
+        {
+            auto col_type = std::stoi(col_type_str);
+            if (col_type >= static_cast<int>(GncTransPropType::NONE) &&
+                col_type <= static_cast<int>(GncTransPropType::SPLIT_PROPS))
+                column_types.push_back (static_cast<GncTransPropType>(col_type));
+            else
+                column_types.push_back (GncTransPropType::NONE);
+        }
+    }
 
     column_widths = g_key_file_get_string (keyfile, group.c_str(), CSV_COL_WIDTHS, &key_error);
     error |= handle_load_error (&key_error, group);
 
-    g_free (key_char);
     return error;
 }
 
@@ -238,19 +261,28 @@ CsvTransSettings::save (const std::string& settings_name)
     }
 
     g_key_file_set_boolean (keyfile, group.c_str(), CSV_CUSTOM, custom);
-    g_key_file_set_string (keyfile, group.c_str(), CSV_CUSTOM_ENTRY, custom_entry);
+    g_key_file_set_string (keyfile, group.c_str(), CSV_CUSTOM_ENTRY, custom_entry.c_str());
     g_key_file_set_integer (keyfile, group.c_str(), CSV_DATE, date_active);
     g_key_file_set_integer (keyfile, group.c_str(), CSV_CURRENCY, currency_active);
-    g_key_file_set_string (keyfile, group.c_str(), CSV_ENCODING, encoding);
-    g_key_file_set_string (keyfile, group.c_str(), CSV_COL_TYPES, column_types);
+    g_key_file_set_string (keyfile, group.c_str(), CSV_ENCODING, encoding.c_str());
+
+    std::stringstream ss;
+    for (auto col_type : column_types)
+    {
+        if (!ss.str().empty())
+            ss << ",";
+        ss << static_cast<uint>(col_type);
+    }
+
+    g_key_file_set_string (keyfile, group.c_str(), CSV_COL_TYPES, ss.str().c_str());
     g_key_file_set_string (keyfile, group.c_str(), CSV_COL_WIDTHS, column_widths);
 
     // Do a test read of column types
     GError *key_error = nullptr;
     bool error = false;
-    gchar *test_string = g_key_file_get_string (keyfile, group.c_str(), CSV_COL_TYPES, &key_error);
+    std::string test_string = g_key_file_get_string (keyfile, group.c_str(), CSV_COL_TYPES, &key_error);
 
-    if ((key_error) || (g_strcmp0 (test_string, column_types) != 0))
+    if ((key_error) || (test_string != ss.str()))
     {
         if (key_error)
         {
@@ -258,9 +290,8 @@ CsvTransSettings::save (const std::string& settings_name)
             g_error_free (key_error);
         }
         else
-            g_warning ("Error comparing group %s key %s: '%s' and '%s'", group.c_str(), CSV_COL_TYPES, test_string, group.c_str());
+            g_warning ("Error comparing group %s key %s: '%s' and '%s'", group.c_str(), CSV_COL_TYPES, test_string.c_str(), group.c_str());
         error = true;
     }
-    g_free (test_string);
     return error;
 }
diff --git a/src/import-export/csv-imp/gnc-csv-trans-settings.hpp b/src/import-export/csv-imp/gnc-csv-trans-settings.hpp
index 1849be4..6a447c9 100644
--- a/src/import-export/csv-imp/gnc-csv-trans-settings.hpp
+++ b/src/import-export/csv-imp/gnc-csv-trans-settings.hpp
@@ -32,6 +32,8 @@ extern "C" {
 }
 
 #include <string>
+#include <vector>
+#include "gnc-trans-props.hpp"
 
 /** Enumeration for separator checkbutton types. These are the
  *  different types of checkbuttons that the user can click to
@@ -47,8 +49,7 @@ struct CsvTransSettings
     CsvTransSettings() : header_rows{0}, footer_rows{0}, csv_format (true),
                     skip_alt_rows (false), multi_split (false),
                     encoding {"UTF-8"}, custom {false}, custom_entry {nullptr},
-                    date_active {0}, currency_active {0},
-                    column_types{nullptr}, column_widths{nullptr}
+                    date_active {0}, currency_active {0}, column_widths{nullptr}
                     {
                         for (uint i = 0; i < SEP_NUM_OF_TYPES; i++)
                         {
@@ -88,16 +89,16 @@ bool          csv_format;                   // CSV import Format
 bool          skip_alt_rows;                // Skip alternate rows
 bool          multi_split;                  // Assume multiple lines per transaction
 
-const gchar  *encoding;                     // File encoding
+std::string   encoding;                     // File encoding
 
 bool          separator[SEP_NUM_OF_TYPES];  // The separators
 
 bool          custom;                       // Custom entry set
-const gchar  *custom_entry;                 // Custom Entry
+std::string   custom_entry;                 // Custom Entry
 
 int           date_active;                  // Date Active id
 int           currency_active;              // Currency Active id
-const gchar  *column_types;                 // The Column types in order
+std::vector<GncTransPropType>  column_types;// The Column types in order
 const gchar  *column_widths;                // The Column widths
 
 };
diff --git a/src/import-export/csv-imp/gnc-trans-props.hpp b/src/import-export/csv-imp/gnc-trans-props.hpp
index 864cbf5..b18d671 100644
--- a/src/import-export/csv-imp/gnc-trans-props.hpp
+++ b/src/import-export/csv-imp/gnc-trans-props.hpp
@@ -31,6 +31,9 @@ extern "C" {
 #endif
 
 #include <glib/gi18n.h>
+
+#include "Account.h"
+#include "Transaction.h"
 }
 
 #include <string>

commit 7a69d552bd006786199e7a5e17672d2a09f7d1ec
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Mon Dec 5 17:42:55 2016 +0100

    Convert CsvTransSettings into a c++ class

diff --git a/po/POTFILES.in b/po/POTFILES.in
index 1d9b7c3..f9917f4 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -437,7 +437,7 @@ src/import-export/csv-imp/csv-fixed-trans-import.cpp
 src/import-export/csv-imp/gnc-csv-account-map.c
 src/import-export/csv-imp/gnc-csv-gnumeric-popup.c
 src/import-export/csv-imp/gnc-csv-tokenizer.cpp
-src/import-export/csv-imp/gnc-csv-trans-settings.c
+src/import-export/csv-imp/gnc-csv-trans-settings.cpp
 src/import-export/csv-imp/gnc-dummy-tokenizer.cpp
 src/import-export/csv-imp/gnc-fw-tokenizer.cpp
 src/import-export/csv-imp/gncmod-csv-import.c
diff --git a/src/import-export/csv-imp/CMakeLists.txt b/src/import-export/csv-imp/CMakeLists.txt
index 2a978d3..1a54510 100644
--- a/src/import-export/csv-imp/CMakeLists.txt
+++ b/src/import-export/csv-imp/CMakeLists.txt
@@ -12,7 +12,7 @@ SET(csv_import_SOURCES
   gnc-csv-account-map.c
   gnc-csv-gnumeric-popup.c
   gnc-csv-tokenizer.cpp
-  gnc-csv-trans-settings.c
+  gnc-csv-trans-settings.cpp
   gnc-dummy-tokenizer.cpp
   gnc-fw-tokenizer.cpp
   gnc-tokenizer.cpp
@@ -36,7 +36,7 @@ SET(csv_import_noinst_HEADERS
   gnc-csv-account-map.h
   gnc-csv-gnumeric-popup.h
   gnc-csv-tokenizer.hpp
-  gnc-csv-trans-settings.h
+  gnc-csv-trans-settings.hpp
   gnc-dummy-tokenizer.hpp
   gnc-fw-tokenizer.hpp
   gnc-tokenizer.hpp
diff --git a/src/import-export/csv-imp/Makefile.am b/src/import-export/csv-imp/Makefile.am
index 66b97aa..6068ec9 100644
--- a/src/import-export/csv-imp/Makefile.am
+++ b/src/import-export/csv-imp/Makefile.am
@@ -18,7 +18,7 @@ libgncmod_csv_import_la_SOURCES = \
   gnc-tokenizer.cpp \
   gnc-tx-import.cpp \
   gnc-trans-props.cpp \
-  gnc-csv-trans-settings.c
+  gnc-csv-trans-settings.cpp
 
 noinst_HEADERS = \
   assistant-csv-account-import.h \
@@ -35,7 +35,7 @@ noinst_HEADERS = \
   gnc-tokenizer.hpp \
   gnc-tx-import.hpp \
   gnc-trans-props.hpp \
-  gnc-csv-trans-settings.h
+  gnc-csv-trans-settings.hpp
 
 libgncmod_csv_import_la_LDFLAGS = -avoid-version
 
diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index dc89c0c..4806592 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -47,7 +47,7 @@ extern "C"
 #include "gnc-state.h"
 
 #include "assistant-csv-trans-import.h"
-#include "gnc-csv-trans-settings.h"
+#include "gnc-csv-trans-settings.hpp"
 
 #include "import-account-matcher.h"
 #include "import-main-matcher.h"
@@ -94,7 +94,7 @@ typedef struct
     int              home_account_number;           /**< The number of unique home account strings */
 
     GncTxImport     *parse_data;                    /**< The actual data we are previewing */
-    CsvSettings     *settings_data;                 /**< The settings to be saved and loaded */
+    CsvTransSettings settings_data;                 /**< The settings to be saved and loaded */
     GOCharmapSel    *encselector;                   /**< The widget for selecting the encoding */
     GtkCheckButton  *sep_buttons[SEP_NUM_OF_TYPES]; /**< Checkbuttons for common separators */
     GtkCheckButton  *custom_cbutton;                /**< The checkbutton for a custom separator */
@@ -203,10 +203,8 @@ csv_import_trans_load_settings (CsvImportTrans *info)
     if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX(info->settings_combo), &iter))
         return;
 
-    GtkTreeModel *model;
     gchar *group = NULL;
-
-    model = gtk_combo_box_get_model (GTK_COMBO_BOX(info->settings_combo));
+    GtkTreeModel *model = gtk_combo_box_get_model (GTK_COMBO_BOX(info->settings_combo));
     gtk_tree_model_get (model, &iter, SET_GROUP, &group, -1);
 
     // Test for default selection, return
@@ -214,7 +212,9 @@ csv_import_trans_load_settings (CsvImportTrans *info)
         return;
 
     // Load the settings from the keyfile
-    if (gnc_csv_trans_load_settings (info->settings_data, group))
+    auto group_str = std::string{group};
+    g_free (group);
+    if (info->settings_data.load (group_str))
     {
         const gchar *title = _("Load the Import Settings.");
         GtkWidget *dialog = gtk_message_dialog_new (GTK_WINDOW(info->window),
@@ -227,54 +227,53 @@ csv_import_trans_load_settings (CsvImportTrans *info)
         gtk_dialog_run (GTK_DIALOG (dialog));
         gtk_widget_destroy (dialog);
     }
-    g_free (group);
 
     // Set start row
-    info->parse_data->skip_start_lines = info->settings_data->header_rows;
+    info->parse_data->skip_start_lines = info->settings_data.header_rows;
     GtkAdjustment *adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(info->start_row_spin));
     gtk_adjustment_set_upper (adj, info->parse_data->parsed_lines.size());
     gtk_spin_button_set_value (GTK_SPIN_BUTTON(info->start_row_spin),
-            info->settings_data->header_rows);
+            info->settings_data.header_rows);
 
     // Set end row
-    info->parse_data->skip_end_lines = info->settings_data->footer_rows;
+    info->parse_data->skip_end_lines = info->settings_data.footer_rows;
     adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(info->end_row_spin));
     gtk_adjustment_set_upper (adj, info->parse_data->parsed_lines.size());
     gtk_spin_button_set_value (GTK_SPIN_BUTTON(info->end_row_spin),
-            info->settings_data->footer_rows);
+            info->settings_data.footer_rows);
 
     // Set Alternate rows
-    info->parse_data->skip_alt_lines = info->settings_data->skip_alt_rows;
-    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->skip_rows), info->settings_data->skip_alt_rows);
+    info->parse_data->skip_alt_lines = info->settings_data.skip_alt_rows;
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->skip_rows), info->settings_data.skip_alt_rows);
 
     // Set Multi-split indicator
-    info->parse_data->multi_split = info->settings_data->multi_split;
-    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->multi_split_cbutton), info->settings_data->multi_split);
+    info->parse_data->multi_split = info->settings_data.multi_split;
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->multi_split_cbutton), info->settings_data.multi_split);
 
     // Set Import Format
-    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->csv_button), info->settings_data->csv_format);
-    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->fixed_button), !info->settings_data->csv_format);
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->csv_button), info->settings_data.csv_format);
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->fixed_button), !info->settings_data.csv_format);
 
     // This section deals with the combo's and character encoding
-    info->parse_data->date_format = info->settings_data->date_active;
-    gtk_combo_box_set_active (GTK_COMBO_BOX(info->date_format_combo), info->settings_data->date_active);
+    info->parse_data->date_format = info->settings_data.date_active;
+    gtk_combo_box_set_active (GTK_COMBO_BOX(info->date_format_combo), info->settings_data.date_active);
 
-    info->parse_data->currency_format = info->settings_data->currency_active;
-    gtk_combo_box_set_active (GTK_COMBO_BOX(info->currency_format_combo), info->settings_data->currency_active);
-    info->parse_data->convert_encoding (info->settings_data->encoding);
-    go_charmap_sel_set_encoding (info->encselector, info->settings_data->encoding);
+    info->parse_data->currency_format = info->settings_data.currency_active;
+    gtk_combo_box_set_active (GTK_COMBO_BOX(info->currency_format_combo), info->settings_data.currency_active);
+    info->parse_data->convert_encoding (info->settings_data.encoding);
+    go_charmap_sel_set_encoding (info->encselector, info->settings_data.encoding);
 
     try
     {
-        if (info->settings_data->csv_format)
+        if (info->settings_data.csv_format)
         {
             // Handle separators, only relevant if the file format is csv
             info->parse_data->file_format (GncImpFileFormat::CSV);
             for (int i = 0; i < SEP_NUM_OF_TYPES; i++)
-                gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->sep_buttons[i]), info->settings_data->separator[i]);
-            gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->custom_cbutton), info->settings_data->custom);
-            if (info->settings_data->custom)
-                gtk_entry_set_text (GTK_ENTRY(info->custom_entry), info->settings_data->custom_entry);
+                gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->sep_buttons[i]), info->settings_data.separator[i]);
+            gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->custom_cbutton), info->settings_data.custom);
+            if (info->settings_data.custom)
+                gtk_entry_set_text (GTK_ENTRY(info->custom_entry), info->settings_data.custom_entry);
             else
                 gtk_entry_set_text (GTK_ENTRY(info->custom_entry), "");
 
@@ -285,8 +284,8 @@ csv_import_trans_load_settings (CsvImportTrans *info)
             // Handle column widths, only relevant if the file format is fixed width
             info->parse_data->file_format (GncImpFileFormat::FIXED_WIDTH);
             GncFwTokenizer *fwtok = dynamic_cast<GncFwTokenizer*>(info->parse_data->tokenizer.get());
-            if (info->settings_data->column_widths, NULL)
-                fwtok->cols_from_string (std::string(info->settings_data->column_widths));
+            if (info->settings_data.column_widths, NULL)
+                fwtok->cols_from_string (std::string(info->settings_data.column_widths));
 
             info->parse_data->tokenize (false);
             gnc_csv_preview_update_assist (info);
@@ -305,7 +304,7 @@ csv_import_trans_load_settings (CsvImportTrans *info)
     }
 
     // This section deals with the column types
-    if (info->settings_data->column_types)
+    if (info->settings_data.column_types)
     {
         GtkTreeModel *ctstore;
         GtkTreeIter   iter;
@@ -313,7 +312,7 @@ csv_import_trans_load_settings (CsvImportTrans *info)
         uint          i;
         bool          error = false;
 
-        columns = g_strsplit (info->settings_data->column_types, ",", -1);
+        columns = g_strsplit (info->settings_data.column_types, ",", -1);
 
         // ctstore contains the column types and their (translated) string representation appearing in the column types treeview.
         ctstore = gtk_tree_view_get_model (info->ctreeview);
@@ -406,7 +405,7 @@ csv_import_trans_delete_settings_cb (GtkWidget *button, CsvImportTrans *info)
             if (response == GTK_RESPONSE_OK)
             {
                 g_key_file_remove_group (keyfile, group, NULL);
-                gnc_csv_trans_find_settings (model);
+                info->settings_data.find (model);
                 gtk_combo_box_set_active (GTK_COMBO_BOX(info->settings_combo), 0); // Default
                 gnc_csv_reset_preview_setting (info, false); // Reset the widgets
             }
@@ -542,24 +541,24 @@ csv_import_trans_save_settings_cb (GtkWidget *button, CsvImportTrans *info)
         gchar        *details = NULL;
 
         /* This section deals with the header and rows */
-        info->settings_data->header_rows = gtk_spin_button_get_value (GTK_SPIN_BUTTON(info->start_row_spin));
-        info->settings_data->footer_rows = gtk_spin_button_get_value (GTK_SPIN_BUTTON(info->end_row_spin));
-        info->settings_data->skip_alt_rows = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(info->skip_rows));
-        info->settings_data->csv_format = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(info->csv_button));
+        info->settings_data.header_rows = gtk_spin_button_get_value (GTK_SPIN_BUTTON(info->start_row_spin));
+        info->settings_data.footer_rows = gtk_spin_button_get_value (GTK_SPIN_BUTTON(info->end_row_spin));
+        info->settings_data.skip_alt_rows = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(info->skip_rows));
+        info->settings_data.csv_format = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(info->csv_button));
 
         /* This Section deals with the separators */
         for (i = 0; i < SEP_NUM_OF_TYPES; i++)
         {
-            info->settings_data->separator[i] = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(info->sep_buttons[i]));
+            info->settings_data.separator[i] = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(info->sep_buttons[i]));
         }
-        info->settings_data->custom = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(info->custom_cbutton));
-        info->settings_data->custom_entry = gtk_entry_get_text (GTK_ENTRY(info->custom_entry));
+        info->settings_data.custom = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(info->custom_cbutton));
+        info->settings_data.custom_entry = gtk_entry_get_text (GTK_ENTRY(info->custom_entry));
 
         /* This section deals with the combo's and character encoding */
-        info->settings_data->date_active = gtk_combo_box_get_active (GTK_COMBO_BOX(info->date_format_combo));
-        info->settings_data->currency_active = gtk_combo_box_get_active (GTK_COMBO_BOX(info->currency_format_combo));
-        info->settings_data->encoding = go_charmap_sel_get_encoding (info->encselector);
-        info->settings_data->multi_split = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(info->multi_split_cbutton));
+        info->settings_data.date_active = gtk_combo_box_get_active (GTK_COMBO_BOX(info->date_format_combo));
+        info->settings_data.currency_active = gtk_combo_box_get_active (GTK_COMBO_BOX(info->currency_format_combo));
+        info->settings_data.encoding = go_charmap_sel_get_encoding (info->encselector);
+        info->settings_data.multi_split = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(info->multi_split_cbutton));
 
         /* This section deals with the Treeview column names */
         columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(info->ctreeview));
@@ -594,20 +593,20 @@ csv_import_trans_save_settings_cb (GtkWidget *button, CsvImportTrans *info)
         }
         g_list_free (columns);
 
-        info->settings_data->column_types = g_strdup (details);
+        info->settings_data.column_types = g_strdup (details);
         g_free (details);
 
         /* Save the column widths in fixed mode */
-        if (info->settings_data->csv_format)
-            info->settings_data->column_widths = "5,10,15";
+        if (info->settings_data.csv_format)
+            info->settings_data.column_widths = "5,10,15";
         else
         {
             GncFwTokenizer *fwtok = dynamic_cast<GncFwTokenizer*>(info->parse_data->tokenizer.get());
-            info->settings_data->column_widths = g_strdup (fwtok->cols_to_string().c_str());
+            info->settings_data.column_widths = g_strdup (fwtok->cols_to_string().c_str());
         }
 
         // Save the settings
-        if (!gnc_csv_trans_save_settings (info->settings_data, g_strdup (entry_text)))
+        if (!info->settings_data.save (g_strdup (entry_text)))
         {
             GtkTreeModel *model;
             GtkTreeIter   iter;
@@ -625,7 +624,7 @@ csv_import_trans_save_settings_cb (GtkWidget *button, CsvImportTrans *info)
 
             // Update the settings store
             model = gtk_combo_box_get_model (GTK_COMBO_BOX(info->settings_combo));
-            gnc_csv_trans_find_settings (model);
+            info->settings_data.find (model);
 
             // Get the first entry in model
             valid = gtk_tree_model_get_iter_first (model, &iter);
@@ -731,7 +730,7 @@ csv_import_trans_file_chooser_confirm_cb (GtkWidget *button, CsvImportTrans *inf
 
     /* Get settings store and populate */
     GtkTreeModel   *settings_store = gtk_combo_box_get_model (GTK_COMBO_BOX(info->settings_combo));
-    gnc_csv_trans_find_settings (settings_store);
+    info->settings_data.find (settings_store);
     gtk_combo_box_set_active (GTK_COMBO_BOX(info->settings_combo), 0);
 
     gtk_assistant_set_page_complete (assistant, page, TRUE);
@@ -1922,9 +1921,6 @@ void load_settings (CsvImportTrans *info)
     if (!info->error_text.empty())
         info->error_text.clear();
 
-    /* Init Settings data. */
-    info->settings_data = gnc_csv_trans_new_settings_data();
-
     /* The default directory for the user to select files. */
     info->starting_dir = gnc_get_default_directory (GNC_PREFS_GROUP);
 }
@@ -2692,9 +2688,6 @@ csv_import_trans_close_handler (gpointer user_data)
     if (!(info->parse_data == NULL))
         delete info->parse_data;
 
-    if (!(info->settings_data == NULL))
-        gnc_csv_trans_settings_data_free (info->settings_data);
-
     if (!(info->account_picker == NULL))
         info->account_picker = NULL;
 
diff --git a/src/import-export/csv-imp/gnc-csv-trans-settings.c b/src/import-export/csv-imp/gnc-csv-trans-settings.c
deleted file mode 100644
index c4d909e..0000000
--- a/src/import-export/csv-imp/gnc-csv-trans-settings.c
+++ /dev/null
@@ -1,390 +0,0 @@
-/*******************************************************************\
- * gnc-csv-trans-settings.c -- Save and Load CSV Import Settings    *
- *                                                                  *
- * Copyright (C) 2014 Robert Fewell                                 *
- *                                                                  *
- * 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 2 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, contact:                        *
- *                                                                  *
- * Free Software Foundation           Voice:  +1-617-542-5942       *
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
- * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
-\********************************************************************/
-/** @file gnc-csv-trans-settings.c
-    @brief CSV Import Settings
-    @author Copyright (c) 2014 Robert Fewell
-*/
-#include "config.h"
-
-#include <gtk/gtk.h>
-#include <glib/gi18n.h>
-
-#include "gnc-state.h"
-#include "gnc-csv-trans-settings.h"
-
-#define CSV_GROUP_PREFIX "CSV - "
-#define CSV_NAME         "Name"
-#define CSV_FORMAT       "CsvFormat"
-#define CSV_ALT_ROWS     "AltRows"
-#define CSV_SKIP_START   "SkipStartRows"
-#define CSV_SKIP_END     "SkipEndRows"
-#define CSV_MULTI_SPLIT  "MultiSplit"
-
-/* The following two key names are only used by gnucash 2.6
- * They have been superseded by CSV_SKIP_START and CSV_SKIP_END.
- */
-#define CSV_START_ROW    "StartRow"
-#define CSV_END_ROWS     "EndRows"
-
-#define CSV_SEP          "Separator"
-
-#define CSV_CUSTOM       "Custom"
-#define CSV_CUSTOM_ENTRY "CustomEntry"
-
-#define CSV_DATE         "DateActive"
-#define CSV_CURRENCY     "CurrencyActive"
-
-#define CSV_ENCODING     "Encoding"
-#define CSV_COL_TYPES    "ColumnTypes"
-#define CSV_COL_WIDTHS   "ColumnWidths"
-
-
-/**************************************************
- * gnc_csv_trans_new_settings_data
- *
- * Create CsvSettings structure and set defaults 
- **************************************************/
-CsvSettings * gnc_csv_trans_new_settings_data (void)
-{
-    CsvSettings* settings_data = g_new (CsvSettings, 1);
-    int i;
-
-    settings_data->header_rows = 1;
-    settings_data->skip_alt_rows = FALSE;
-    settings_data->multi_split = FALSE;
-    settings_data->csv_format = TRUE;
-
-    settings_data->encoding = "UTF-8";
-
-    for (i = 0; i < SEP_NUM_OF_TYPES; i++)
-    {
-        settings_data->separator[i] = FALSE;
-    }
-
-    settings_data->custom = FALSE;
-    settings_data->date_active = 0;
-    settings_data->currency_active = 0;
-
-    return settings_data;
-}
-
-
-/**************************************************
- * gnc_csv_trans_settings_data_free
- *
- * settings_data whose memory will be freed
- **************************************************/
-void gnc_csv_trans_settings_data_free (CsvSettings* settings_data)
-{
-    /* All non-NULL pointers that have been initialized and must be freed. */
-    g_free (settings_data);
-}
-
-
-/**************************************************
- * gnc_csv_trans_find_settings
- *
- * find the setting entries in a key file
- **************************************************/
-void
-gnc_csv_trans_find_settings (GtkTreeModel *settings_store)
-{
-    GtkTreeIter iter;
-    GKeyFile   *keyfile;
-    gchar     **groups = NULL;
-    gint        i;
-    gsize       grouplength;
-    GError     *key_error = NULL;
-
-    // Get the Key file
-    keyfile = gnc_state_get_current ();
-
-    // Find all Groups
-    groups = g_key_file_get_groups (keyfile, &grouplength);
-
-    // Clear the list store
-    gtk_list_store_clear (GTK_LIST_STORE(settings_store));
-
-    // Append the default entry
-    gtk_list_store_append (GTK_LIST_STORE(settings_store), &iter);
-    gtk_list_store_set (GTK_LIST_STORE(settings_store), &iter, SET_GROUP, NULL, SET_NAME, _("No Settings"), -1);
-
-    // Search all Groups for ones starting with prefix
-    for (i=0; i < grouplength; i++)
-    {
-        if (g_str_has_prefix (groups[i], CSV_GROUP_PREFIX))
-        {
-            gchar *name = g_key_file_get_string (keyfile, groups[i], CSV_NAME, &key_error);
-
-            if (key_error == NULL)
-            {
-                gtk_list_store_append (GTK_LIST_STORE(settings_store), &iter);
-                gtk_list_store_set (GTK_LIST_STORE(settings_store), &iter, SET_GROUP, groups[i], SET_NAME, name, -1);
-            }
-            else
-            {
-                g_warning ("Error reading group '%s' name '%s': %s", groups[i], CSV_NAME, key_error->message);
-                g_clear_error (&key_error);
-            }
-            g_free (name);
-        }
-    }
-    // free the strings
-    g_strfreev (groups);
-}
-
-
-/**************************************************
- * handle_load_error
- *
- * record possible errors in the log file
- * ignore key-not-found errors though. We'll just
- * use a default value and go on.
- **************************************************/
-static gboolean
-handle_load_error (GError **key_error, gchar *group)
-{
-    if (!*key_error)
-        return FALSE;
-
-    if ((*key_error)->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND)
-    {
-        g_clear_error (key_error);
-        return FALSE;
-    }
-
-    g_warning ("Error reading group '%s' : %s", group, (*key_error)->message);
-    g_clear_error (key_error);
-    return TRUE;
-}
-
-/**************************************************
- * gnc_csv_trans_load_settings
- *
- * load the settings from a key file
- **************************************************/
-gboolean
-gnc_csv_trans_load_settings (CsvSettings *settings_data, gchar *group)
-{
-    GKeyFile   *keyfile;
-    gint        i;
-    GError     *key_error = NULL;
-    gboolean    key_boolean = FALSE;
-    int         key_int = 0;
-    gchar      *key_char = NULL;
-    gboolean    error = FALSE;
-
-    // Get the Key file
-    keyfile = gnc_state_get_current ();
-
-    key_int = g_key_file_get_integer (keyfile, group, CSV_SKIP_START, &key_error);
-    settings_data->header_rows = (key_error) ? 0 : key_int;
-    if (key_error)
-    {
-
-       /* If the key was not found (in contrast to failing to interpret its value)
-        * perhaps the file still uses the 2.6 key format. Let's check */
-       gboolean tmp_err = handle_load_error (&key_error, group);
-       if (!tmp_err)
-       {
-           key_int = g_key_file_get_integer (keyfile, group, CSV_START_ROW, &key_error);
-           settings_data->header_rows = (key_error) ? 0 : key_int - 1; // Old key was 1-based, new key is 0-based !
-           error |= handle_load_error (&key_error, group);
-       }
-       else
-           error |= tmp_err;
-
-    }
-
-    key_int = g_key_file_get_integer (keyfile, group, CSV_SKIP_END, &key_error);
-    settings_data->footer_rows = (key_error) ? 0 : key_int;
-    if (key_error)
-    {
-       /* If the key was not found (in contrast to failing to interpret its value)
-        * perhaps the file still uses the 2.6 key format. Let's check */
-       gboolean tmp_err = handle_load_error (&key_error, group);
-       if (!tmp_err)
-       {
-           key_int = g_key_file_get_integer (keyfile, group, CSV_END_ROWS, &key_error);
-           settings_data->footer_rows = (key_error) ? 0 : key_int; // Old key and new key are both 0-based !
-           error |= handle_load_error (&key_error, group);
-       }
-       else
-           error |= tmp_err;
-    }
-
-    key_boolean = g_key_file_get_boolean (keyfile, group, CSV_ALT_ROWS, &key_error);
-    settings_data->skip_alt_rows = (key_error) ? FALSE : key_boolean;
-    error |= handle_load_error (&key_error, group);
-
-    key_boolean = g_key_file_get_boolean (keyfile, group, CSV_MULTI_SPLIT, &key_error);
-    settings_data->multi_split = (key_error) ? FALSE : key_boolean;
-    error |= handle_load_error (&key_error, group);
-
-    key_boolean = g_key_file_get_boolean (keyfile, group, CSV_FORMAT, &key_error);
-    settings_data->csv_format = (key_error) ? TRUE : key_boolean;
-    error |= handle_load_error (&key_error, group);
-
-    for (i = 0; i < SEP_NUM_OF_TYPES; i++)
-    {
-        gchar *sep;
-        sep = g_strdup_printf ("%s%d", CSV_SEP, i);
-        key_boolean = g_key_file_get_boolean (keyfile, group, sep, &key_error);
-        settings_data->separator[i] = (key_error) ? FALSE : key_boolean;
-        error |= handle_load_error (&key_error, group);
-        g_free (sep);
-    }
-
-    key_boolean = g_key_file_get_boolean (keyfile, group, CSV_CUSTOM, &key_error);
-    settings_data->custom = (key_error) ? FALSE : key_boolean;
-    error |= handle_load_error (&key_error, group);
-
-    settings_data->custom_entry = g_key_file_get_string (keyfile, group, CSV_CUSTOM_ENTRY, &key_error);
-    error |= handle_load_error (&key_error, group);
-
-    key_int = g_key_file_get_integer (keyfile, group, CSV_DATE, &key_error);
-    settings_data->date_active = (key_error) ? 0 : key_int;
-    error |= handle_load_error (&key_error, group);
-
-    key_int = g_key_file_get_integer (keyfile, group, CSV_CURRENCY, &key_error);
-    settings_data->currency_active = (key_error) ? 0 : key_int;
-    error |= handle_load_error (&key_error, group);
-
-    key_char = g_key_file_get_string (keyfile, group, CSV_ENCODING, &key_error);
-    settings_data->encoding = g_strdup((key_error) ? "UTF-8" : key_char);
-    error |= handle_load_error (&key_error, group);
-
-    settings_data->column_types = g_key_file_get_string (keyfile, group, CSV_COL_TYPES, &key_error);
-    error |= handle_load_error (&key_error, group);
-
-    settings_data->column_widths = g_key_file_get_string (keyfile, group, CSV_COL_WIDTHS, &key_error);
-    error |= handle_load_error (&key_error, group);
-
-    g_free (key_char);
-    return error;
-}
-
-
-/**************************************************
- * gnc_csv_trans_save_settings
- *
- * save settings to a key file
- **************************************************/
-gboolean
-gnc_csv_trans_save_settings (CsvSettings *settings_data, gchar *settings_name)
-{
-    GKeyFile   *keyfile;
-    gchar     **groups = NULL;
-    gint        i;
-    gsize       grouplenght;
-    gchar      *group = NULL;
-    gchar      *test_string = NULL;
-    GError     *key_error = NULL;
-    gboolean    error = FALSE;
-
-    // Get the Key file
-    keyfile = gnc_state_get_current ();
-
-    // Find all Groups
-    groups = g_key_file_get_groups (keyfile, &grouplenght);
-
-    // Search all Groups for ones starting with prefix
-    for (i=0; i < grouplenght; i++)
-    {
-        if (g_str_has_prefix (groups[i], CSV_GROUP_PREFIX))
-        {
-            gchar *name = g_key_file_get_string (keyfile, groups[i], CSV_NAME, NULL);
-
-            if (g_strcmp0 (name, settings_name) == 0)
-                group = g_strdup (groups[i]);
-
-            g_free (name);
-        }
-    }
-
-    // group is NULL, saving to a new group, create a guid 
-    if (g_strcmp0 (group, NULL) == 0)
-    {
-        GncGUID *settings_guid;
-        gchar *string_guid;
-
-        settings_guid = guid_new ();
-        string_guid = guid_to_string (settings_guid);
-
-        group = g_strconcat (CSV_GROUP_PREFIX, string_guid, NULL);
-
-        g_free (string_guid);
-        guid_free (settings_guid);
-    }
-
-    // Start Saving the settings
-    g_key_file_set_string (keyfile, group, CSV_NAME, settings_name);
-
-    g_key_file_set_boolean (keyfile, group, CSV_MULTI_SPLIT, settings_data->multi_split);
-    g_key_file_set_integer (keyfile, group, CSV_SKIP_START, settings_data->header_rows);
-    g_key_file_set_integer (keyfile, group, CSV_SKIP_END, settings_data->footer_rows);
-    g_key_file_set_boolean (keyfile, group, CSV_ALT_ROWS, settings_data->skip_alt_rows);
-    g_key_file_set_boolean (keyfile, group, CSV_FORMAT, settings_data->csv_format);
-
-    for (i = 0; i < SEP_NUM_OF_TYPES; i++)
-    {
-        gchar *sep;
-        sep = g_strdup_printf ("%s%d", CSV_SEP, i);
-        g_key_file_set_boolean (keyfile, group, sep, settings_data->separator[i]);
-        g_free (sep);
-    }
-
-    g_key_file_set_boolean (keyfile, group, CSV_CUSTOM, settings_data->custom);
-    g_key_file_set_string (keyfile, group, CSV_CUSTOM_ENTRY, settings_data->custom_entry);
-
-    g_key_file_set_integer (keyfile, group, CSV_DATE, settings_data->date_active);
-    g_key_file_set_integer (keyfile, group, CSV_CURRENCY, settings_data->currency_active);
-
-    g_key_file_set_string (keyfile, group, CSV_ENCODING, settings_data->encoding);
-
-    g_key_file_set_string (keyfile, group, CSV_COL_TYPES, settings_data->column_types);
-
-    g_key_file_set_string (keyfile, group, CSV_COL_WIDTHS, settings_data->column_widths);
-
-    // free the strings
-    g_free (settings_name);
-    g_strfreev (groups);
-
-    // Do a test read of column types
-    test_string = g_key_file_get_string (keyfile, group, CSV_COL_TYPES, &key_error);
-
-    if ((key_error) || (g_strcmp0 (test_string, settings_data->column_types) != 0))
-    {
-        if (key_error)
-        {
-            g_warning ("Error reading group %s key %s: %s", group, CSV_COL_TYPES, key_error->message);
-            g_error_free (key_error);
-        }
-        else
-            g_warning ("Error comparing group %s key %s: '%s' and '%s'", group, CSV_COL_TYPES, test_string, group);
-        error = TRUE;
-    }
-    g_free (group);
-    g_free (test_string);
-    return error;
-}
diff --git a/src/import-export/csv-imp/gnc-csv-trans-settings.cpp b/src/import-export/csv-imp/gnc-csv-trans-settings.cpp
new file mode 100644
index 0000000..522d1b2
--- /dev/null
+++ b/src/import-export/csv-imp/gnc-csv-trans-settings.cpp
@@ -0,0 +1,266 @@
+/*******************************************************************\
+ * gnc-csv-trans-settings.c -- Save and Load CSV Import Settings    *
+ *                                                                  *
+ * Copyright (C) 2014 Robert Fewell                                 *
+ *                                                                  *
+ * 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 2 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, contact:                        *
+ *                                                                  *
+ * Free Software Foundation           Voice:  +1-617-542-5942       *
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
+\********************************************************************/
+/** @file gnc-csv-trans-settings.c
+    @brief CSV Import Settings
+    @author Copyright (c) 2014 Robert Fewell
+*/
+#include "gnc-csv-trans-settings.hpp"
+
+extern "C"
+{
+#include "config.h"
+
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+
+#include "gnc-state.h"
+}
+
+const std::string csv_group_prefix{"CSV - "};
+#define CSV_NAME         "Name"
+#define CSV_FORMAT       "CsvFormat"
+#define CSV_ALT_ROWS     "AltRows"
+#define CSV_SKIP_START   "SkipStartRows"
+#define CSV_SKIP_END     "SkipEndRows"
+#define CSV_MULTI_SPLIT  "MultiSplit"
+
+#define CSV_SEP          "Separator"
+
+#define CSV_CUSTOM       "Custom"
+#define CSV_CUSTOM_ENTRY "CustomEntry"
+
+#define CSV_DATE         "DateActive"
+#define CSV_CURRENCY     "CurrencyActive"
+
+#define CSV_ENCODING     "Encoding"
+#define CSV_COL_TYPES    "ColumnTypes"
+#define CSV_COL_WIDTHS   "ColumnWidths"
+
+
+/**************************************************
+ * find
+ *
+ * find all settings entries in the state key file
+ **************************************************/
+void
+CsvTransSettings::find (GtkTreeModel *settings_store)
+{
+
+    // Clear the list store
+    gtk_list_store_clear (GTK_LIST_STORE(settings_store));
+
+    // Append the default entry
+    GtkTreeIter iter;
+    gtk_list_store_append (GTK_LIST_STORE(settings_store), &iter);
+    gtk_list_store_set (GTK_LIST_STORE(settings_store), &iter, SET_GROUP, NULL, SET_NAME, _("No Settings"), -1);
+
+    // Search all Groups in the state key file for ones starting with prefix
+    GKeyFile   *keyfile = gnc_state_get_current ();
+    gsize grouplength;
+    gchar **groups = g_key_file_get_groups (keyfile, &grouplength);
+
+    for (gsize i=0; i < grouplength; i++)
+    {
+        if (g_str_has_prefix (groups[i], csv_group_prefix.c_str()))
+        {
+            GError *key_error = nullptr;
+            gchar *name = g_key_file_get_string (keyfile, groups[i], CSV_NAME, &key_error);
+
+            if (!key_error)
+            {
+                gtk_list_store_append (GTK_LIST_STORE(settings_store), &iter);
+                gtk_list_store_set (GTK_LIST_STORE(settings_store), &iter, SET_GROUP, groups[i], SET_NAME, name, -1);
+            }
+            else
+            {
+                g_warning ("Error reading group '%s' name '%s': %s", groups[i], CSV_NAME, key_error->message);
+                g_clear_error (&key_error);
+            }
+            g_free (name);
+        }
+    }
+    // free the strings
+    g_strfreev (groups);
+}
+
+
+/**************************************************
+ * handle_load_error
+ *
+ * record possible errors in the log file
+ * ignore key-not-found errors though. We'll just
+ * use a default value and go on.
+ **************************************************/
+static bool
+handle_load_error (GError **key_error, const std::string& group)
+{
+    if (!*key_error)
+        return false;
+
+    if ((*key_error)->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND)
+    {
+        g_clear_error (key_error);
+        return false;
+    }
+
+    g_warning ("Error reading group '%s' : %s", group.c_str(), (*key_error)->message);
+    g_clear_error (key_error);
+    return true;
+}
+
+/**************************************************
+ * load
+ *
+ * load the settings from a key file
+ **************************************************/
+bool
+CsvTransSettings::load (const std::string& group)
+{
+    GKeyFile   *keyfile;
+    gint        i;
+    GError     *key_error = NULL;
+    bool        key_boolean = false;
+    int         key_int = 0;
+    gchar      *key_char = NULL;
+    bool        error = false;
+
+    // Get the Key file
+    keyfile = gnc_state_get_current ();
+
+    key_int = g_key_file_get_integer (keyfile, group.c_str(), CSV_SKIP_START, &key_error);
+    header_rows = (key_error) ? 0 : key_int;
+    error |= handle_load_error (&key_error, group);
+
+    key_int = g_key_file_get_integer (keyfile, group.c_str(), CSV_SKIP_END, &key_error);
+    footer_rows = (key_error) ? 0 : key_int;
+    error |= handle_load_error (&key_error, group);
+
+    key_boolean = g_key_file_get_boolean (keyfile, group.c_str(), CSV_ALT_ROWS, &key_error);
+    skip_alt_rows = (key_error) ? false : key_boolean;
+    error |= handle_load_error (&key_error, group);
+
+    key_boolean = g_key_file_get_boolean (keyfile, group.c_str(), CSV_MULTI_SPLIT, &key_error);
+    multi_split = (key_error) ? false : key_boolean;
+    error |= handle_load_error (&key_error, group);
+
+    key_boolean = g_key_file_get_boolean (keyfile, group.c_str(), CSV_FORMAT, &key_error);
+    csv_format = (key_error) ? true : key_boolean;
+    error |= handle_load_error (&key_error, group);
+
+    for (i = 0; i < SEP_NUM_OF_TYPES; i++)
+    {
+        gchar *sep;
+        sep = g_strdup_printf ("%s%d", CSV_SEP, i);
+        key_boolean = g_key_file_get_boolean (keyfile, group.c_str(), sep, &key_error);
+        separator[i] = (key_error) ? false : key_boolean;
+        error |= handle_load_error (&key_error, group);
+        g_free (sep);
+    }
+
+    key_boolean = g_key_file_get_boolean (keyfile, group.c_str(), CSV_CUSTOM, &key_error);
+    custom = (key_error) ? false : key_boolean;
+    error |= handle_load_error (&key_error, group);
+
+    custom_entry = g_key_file_get_string (keyfile, group.c_str(), CSV_CUSTOM_ENTRY, &key_error);
+    error |= handle_load_error (&key_error, group);
+
+    key_int = g_key_file_get_integer (keyfile, group.c_str(), CSV_DATE, &key_error);
+    date_active = (key_error) ? 0 : key_int;
+    error |= handle_load_error (&key_error, group);
+
+    key_int = g_key_file_get_integer (keyfile, group.c_str(), CSV_CURRENCY, &key_error);
+    currency_active = (key_error) ? 0 : key_int;
+    error |= handle_load_error (&key_error, group);
+
+    key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_ENCODING, &key_error);
+    encoding = g_strdup((key_error) ? "UTF-8" : key_char);
+    error |= handle_load_error (&key_error, group);
+
+    column_types = g_key_file_get_string (keyfile, group.c_str(), CSV_COL_TYPES, &key_error);
+    error |= handle_load_error (&key_error, group);
+
+    column_widths = g_key_file_get_string (keyfile, group.c_str(), CSV_COL_WIDTHS, &key_error);
+    error |= handle_load_error (&key_error, group);
+
+    g_free (key_char);
+    return error;
+}
+
+
+/**************************************************
+ * save
+ *
+ * save settings to a key file
+ **************************************************/
+bool
+CsvTransSettings::save (const std::string& settings_name)
+{
+    auto keyfile = gnc_state_get_current ();
+    std::string group = csv_group_prefix + settings_name;
+
+    // Drop previous saved settings with this name
+    g_key_file_remove_group (keyfile, group.c_str(), nullptr);
+
+    // Start Saving the settings
+    g_key_file_set_string (keyfile, group.c_str(), CSV_NAME, settings_name.c_str());
+    g_key_file_set_boolean (keyfile, group.c_str(), CSV_MULTI_SPLIT, multi_split);
+    g_key_file_set_integer (keyfile, group.c_str(), CSV_SKIP_START, header_rows);
+    g_key_file_set_integer (keyfile, group.c_str(), CSV_SKIP_END, footer_rows);
+    g_key_file_set_boolean (keyfile, group.c_str(), CSV_ALT_ROWS, skip_alt_rows);
+    g_key_file_set_boolean (keyfile, group.c_str(), CSV_FORMAT, csv_format);
+
+    for (guint i = 0; i < SEP_NUM_OF_TYPES; i++)
+    {
+        gchar *sep;
+        sep = g_strdup_printf ("%s%d", CSV_SEP, i);
+        g_key_file_set_boolean (keyfile, group.c_str(), sep, separator[i]);
+        g_free (sep);
+    }
+
+    g_key_file_set_boolean (keyfile, group.c_str(), CSV_CUSTOM, custom);
+    g_key_file_set_string (keyfile, group.c_str(), CSV_CUSTOM_ENTRY, custom_entry);
+    g_key_file_set_integer (keyfile, group.c_str(), CSV_DATE, date_active);
+    g_key_file_set_integer (keyfile, group.c_str(), CSV_CURRENCY, currency_active);
+    g_key_file_set_string (keyfile, group.c_str(), CSV_ENCODING, encoding);
+    g_key_file_set_string (keyfile, group.c_str(), CSV_COL_TYPES, column_types);
+    g_key_file_set_string (keyfile, group.c_str(), CSV_COL_WIDTHS, column_widths);
+
+    // Do a test read of column types
+    GError *key_error = nullptr;
+    bool error = false;
+    gchar *test_string = g_key_file_get_string (keyfile, group.c_str(), CSV_COL_TYPES, &key_error);
+
+    if ((key_error) || (g_strcmp0 (test_string, column_types) != 0))
+    {
+        if (key_error)
+        {
+            g_warning ("Error reading group %s key %s: %s", group.c_str(), CSV_COL_TYPES, key_error->message);
+            g_error_free (key_error);
+        }
+        else
+            g_warning ("Error comparing group %s key %s: '%s' and '%s'", group.c_str(), CSV_COL_TYPES, test_string, group.c_str());
+        error = true;
+    }
+    g_free (test_string);
+    return error;
+}
diff --git a/src/import-export/csv-imp/gnc-csv-trans-settings.h b/src/import-export/csv-imp/gnc-csv-trans-settings.hpp
similarity index 58%
rename from src/import-export/csv-imp/gnc-csv-trans-settings.h
rename to src/import-export/csv-imp/gnc-csv-trans-settings.hpp
index 08438c2..1849be4 100644
--- a/src/import-export/csv-imp/gnc-csv-trans-settings.h
+++ b/src/import-export/csv-imp/gnc-csv-trans-settings.hpp
@@ -27,6 +27,12 @@
 #ifndef GNC_CSV_TRANS_SETTINGS_H
 #define GNC_CSV_TRANS_SETTINGS_H
 
+extern "C" {
+#include <gtk/gtk.h>
+}
+
+#include <string>
+
 /** Enumeration for separator checkbutton types. These are the
  *  different types of checkbuttons that the user can click to
  *  configure separators in a delimited file. */
@@ -36,66 +42,64 @@ enum SEP_BUTTON_TYPES {SEP_SPACE, SEP_TAB, SEP_COMMA, SEP_COLON, SEP_SEMICOLON,
 /** Enumeration for the settings combo's */
 enum SETTINGS_COL {SET_GROUP, SET_NAME};
 
-typedef struct
+struct CsvTransSettings
 {
-    int           header_rows;                  // Number of header rows
-    int           footer_rows;                  // Number of footer rows
-    gboolean      csv_format;                   // CSV import Format
-    gboolean      skip_alt_rows;                // Skip alternate rows
-    gboolean      multi_split;                  // Assume multiple lines per transaction
-
-    const gchar  *encoding;                     // File encoding
-
-    gboolean      separator[SEP_NUM_OF_TYPES];  // The separators
+    CsvTransSettings() : header_rows{0}, footer_rows{0}, csv_format (true),
+                    skip_alt_rows (false), multi_split (false),
+                    encoding {"UTF-8"}, custom {false}, custom_entry {nullptr},
+                    date_active {0}, currency_active {0},
+                    column_types{nullptr}, column_widths{nullptr}
+                    {
+                        for (uint i = 0; i < SEP_NUM_OF_TYPES; i++)
+                        {
+                            separator[i] = false;
+                        }
+                    }
 
-    gboolean      custom;                       // Custom entry set
-    const gchar  *custom_entry;                 // Custom Entry
-
-    int           date_active;                  // Date Active id
-    int           currency_active;              // Currency Active id
-    const gchar  *column_types;                 // The Column types in order
-    const gchar  *column_widths;                // The Column widths
-} CsvSettings;
-
-/** Finds CSV settings entries in the key file and populates the
+/** Finds CSV settings entries in the state key file and populates the
  *  tree model.
  *
  *  @param settings_store The liststore that is used for the combo's
  *  which holds the key name and visual text.
  */
-void gnc_csv_trans_find_settings (GtkTreeModel *settings_store);
+void find (GtkTreeModel *settings_store);
 
 /** Save the gathered widget properties to a key File.
  *
- *  @param settings_data The settings structure where all the settings
- *  are located.
- *
  *  @param settings_name The name the settings will be stored under.
  *
- *  @return TRUE if there was a problem in saving.
+ *  @return true if there was a problem in saving.
  */
-gboolean gnc_csv_trans_save_settings (CsvSettings *settings_data, gchar *settings_name);
+bool save (const std::string& settings_name);
 
 /** Load the widget properties from a key File.
  *
- *  @param settings_data The settings structure where all the settings
- *  are located.
- *
  *  @param group The group name where the settings are stored in the
  *  key file.
  *
- *  @return TRUE if there was a problem.
+ *  @return true if there was a problem.
  */
-gboolean gnc_csv_trans_load_settings (CsvSettings *settings_data, gchar *group);
+bool load (const std::string& group);
 
-/** Create a new CsvSettings structure and set default values.
- *
- *  @return CsvSettings settings structure.
- */
-CsvSettings * gnc_csv_trans_new_settings_data (void);
 
-/** Free the CsvSettings structure.
- */
-void gnc_csv_trans_settings_data_free (CsvSettings *settings_data);
+int           header_rows;                  // Number of header rows
+int           footer_rows;                  // Number of footer rows
+bool          csv_format;                   // CSV import Format
+bool          skip_alt_rows;                // Skip alternate rows
+bool          multi_split;                  // Assume multiple lines per transaction
+
+const gchar  *encoding;                     // File encoding
+
+bool          separator[SEP_NUM_OF_TYPES];  // The separators
+
+bool          custom;                       // Custom entry set
+const gchar  *custom_entry;                 // Custom Entry
+
+int           date_active;                  // Date Active id
+int           currency_active;              // Currency Active id
+const gchar  *column_types;                 // The Column types in order
+const gchar  *column_widths;                // The Column widths
+
+};
 
 #endif

commit dcce2d79cd24b380a652ddbbfd5b3d35c3ee8710
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sat Jan 14 14:36:56 2017 +0100

    Remove final dependencies on GOFFICE
    
    This was still pulled in via the now removed gnc-csv-model.
    In order to remove this, the fixed format csv importer
    has been tweaked to get the required features from
    the c++ csv importer code instead.

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 733cb16..34e4668 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -122,7 +122,7 @@ IF (WIN32)
   #
 
   SET(CMAKE_FRAMEWORK_PATH_TMP ${CMAKE_PREFIX_PATH})
-  SET(DEV_SUBDIRS aqbanking gnome goffice guile gwenhywfar libgsf libofx libsoup libxslt webkit)
+  SET(DEV_SUBDIRS aqbanking gnome guile gwenhywfar libgsf libofx libsoup libxslt webkit)
   FOREACH(subdir ${DEV_SUBDIRS})
     LIST(APPEND CMAKE_FRAMEWORK_PATH_TMP ${CMAKE_PREFIX_PATH}/${subdir})
   ENDFOREACH()
@@ -174,7 +174,6 @@ GNC_PKG_CHECK_MODULES (LIBXSLT REQUIRED libxslt)
 GNC_PKG_CHECK_MODULES (WEBKIT REQUIRED webkit-1.0>=1.2)
 IF (WITH_GNUCASH)
   GNC_PKG_CHECK_MODULES (GTK2 REQUIRED gtk+-2.0>=2.24.0)
-  GNC_PKG_CHECK_MODULES (GOFFICE REQUIRED libgoffice-0.8>=0.8.17)
 ENDIF (WITH_GNUCASH)
 
 GNC_PKG_CHECK_MODULES (ZLIB REQUIRED zlib)
diff --git a/configure.ac b/configure.ac
index 2ce9c19..de17907 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1382,11 +1382,6 @@ then
   AC_SUBST(GDK_PIXBUF_CFLAGS)
   AC_SUBST(GDK_PIXBUF_LIBS)
 
-   # checks for goffice
-  PKG_CHECK_MODULES(GOFFICE, libgoffice-0.8 >= 0.7.0 libgoffice-0.8 < 0.9.0, [goffice=1], [AC_MSG_ERROR([Cannot find libgoffice >= 0.7.0 and < 0.9.0])])
-  AC_SUBST(GOFFICE_CFLAGS)
-  AC_SUBST(GOFFICE_LIBS)
-
   ### --------------------------------------------------------------------------
   ### checks for webkit
 
@@ -1699,7 +1694,6 @@ AC_CONFIG_FILES(
   lib/Makefile
   lib/goffice/Makefile
   lib/libc/Makefile
-  lib/stf/Makefile
   packaging/Makefile
   src/Makefile
   src/app-utils/Makefile
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 8f01526..5930c6f 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -1,5 +1,5 @@
 if GNUCASH_ENABLE_GUI
-SUBDIRS = libc stf goffice
+SUBDIRS = libc goffice
 else
 SUBDIRS = libc
 endif
diff --git a/lib/goffice/go-charmap-sel.c b/lib/goffice/go-charmap-sel.c
index 7d6e244..28c1a97 100644
--- a/lib/goffice/go-charmap-sel.c
+++ b/lib/goffice/go-charmap-sel.c
@@ -584,6 +584,34 @@ static void cs_class_init(GtkWidgetClass *widget_klass)
     }
 }
 
+GType
+go_charmap_sel_get_type (void)
+{
+    static GType go_charmap_sel_type = 0;
+
+    if (go_charmap_sel_type == 0)
+    {
+        GTypeInfo go_charmap_sel_info =
+        {
+            sizeof (GOCharmapSelClass),
+            NULL,
+            NULL,
+            (GClassInitFunc) cs_class_init,
+            NULL,
+            NULL,
+            sizeof (GOCharmapSel),
+            0,
+            (GInstanceInitFunc) cs_init
+        };
+
+        go_charmap_sel_type = g_type_register_static (GTK_TYPE_HBOX,
+                           "GOCharmapSel",
+                           &go_charmap_sel_info, 0);
+    }
+
+    return go_charmap_sel_type;
+}
+
 GtkWidget *
 go_charmap_sel_new(GOCharmapSelTestDirection test)
 {
diff --git a/lib/stf/Makefile.am b/lib/stf/Makefile.am
deleted file mode 100644
index 91acf4f..0000000
--- a/lib/stf/Makefile.am
+++ /dev/null
@@ -1,13 +0,0 @@
-noinst_LTLIBRARIES = libgnc-stf.la
-
-REALSRCS = stf-parse.c
-REALHDRS = stf-parse.h
-
-libgnc_stf_la_SOURCES = ${REALSRCS}
-noinst_HEADERS = ${REALHDRS}
-
-libgnc_stf_la_LIBADD = $(GOFFICE_LIBS)
-
-AM_CPPFLAGS = $(GOFFICE_CFLAGS)
-
-EXTRA_DIST = $(REALSRCS) $(REALHDRS)
diff --git a/lib/stf/README b/lib/stf/README
deleted file mode 100644
index bb00f09..0000000
--- a/lib/stf/README
+++ /dev/null
@@ -1,2 +0,0 @@
-This consists of code taken from Gnumeric's Structured Text Format
-parser. It is used for the CSV/Fixed-Width file importer.
diff --git a/lib/stf/stf-parse.c b/lib/stf/stf-parse.c
deleted file mode 100644
index 88d1c96..0000000
--- a/lib/stf/stf-parse.c
+++ /dev/null
@@ -1,1414 +0,0 @@
-/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * stf-parse.c : Structured Text Format parser. (STF)
- *               A general purpose engine for parsing data
- *               in CSV and Fixed width format.
- *
- *
- * Copyright (C) Almer. S. Tigelaar.
- * EMail: almer1 at dds.nl or almer-t at bigfoot.com
- *
- * Copyright (C) 2003 Andreas J. Guelzow <aguelzow at taliesin.ca>
- * Copyright (C) 2003 Morten Welinder <terra at gnome.org>
- *
- * 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 2 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, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#define GETTEXT_PACKAGE gnumeric
-
-#include <glib/gi18n-lib.h>
-/* #include "gnumeric.h" */
-#include "stf-parse.h"
-
-/* #include "workbook.h" */
-/* #include "cell.h" */
-/* #include "sheet.h" */
-/* #include "clipboard.h" */
-/* #include "sheet-style.h" */
-/* #include "value.h" */
-/* #include "mstyle.h" */
-/* #include "number-match.h" */
-/* #include "gutils.h" */
-/* #include "parse-util.h" */
-#include <goffice/utils/go-glib-extras.h>
-#include <goffice/utils/go-format.h>
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <locale.h>
-#include <string.h>
-#include <math.h>
-
-#define SETUP_LOCALE_SWITCH char *oldlocale = NULL
-
-#define START_LOCALE_SWITCH if (parseoptions->locale) {\
-oldlocale = g_strdup(go_setlocale (LC_ALL, NULL)); \
-go_setlocale(LC_ALL, parseoptions->locale);}
-
-#define END_LOCALE_SWITCH if (oldlocale) {\
-go_setlocale(LC_ALL, oldlocale);\
-g_free (oldlocale);}
-
-/* Source_t struct, used for interchanging parsing information between the low level parse functions */
-typedef struct {
-	GStringChunk *chunk;
-	char const *position;  /* Indicates the current position within data */
-
-	/* Used internally for fixed width parsing */
-	int splitpos;          /* Indicates current position in splitpositions array */
-	int linepos;           /* Position on the current line */
-} Source_t;
-
-/* Struct used for autodiscovery */
-typedef struct {
-	int start;
-	int stop;
-} AutoDiscovery_t;
-
-/*
- * Some silly dude make the length field an unsigned int.  C just does
- * not deal very well with that.
- */
-static inline int
-my_garray_len (GArray const *a)
-{
-	return (int)a->len;
-}
-
-static inline int
-my_gptrarray_len (GPtrArray const *a)
-{
-	return (int)a->len;
-}
-
-static int
-compare_terminator (char const *s, StfParseOptions_t *parseoptions)
-{
-	guchar const *us = (guchar const *)s;
-	GSList *l;
-
-	if (*us > parseoptions->compiled_terminator.max ||
-	    *us < parseoptions->compiled_terminator.min)
-		return 0;
-
-	for (l = parseoptions->terminator; l; l = l->next) {
-		char const *term = l->data;
-		char const *d = s;
-
-		while (*term) {
-			if (*d != *term)
-				goto next;
-			term++;
-			d++;
-		}
-		return d - s;
-
-	next:
-		;
-	}
-	return 0;
-}
-
-
-/*******************************************************************************************************
- * STF PARSE OPTIONS : StfParseOptions related
- *******************************************************************************************************/
-
-/**
- * stf_parse_options_new:
- *
- * This will return a new StfParseOptions_t struct.
- * The struct should, after being used, freed with stf_parse_options_free.
- **/
-StfParseOptions_t *
-stf_parse_options_new (void)
-{
-	StfParseOptions_t* parseoptions = g_new0 (StfParseOptions_t, 1);
-
-	parseoptions->parsetype   = PARSE_TYPE_NOTSET;
-
-	parseoptions->terminator  = NULL;
-	stf_parse_options_add_line_terminator (parseoptions, "\r\n");
-	stf_parse_options_add_line_terminator (parseoptions, "\n");
-	stf_parse_options_add_line_terminator (parseoptions, "\r");
-
-	parseoptions->trim_spaces = (TRIM_TYPE_RIGHT | TRIM_TYPE_LEFT);
-	parseoptions->locale = NULL;
-
-	parseoptions->splitpositions = NULL;
-	stf_parse_options_fixed_splitpositions_clear (parseoptions);
-
-	parseoptions->stringindicator = '"';
-	parseoptions->indicator_2x_is_single = TRUE;
-	parseoptions->duplicates = FALSE;
-	parseoptions->trim_seps = FALSE;
-
-	parseoptions->sep.str = NULL;
-	parseoptions->sep.chr = NULL;
-
-	parseoptions->col_import_array = NULL;
-	parseoptions->col_import_array_len = 0;
-	parseoptions->formats = NULL;
-
-	parseoptions->cols_exceeded = FALSE;
-
-	return parseoptions;
-}
-
-/**
- * stf_parse_options_free:
- *
- * will free @parseoptions, note that this will not free the splitpositions
- * member (GArray) of the struct, the caller is responsible for that.
- **/
-void
-stf_parse_options_free (StfParseOptions_t *parseoptions)
-{
-	g_return_if_fail (parseoptions != NULL);
-
-	g_free (parseoptions->col_import_array);
-	g_free (parseoptions->locale);
-	g_free (parseoptions->sep.chr);
-
-	if (parseoptions->sep.str) {
-		GSList *l;
-
-		for (l = parseoptions->sep.str; l != NULL; l = l->next)
-			g_free ((char *) l->data);
-		g_slist_free (parseoptions->sep.str);
-	}
-
-	g_array_free (parseoptions->splitpositions, TRUE);
-
-	stf_parse_options_clear_line_terminator (parseoptions);
-
-	if (parseoptions->formats) {
-		unsigned int ui;
-		GPtrArray *formats = parseoptions->formats;
-
-		for (ui = 0; ui < formats->len; ui++)
-			go_format_unref (g_ptr_array_index (formats, ui));
-		g_ptr_array_free (formats, TRUE);
-		parseoptions->formats = NULL;
-	}
-
-	g_free (parseoptions);
-}
-
-void
-stf_parse_options_set_type (StfParseOptions_t *parseoptions, StfParseType_t const parsetype)
-{
-	g_return_if_fail (parseoptions != NULL);
-	g_return_if_fail (parsetype == PARSE_TYPE_CSV || parsetype == PARSE_TYPE_FIXED);
-
-	parseoptions->parsetype = parsetype;
-}
-
-static gint
-long_string_first (gchar const *a, gchar const *b)
-{
-	/* This actually is UTF-8 safe.  */
-	return strlen (b) - strlen (a);
-}
-
-static void
-compile_terminators (StfParseOptions_t *parseoptions)
-{
-	GSList *l;
-	GO_SLIST_SORT (parseoptions->terminator, (GCompareFunc)long_string_first);
-
-	parseoptions->compiled_terminator.min = 255;
-	parseoptions->compiled_terminator.max = 0;
-	for (l = parseoptions->terminator; l; l = l->next) {
-		const guchar *term = l->data;
-		parseoptions->compiled_terminator.min =
-			MIN (parseoptions->compiled_terminator.min, *term);
-		parseoptions->compiled_terminator.max =
-			MAX (parseoptions->compiled_terminator.max, *term);
-	}
-}
-
-/**
- * stf_parse_options_add_line_terminator:
- *
- * This will add to the line terminators, in both the Fixed width and CSV delimited importers
- * this indicates the end of a row.
- *
- **/
-void
-stf_parse_options_add_line_terminator (StfParseOptions_t *parseoptions, char const *terminator)
-{
-	g_return_if_fail (parseoptions != NULL);
-	g_return_if_fail (terminator != NULL && *terminator != 0);
-
-	GO_SLIST_PREPEND (parseoptions->terminator, g_strdup (terminator));
-	compile_terminators (parseoptions);
-}
-
-/**
- * stf_parse_options_clear_line_terminator:
- *
- * This will clear the line terminator, in both the Fixed width and CSV delimited importers
- * this indicates the end of a row.
- *
- **/
-void
-stf_parse_options_clear_line_terminator (StfParseOptions_t *parseoptions)
-{
-	g_return_if_fail (parseoptions != NULL);
-
-	go_slist_free_custom (parseoptions->terminator, g_free);
-	parseoptions->terminator = NULL;
-	compile_terminators (parseoptions);
-}
-
-/**
- * stf_parse_options_set_trim_spaces:
- *
- * If enabled will trim spaces in every parsed field on left and/or right
- * sides.
- **/
-void
-stf_parse_options_set_trim_spaces (StfParseOptions_t *parseoptions, StfTrimType_t const trim_spaces)
-{
-	g_return_if_fail (parseoptions != NULL);
-
-	parseoptions->trim_spaces = trim_spaces;
-}
-
-/**
- * stf_parse_options_csv_set_separators:
- *
- * A copy is made of the parameters.
- **/
-void
-stf_parse_options_csv_set_separators (StfParseOptions_t *parseoptions, char const *character,
-				      GSList const *string)
-{
-	g_return_if_fail (parseoptions != NULL);
-
-	g_free (parseoptions->sep.chr);
-	parseoptions->sep.chr = g_strdup (character);
-
-	go_slist_free_custom (parseoptions->sep.str, g_free);
-	parseoptions->sep.str = go_slist_map (string, (GOMapFunc)g_strdup);
-}
-
-void
-stf_parse_options_csv_set_stringindicator (StfParseOptions_t *parseoptions, gunichar const stringindicator)
-{
-	g_return_if_fail (parseoptions != NULL);
-	g_return_if_fail (stringindicator != '\0');
-
-	parseoptions->stringindicator = stringindicator;
-}
-
-/**
- * stf_parse_options_csv_set_indicator_2x_is_single:
- * @indic_2x : a boolean value indicating whether we want to see two
- * 		adjacent string indicators as a single string indicator
- * 		that is part of the cell, rather than a terminator.
- **/
-void
-stf_parse_options_csv_set_indicator_2x_is_single (StfParseOptions_t *parseoptions,
-						  gboolean const indic_2x)
-{
-	g_return_if_fail (parseoptions != NULL);
-
-	parseoptions->indicator_2x_is_single = indic_2x;
-}
-
-/**
- * stf_parse_options_csv_set_duplicates:
- * @duplicates : a boolean value indicating whether we want to see two
- *               separators right behind each other as one
- **/
-void
-stf_parse_options_csv_set_duplicates (StfParseOptions_t *parseoptions, gboolean const duplicates)
-{
-	g_return_if_fail (parseoptions != NULL);
-
-	parseoptions->duplicates = duplicates;
-}
-
-/**
- * stf_parse_options_csv_set_trim_seps:
- * @trim_seps : a boolean value indicating whether we want to ignore
- *               separators at the beginning of lines
- **/
-void
-stf_parse_options_csv_set_trim_seps (StfParseOptions_t *parseoptions, gboolean const trim_seps)
-{
-	g_return_if_fail (parseoptions != NULL);
-
-	parseoptions->trim_seps = trim_seps;
-}
-
-/**
- * stf_parse_options_fixed_splitpositions_clear:
- *
- * This will clear the splitpositions (== points on which a line is split)
- **/
-void
-stf_parse_options_fixed_splitpositions_clear (StfParseOptions_t *parseoptions)
-{
-	int minus_one = -1;
-	g_return_if_fail (parseoptions != NULL);
-
-	if (parseoptions->splitpositions)
-		g_array_free (parseoptions->splitpositions, TRUE);
-	parseoptions->splitpositions = g_array_new (FALSE, FALSE, sizeof (int));
-
-	g_array_append_val (parseoptions->splitpositions, minus_one);
-}
-
-/**
- * stf_parse_options_fixed_splitpositions_add:
- *
- * @position will be added to the splitpositions.
- **/
-void
-stf_parse_options_fixed_splitpositions_add (StfParseOptions_t *parseoptions, int position)
-{
-	unsigned int ui;
-
-	g_return_if_fail (parseoptions != NULL);
-	g_return_if_fail (position >= 0);
-
-	for (ui = 0; ui < parseoptions->splitpositions->len - 1; ui++) {
-		int here = g_array_index (parseoptions->splitpositions, int, ui);
-		if (position == here)
-			return;
-		if (position < here)
-			break;
-	}
-
-	g_array_insert_val (parseoptions->splitpositions, ui, position);
-}
-
-void
-stf_parse_options_fixed_splitpositions_remove (StfParseOptions_t *parseoptions, int position)
-{
-	unsigned int ui;
-
-	g_return_if_fail (parseoptions != NULL);
-	g_return_if_fail (position >= 0);
-
-	for (ui = 0; ui < parseoptions->splitpositions->len - 1; ui++) {
-		int here = g_array_index (parseoptions->splitpositions, int, ui);
-		if (position == here)
-			g_array_remove_index (parseoptions->splitpositions, ui);
-		if (position <= here)
-			return;
-	}
-}
-
-int
-stf_parse_options_fixed_splitpositions_count (StfParseOptions_t *parseoptions)
-{
-	return parseoptions->splitpositions->len;
-}
-
-int
-stf_parse_options_fixed_splitpositions_nth (StfParseOptions_t *parseoptions, int n)
-{
-	return g_array_index (parseoptions->splitpositions, int, n);
-}
-
-
-/**
- * stf_parse_options_valid:
- * @parseoptions : an import options struct
- *
- * Checks if @parseoptions is correctly filled
- *
- * returns : TRUE if it is correctly filled, FALSE otherwise.
- **/
-static gboolean
-stf_parse_options_valid (StfParseOptions_t *parseoptions)
-{
-	g_return_val_if_fail (parseoptions != NULL, FALSE);
-
-	if (parseoptions->parsetype == PARSE_TYPE_CSV) {
-		if (parseoptions->stringindicator == '\0') {
-			g_warning ("STF: Cannot have \\0 as string indicator");
-			return FALSE;
-		}
-
-	} else if (parseoptions->parsetype == PARSE_TYPE_FIXED) {
-		if (!parseoptions->splitpositions) {
-			g_warning ("STF: No splitpositions in struct");
-			return FALSE;
-		}
-	}
-
-	return TRUE;
-}
-
-/*******************************************************************************************************
- * STF PARSE : The actual routines that do the 'trick'
- *******************************************************************************************************/
-
-static void
-trim_spaces_inplace (char *field, StfParseOptions_t const *parseoptions)
-{
-	if (!field) return;
-
-	if (parseoptions->trim_spaces & TRIM_TYPE_LEFT) {
-		char *s = field;
-
-		while (g_unichar_isspace (g_utf8_get_char (s)))
-			s = g_utf8_next_char (s);
-
-		if (s != field)
-			strcpy (field, s);
-	}
-
-	if (parseoptions->trim_spaces & TRIM_TYPE_RIGHT) {
-		char *s = field + strlen (field);
-
-		while (field != s) {
-			s = g_utf8_prev_char (s);
-			if (!g_unichar_isspace (g_utf8_get_char (s)))
-				break;
-			*s = 0;
-		}
-	}
-}
-
-/**
- * stf_parse_csv_is_separator:
- *
- * returns NULL if @character is not a separator, a pointer to the character
- * after the separator otherwise.
- **/
-static char const *
-stf_parse_csv_is_separator (char const *character, char const *chr, GSList const *str)
-{
-	g_return_val_if_fail (character != NULL, NULL);
-
-	if (*character == 0)
-		return NULL;
-
-	if (str) {
-		GSList const *l;
-
-		for (l = str; l != NULL; l = l->next) {
-			char const *s = l->data;
-			char const *r;
-			glong cnt;
-			glong const len = g_utf8_strlen (s, -1);
-
-			/* Don't compare past the end of the buffer! */
-			for (r = character, cnt = 0; cnt < len; cnt++, r = g_utf8_next_char (r))
-				if (*r == '\0')
-					break;
-
-			if ((cnt == len) && (memcmp (character, s, len) == 0))
-				return g_utf8_offset_to_pointer (character, len);
-		}
-	}
-
-	if (chr && g_utf8_strchr (chr, -1,
-				  g_utf8_get_char (character)))
-		return g_utf8_next_char(character);
-
-	return NULL;
-}
-
-/*
- * stf_parse_eat_separators:
- *
- * skip over leading separators
- *
- */
-
-static void
-stf_parse_eat_separators (Source_t *src, StfParseOptions_t *parseoptions)
-{
-	char const *cur, *next;
-
-	g_return_if_fail (src != NULL);
-        g_return_if_fail (parseoptions != NULL);
-
-	cur = src->position;
-
-	if (*cur == '\0' || compare_terminator (cur, parseoptions))
-		return;
-	while ((next = stf_parse_csv_is_separator (cur, parseoptions->sep.chr, parseoptions->sep.str)))
-		cur = next;
-	src->position = cur;
-	return;
-}
-
-
-typedef enum {
-	STF_CELL_ERROR,
-	STF_CELL_EOF,
-	STF_CELL_EOL,
-	STF_CELL_FIELD_NO_SEP,
-	STF_CELL_FIELD_SEP,
-} StfParseCellRes;
-
-static StfParseCellRes
-stf_parse_csv_cell (GString *text, Source_t *src, StfParseOptions_t *parseoptions)
-{
-	char const *cur;
-	gboolean saw_sep = FALSE;
-
-	g_return_val_if_fail (src != NULL, STF_CELL_ERROR);
-	g_return_val_if_fail (parseoptions != NULL, STF_CELL_ERROR);
-
-	cur = src->position;
-	g_return_val_if_fail (cur != NULL, STF_CELL_ERROR);
-
-	/* Skip whitespace, but stop at line terminators.  */
-	while (1) {
-		int term_len;
-
-		if (*cur == 0) {
-			src->position = cur;
-			return STF_CELL_EOF;
-		}
-
-		term_len = compare_terminator (cur, parseoptions);
-		if (term_len) {
-			src->position = cur + term_len;
-			return STF_CELL_EOL;
-		}
-
-		if ((parseoptions->trim_spaces & TRIM_TYPE_LEFT) == 0)
-			break;
-
-		if (stf_parse_csv_is_separator (cur, parseoptions->sep.chr,
-						parseoptions->sep.str))
-			break;
-
-		if (!g_unichar_isspace (g_utf8_get_char (cur)))
-			break;
-		cur = g_utf8_next_char (cur);
-	}
-
-	if (g_utf8_get_char (cur) == parseoptions->stringindicator) {
-		cur = g_utf8_next_char (cur);
-		while (*cur) {
-			gunichar uc = g_utf8_get_char (cur);
-			cur = g_utf8_next_char (cur);
-
-			if (uc == parseoptions->stringindicator) {
-				if (parseoptions->indicator_2x_is_single &&
-				    g_utf8_get_char (cur) == parseoptions->stringindicator)
-					cur = g_utf8_next_char (cur);
-				else {
-					/* "field content"dropped-garbage,  */
-					while (*cur && !compare_terminator (cur, parseoptions)) {
-						char const *post = stf_parse_csv_is_separator
-							(cur, parseoptions->sep.chr, parseoptions->sep.str);
-						if (post) {
-							cur = post;
-							saw_sep = TRUE;
-							break;
-						}
-						cur = g_utf8_next_char (cur);
-					}
-					break;
-				}
-			}
-
-			g_string_append_unichar (text, uc);
-		}
-
-		/* We silently allow a missing terminating quote.  */
-	} else {
-		/* Unquoted field.  */
-
-		while (*cur && !compare_terminator (cur, parseoptions)) {
-
-			char const *post = stf_parse_csv_is_separator
-				(cur, parseoptions->sep.chr, parseoptions->sep.str);
-			if (post) {
-				cur = post;
-				saw_sep = TRUE;
-				break;
-			}
-
-			g_string_append_unichar (text, g_utf8_get_char (cur));
-			cur = g_utf8_next_char (cur);
-		}
-
-		if (parseoptions->trim_spaces & TRIM_TYPE_RIGHT) {
-			while (text->len) {
-				const char *last = g_utf8_prev_char (text->str + text->len);
-				if (!g_unichar_isspace (g_utf8_get_char (last)))
-					break;
-				g_string_truncate (text, last - text->str);
-			}
-		}
-	}
-
-	src->position = cur;
-
-	if (saw_sep && parseoptions->duplicates)
-		stf_parse_eat_separators (src, parseoptions);
-
-	return saw_sep ? STF_CELL_FIELD_SEP : STF_CELL_FIELD_NO_SEP;
-}
-
-/**
- * stf_parse_csv_line:
- *
- * This will parse one line from the current @src->position.
- * NOTE: The calling routine is responsible for freeing the result.
- *
- * returns : a GPtrArray of char*'s
- **/
-static GPtrArray *
-stf_parse_csv_line (Source_t *src, StfParseOptions_t *parseoptions)
-{
-	GPtrArray *line;
-	gboolean cont = FALSE;
-
-	g_return_val_if_fail (src != NULL, NULL);
-	g_return_val_if_fail (parseoptions != NULL, NULL);
-
-	line = g_ptr_array_new ();
-	if (parseoptions->trim_seps)
-		stf_parse_eat_separators (src, parseoptions);
-
-	while (1) {
-		GString *text = g_string_sized_new (30);
-		StfParseCellRes res =
-			stf_parse_csv_cell (text, src, parseoptions);
-		trim_spaces_inplace (text->str, parseoptions);
-		switch (res) {
-		case STF_CELL_FIELD_NO_SEP:
-			g_ptr_array_add (line, g_string_free (text, FALSE));
-			cont = FALSE;
-			break;
-
-		case STF_CELL_FIELD_SEP:
-			g_ptr_array_add (line, g_string_free (text, FALSE));
-			cont = TRUE;  /* Make sure we see one more field.  */
-			break;
-
-		default:
-			if (cont)
-				g_ptr_array_add (line, g_string_free (text, FALSE));
-			else
-				g_string_free (text, TRUE);
-			return line;
-		}
-	}
-}
-
-/**
- * stf_parse_fixed_cell:
- *
- * returns a pointer to the parsed cell contents.
- **/
-static char *
-stf_parse_fixed_cell (Source_t *src, StfParseOptions_t *parseoptions)
-{
-	char *res;
-	char const *cur;
-	int splitval;
-
-	g_return_val_if_fail (src != NULL, NULL);
-	g_return_val_if_fail (parseoptions != NULL, NULL);
-
-	cur = src->position;
-
-	if (src->splitpos < my_garray_len (parseoptions->splitpositions))
-		splitval = (int) g_array_index (parseoptions->splitpositions, int, src->splitpos);
-	else
-		splitval = -1;
-
-	while (*cur != 0 && !compare_terminator (cur, parseoptions) && splitval != src->linepos) {
-		src->linepos++;
-	        cur = g_utf8_next_char (cur);
-	}
-
-	res = g_string_chunk_insert_len (src->chunk,
-					 src->position,
-					 cur - src->position);
-
-	src->position = cur;
-
-	return res;
-}
-
-/**
- * stf_parse_fixed_line:
- *
- * This will parse one line from the current @src->position.
- * It will return a GPtrArray with the cell contents as strings.
-
- * NOTE: The calling routine is responsible for freeing result.
- **/
-static GPtrArray *
-stf_parse_fixed_line (Source_t *src, StfParseOptions_t *parseoptions)
-{
-	GPtrArray *line;
-
-	g_return_val_if_fail (src != NULL, NULL);
-	g_return_val_if_fail (parseoptions != NULL, NULL);
-
-	src->linepos = 0;
-	src->splitpos = 0;
-
-	line = g_ptr_array_new ();
-	while (*src->position != '\0' && !compare_terminator (src->position, parseoptions)) {
-		char *field = stf_parse_fixed_cell (src, parseoptions);
-
-		trim_spaces_inplace (field, parseoptions);
-		g_ptr_array_add (line, field);
-
-		src->splitpos++;
-	}
-
-	return line;
-}
-
-void
-stf_parse_general_free (GPtrArray *lines)
-{
-	unsigned lineno;
-	for (lineno = 0; lineno < lines->len; lineno++) {
-		GPtrArray *line = g_ptr_array_index (lines, lineno);
-		/* Fields are not free here.  */
-		g_ptr_array_free (line, TRUE);
-	}
-	g_ptr_array_free (lines, TRUE);
-}
-
-
-/**
- * stf_parse_general:
- *
- * Returns a GPtrArray of lines, where each line is itself a
- * GPtrArray of strings.
- *
- * The caller must free this entire structure, for example by calling
- * stf_parse_general_free.
- **/
-GPtrArray *
-stf_parse_general (StfParseOptions_t *parseoptions,
-		   GStringChunk *lines_chunk,
-		   char const *data, char const *data_end)
-{
-	GPtrArray *lines;
-	Source_t src;
-	int row;
-
-	g_return_val_if_fail (parseoptions != NULL, NULL);
-	g_return_val_if_fail (data != NULL, NULL);
-	g_return_val_if_fail (data_end != NULL, NULL);
-	g_return_val_if_fail (stf_parse_options_valid (parseoptions), NULL);
-	g_return_val_if_fail (g_utf8_validate (data, -1, NULL), NULL);
-
-	src.chunk = lines_chunk;
-	src.position = data;
-	row = 0;
-
-	lines = g_ptr_array_new ();
-	while (*src.position != '\0' && src.position < data_end) {
-		GPtrArray *line;
-
-		line = parseoptions->parsetype == PARSE_TYPE_CSV
-			? stf_parse_csv_line (&src, parseoptions)
-			: stf_parse_fixed_line (&src, parseoptions);
-
-		g_ptr_array_add (lines, line);
-		if (parseoptions->parsetype != PARSE_TYPE_CSV)
-			src.position += compare_terminator (src.position, parseoptions);
-
-		if (++row == SHEET_MAX_ROWS)
-			break;
-	}
-
-	return lines;
-}
-
-GPtrArray *
-stf_parse_lines (StfParseOptions_t *parseoptions,
-		 GStringChunk *lines_chunk,
-		 char const *data,
-		 int maxlines, gboolean with_lineno)
-{
-	GPtrArray *lines;
-	int lineno = 1;
-
-	g_return_val_if_fail (data != NULL, NULL);
-
-	lines = g_ptr_array_new ();
-	while (*data) {
-		char const *data0 = data;
-		GPtrArray *line = g_ptr_array_new ();
-
-		if (with_lineno) {
-			char buf[4 * sizeof (int)];
-			sprintf (buf, "%d", lineno);
-			g_ptr_array_add (line,
-					 g_string_chunk_insert (lines_chunk, buf));
-		}
-
-		while (1) {
-			int termlen = compare_terminator (data, parseoptions);
-			if (termlen > 0 || *data == 0) {
-				g_ptr_array_add (line,
-						 g_string_chunk_insert_len (lines_chunk,
-									    data0,
-									    data - data0));
-				data += termlen;
-				break;
-			} else
-				data = g_utf8_next_char (data);
-		}
-
-		g_ptr_array_add (lines, line);
-
-		lineno++;
-		if (lineno >= maxlines)
-			break;
-	}
-	return lines;
-}
-
-char const *
-stf_parse_find_line (StfParseOptions_t *parseoptions,
-		     char const *data,
-		     int line)
-{
-	while (line > 0) {
-		int termlen = compare_terminator (data, parseoptions);
-		if (termlen > 0) {
-			data += termlen;
-			line--;
-		} else if (*data == 0) {
-			return data;
-		} else {
-			data = g_utf8_next_char (data);
-		}
-	}
-	return data;
-}
-
-
-/**
- * stf_parse_options_fixed_autodiscover:
- * @parseoptions: a Parse options struct.
- * @data_lines : The number of lines to look at in @data.
- * @data : The actual data.
- *
- * Automatically try to discover columns in the text to be parsed.
- * We ignore empty lines (only containing parseoptions->terminator)
- *
- * FIXME: This is so extremely ugly that I am too tired to rewrite it right now.
- *        Think hard of a better more flexible solution...
- **/
-void
-stf_parse_options_fixed_autodiscover (StfParseOptions_t *parseoptions,
-				      char const *data, char const *data_end)
-{
-	char const *iterator = data;
-	GSList *list = NULL;
-	GSList *list_start = NULL;
-	int lines = 0;
-	int effective_lines = 0;
-	int max_line_length = 0;
-	int *line_begin_hits = NULL;
-	int *line_end_hits = NULL;
-	int i;
-
-	stf_parse_options_fixed_splitpositions_clear (parseoptions);
-
-	/*
-	 * First take a look at all possible white space combinations
-	 */
-	while (*iterator && iterator < data_end) {
-		gboolean begin_recorded = FALSE;
-		AutoDiscovery_t *disc = NULL;
-		int position = 0;
-		int termlen = 0;
-
-		while (*iterator && (termlen = compare_terminator (iterator, parseoptions)) == 0) {
-			if (!begin_recorded && *iterator == ' ') {
-				disc = g_new0 (AutoDiscovery_t, 1);
-
-				disc->start = position;
-
-				begin_recorded = TRUE;
-			} else if (begin_recorded && *iterator != ' ') {
-				disc->stop = position;
-				list = g_slist_prepend (list, disc);
-
-				begin_recorded = FALSE;
-				disc = NULL;
-			}
-
-			position++;
-			iterator++;
-		}
-
-		if (position > max_line_length)
-			max_line_length = position;
-
-		/*
-		 * If there are excess spaces at the end of
-		 * the line : ignore them
-		 */
-		g_free (disc);
-
-		/*
-		 * Hop over the terminator
-		 */
-		iterator += termlen;
-
-		if (position != 0)
-			effective_lines++;
-
-		lines++;
-	}
-
-	list       = g_slist_reverse (list);
-	list_start = list;
-
-	/*
-	 * Kewl stuff :
-	 * Look at the number of hits at each line position
-	 * if the number of hits equals the number of lines
-	 * we can be pretty sure this is the start or end
-	 * of a column, we filter out empty columns
-	 * later
-	 */
-	line_begin_hits = g_new0 (int, max_line_length + 1);
-	line_end_hits   = g_new0 (int, max_line_length + 1);
-
-	while (list) {
-		AutoDiscovery_t *disc = list->data;
-
-		line_begin_hits[disc->start]++;
-		line_end_hits[disc->stop]++;
-
-		g_free (disc);
-
-		list = g_slist_next (list);
-	}
-	g_slist_free (list_start);
-
-	for (i = 0; i < max_line_length + 1; i++)
-		if (line_begin_hits[i] == effective_lines || line_end_hits[i] == effective_lines)
-			stf_parse_options_fixed_splitpositions_add (parseoptions, i);
-
-	/*
-	 * Do some corrections to the initial columns
-	 * detected here, we obviously don't need to
-	 * do this if there are no columns at all.
-	 */
-	if (my_garray_len (parseoptions->splitpositions) > 0) {
-		/*
-		 * Try to find columns that look like :
-		 *
-		 * Example     100
-		 * Example2      9
-		 *
-		 * (In other words : Columns with left & right justification with
-		 *  a minimum of 2 spaces in the middle)
-		 * Split these columns in 2
-		 */
-
-		for (i = 0; i < my_garray_len (parseoptions->splitpositions) - 1; i++) {
-			int begin = g_array_index (parseoptions->splitpositions, int, i);
-			int end   = g_array_index (parseoptions->splitpositions, int, i + 1);
-			int num_spaces   = -1;
-			int spaces_start = 0;
-			gboolean right_aligned = TRUE;
-			gboolean left_aligned  = TRUE;
-			gboolean has_2_spaces  = TRUE;
-
-			iterator = data;
-			lines = 0;
-			while (*iterator && iterator < data_end) {
-				gboolean trigger = FALSE;
-				gboolean space_trigger = FALSE;
-				int pos = 0;
-
-				num_spaces   = -1;
-				spaces_start = 0;
-				while (*iterator && !compare_terminator (iterator, parseoptions)) {
-					if (pos == begin) {
-						if (*iterator == ' ')
-							left_aligned = FALSE;
-
-						trigger = TRUE;
-					} else if (pos == end - 1) {
-						if (*iterator == ' ')
-							right_aligned = FALSE;
-
-						trigger = FALSE;
-					}
-
-					if (trigger || pos == end - 1) {
-						if (!space_trigger && *iterator == ' ') {
-							space_trigger = TRUE;
-							spaces_start = pos;
-						} else if (space_trigger && *iterator != ' ') {
-							space_trigger = FALSE;
-							num_spaces = pos - spaces_start;
-						}
-					}
-
-					iterator++;
-					pos++;
-				}
-
-				if (num_spaces < 2)
-					has_2_spaces = FALSE;
-
-				if (*iterator)
-					iterator++;
-
-				lines++;
-			}
-
-			/*
-			 * If this column meets all the criteria
-			 * split it into two at the last measured
-			 * spaces_start + num_spaces
-			 */
-			if (has_2_spaces && right_aligned && left_aligned) {
-				int val = (((spaces_start + num_spaces) - spaces_start) / 2) + spaces_start;
-
-				g_array_insert_val (parseoptions->splitpositions, i + 1, val);
-
-				/*
-				 * Skip over the inserted column
-				 */
-				i++;
-			}
-		}
-
-		/*
-		 * Remove empty columns here if needed
-		 */
-		for (i = 0; i < my_garray_len (parseoptions->splitpositions) - 1; i++) {
-			int begin = g_array_index (parseoptions->splitpositions, int, i);
-			int end = g_array_index (parseoptions->splitpositions, int, i + 1);
-			gboolean only_spaces = TRUE;
-
-			iterator = data;
-			lines = 0;
-			while (*iterator && iterator < data_end) {
-				gboolean trigger = FALSE;
-				int pos = 0;
-
-				while (*iterator && !compare_terminator (iterator, parseoptions)) {
-					if (pos == begin)
-						trigger = TRUE;
-					else if (pos == end)
-						trigger = FALSE;
-
-					if (trigger) {
-						if (*iterator != ' ')
-							only_spaces = FALSE;
-					}
-
-					iterator++;
-					pos++;
-				}
-
-				if (*iterator)
-					iterator++;
-
-				lines++;
-			}
-
-			/*
-			 * The column only contains spaces
-			 * remove it
-			 */
-			if (only_spaces) {
-				g_array_remove_index (parseoptions->splitpositions, i);
-
-				/*
-				 * We HAVE to make sure that the next column (end) also
-				 * gets checked out. If we don't decrease "i" here, we
-				 * will skip over it as the indexes shift down after
-				 * the removal
-				 */
-				i--;
-			}
-		}
-	}
-
-	g_free (line_begin_hits);
-	g_free (line_end_hits);
-}
-
-/*******************************************************************************************************
- * STF PARSE HL: high-level functions that dump the raw data returned by the low-level parsing
- *               functions into something meaningful (== application specific)
- *******************************************************************************************************/
-
-/* gboolean */
-/* stf_parse_sheet (StfParseOptions_t *parseoptions, */
-/* 		 char const *data, char const *data_end, */
-/* 		 Sheet *sheet, int start_col, int start_row) */
-/* { */
-/* 	int row, col; */
-/* 	unsigned int lrow, lcol; */
-/* 	GODateConventions const *date_conv; */
-/* 	GStringChunk *lines_chunk; */
-/* 	GPtrArray *lines, *line; */
-
-/* 	SETUP_LOCALE_SWITCH; */
-
-/* 	g_return_val_if_fail (parseoptions != NULL, FALSE); */
-/* 	g_return_val_if_fail (data != NULL, FALSE); */
-/* 	g_return_val_if_fail (IS_SHEET (sheet), FALSE); */
-
-/* 	START_LOCALE_SWITCH; */
-
-/* 	date_conv = workbook_date_conv (sheet->workbook); */
-
-/* 	if (!data_end) */
-/* 		data_end = data + strlen (data); */
-/* 	lines_chunk = g_string_chunk_new (100 * 1024); */
-/* 	lines = stf_parse_general (parseoptions, lines_chunk, data, data_end); */
-/* 	if (lines == NULL) */
-/* 		return FALSE; */
-/* 	for (row = start_row, lrow = 0; lrow < lines->len ; row++, lrow++) { */
-/* 		col = start_col; */
-/* 		line = g_ptr_array_index (lines, lrow); */
-
-/* 		for (lcol = 0; lcol < line->len; lcol++) */
-/* 			if (parseoptions->col_import_array == NULL || */
-/* 			    parseoptions->col_import_array_len <= lcol || */
-/* 			    parseoptions->col_import_array[lcol]) { */
-/* 				if (col >= SHEET_MAX_COLS) { */
-/* 					if (!parseoptions->cols_exceeded) { */
-/* 						g_warning (_("There are more columns of data than " */
-/* 							     "there is room for in the sheet.  Extra " */
-/* 							     "columns will be ignored.")); */
-/* 						parseoptions->cols_exceeded = TRUE; */
-/* 					} */
-/* 				} else { */
-/* 					char const *text = g_ptr_array_index (line, lcol); */
-/* 					if (text && *text) */
-/* 						gnm_cell_set_text ( */
-/* 							sheet_cell_fetch (sheet, col, row), */
-/* 							text); */
-/* 				} */
-/* 				col++; */
-/* 			} */
-/* 	} */
-
-/* 	stf_parse_general_free (lines); */
-/* 	g_string_chunk_free (lines_chunk); */
-/* 	END_LOCALE_SWITCH; */
-/* 	return TRUE; */
-/* } */
-
-/* GnmCellRegion * */
-/* stf_parse_region (StfParseOptions_t *parseoptions, char const *data, char const *data_end, */
-/* 		  Workbook const *wb) */
-/* { */
-/* 	static GODateConventions const default_conv = {FALSE}; */
-/* 	GODateConventions const *date_conv = wb ? workbook_date_conv (wb) : &default_conv; */
-
-/* 	GnmCellRegion *cr; */
-/* 	unsigned int row, colhigh = 0; */
-/* 	char *text; */
-/* 	GStringChunk *lines_chunk; */
-/* 	GPtrArray *lines; */
-/* 	GnmCellCopy	*cc; */
-/* 	GOFormat	*fmt; */
-/* 	GnmValue	*v; */
-
-/* 	SETUP_LOCALE_SWITCH; */
-
-/* 	g_return_val_if_fail (parseoptions != NULL, NULL); */
-/* 	g_return_val_if_fail (data != NULL, NULL); */
-
-/* 	START_LOCALE_SWITCH; */
-
-/* 	cr = cellregion_new (NULL); */
-
-/* 	if (!data_end) */
-/* 		data_end = data + strlen (data); */
-/* 	lines_chunk = g_string_chunk_new (100 * 1024); */
-/* 	lines = stf_parse_general (parseoptions, lines_chunk, data, data_end); */
-/* 	for (row = 0; row < lines->len; row++) { */
-/* 		GPtrArray *line = g_ptr_array_index (lines, row); */
-/* 		unsigned int col, targetcol = 0; */
-/* 		for (col = 0; col < line->len; col++) { */
-/* 			if (parseoptions->col_import_array == NULL || */
-/* 			    parseoptions->col_import_array_len <= col || */
-/* 			    parseoptions->col_import_array[col]) { */
-/* 				if (NULL != (text = g_ptr_array_index (line, col))) { */
-/* 					fmt = g_ptr_array_index ( */
-/* 						parseoptions->formats, col); */
-/* 					if (NULL == (v = format_match (text, fmt, date_conv))) */
-/* 						v = value_new_string (text); */
-
-/* 					cc = gnm_cell_copy_new (cr, targetcol, row); */
-/* 					cc->val  = v; */
-/* 					cc->texpr = NULL; */
-/* 					targetcol++; */
-/* 					if (targetcol > colhigh) */
-/* 						colhigh = targetcol; */
-/* 				} */
-/* 			} */
-/* 		} */
-/* 	} */
-/* 	stf_parse_general_free (lines); */
-/* 	g_string_chunk_free (lines_chunk); */
-
-/* 	END_LOCALE_SWITCH; */
-
-/* 	cr->cols    = (colhigh > 0) ? colhigh : 1; */
-/* 	cr->rows    = row; */
-
-/* 	return cr; */
-/* } */
-
-static int
-int_sort (void const *a, void const *b)
-{
-	return *(int const *)a - *(int const *)b;
-}
-
-static int
-count_character (GPtrArray *lines, gunichar c, double quantile)
-{
-	int *counts, res;
-	unsigned int lno, cno;
-
-	if (lines->len == 0)
-		return 0;
-
-	counts = g_new (int, lines->len);
-	for (lno = cno = 0; lno < lines->len; lno++) {
-		int count = 0;
-		GPtrArray *boxline = g_ptr_array_index (lines, lno);
-		char const *line = g_ptr_array_index (boxline, 0);
-
-		/* Ignore empty lines.  */
-		if (*line == 0)
-			continue;
-
-		while (*line) {
-			if (g_utf8_get_char (line) == c)
-				count++;
-			line = g_utf8_next_char (line);
-		}
-
-		counts[cno++] = count;
-	}
-
-	if (cno == 0)
-		res = 0;
-	else {
-		unsigned int qi = (unsigned int)ceil (quantile * cno);
-		qsort (counts, cno, sizeof (counts[0]), int_sort);
-		if (qi == cno)
-			qi--;
-		res = counts[qi];
-	}
-
-	g_free (counts);
-
-	return res;
-}
-
-
-StfParseOptions_t *
-stf_parse_options_guess (char const *data)
-{
-	StfParseOptions_t *res;
-	GStringChunk *lines_chunk;
-	GPtrArray *lines;
-	int tabcount;
-	int sepcount;
-	/* TODO In the future, use the goffice 0.3. */
-	/* gunichar sepchar = go_locale_get_arg_sep (); */
-	gunichar sepchar = ',';
-
-	g_return_val_if_fail (data != NULL, NULL);
-
-	res = stf_parse_options_new ();
-	lines_chunk = g_string_chunk_new (100 * 1024);
-	lines = stf_parse_lines (res, lines_chunk, data, SHEET_MAX_ROWS, FALSE);
-
-	tabcount = count_character (lines, '\t', 0.2);
-	sepcount = count_character (lines, sepchar, 0.2);
-
-	/* At least one tab per line and enough to separate every
-	   would-be sepchars.  */
-	if (tabcount >= 1 && tabcount >= sepcount - 1)
-		stf_parse_options_csv_set_separators (res, "\t", NULL);
-	else {
-		gunichar c;
-
-		/*
-		 * Try a few more or less likely characters and pick the first
-		 * one that occurs on at least half the lines.
-		 *
-		 * The order is mostly random, although ' ' and '!' which
-		 * could very easily occur in text are put last.
-		 */
-		/* TODO Replace with the 0.3 goffice call in the future. */
-		if (count_character (lines, (c = sepchar), 0.5) > 0 ||
-		    /* count_character (lines, (c = go_locale_get_col_sep ()), 0.5) > 0 || */
-		    count_character (lines, (c = ','), 0.5) > 0 ||
-		    count_character (lines, (c = ':'), 0.5) > 0 ||
-		    count_character (lines, (c = ','), 0.5) > 0 ||
-		    count_character (lines, (c = ';'), 0.5) > 0 ||
-		    count_character (lines, (c = '|'), 0.5) > 0 ||
-		    count_character (lines, (c = '!'), 0.5) > 0 ||
-		    count_character (lines, (c = ' '), 0.5) > 0) {
-			char sep[7];
-			sep[g_unichar_to_utf8 (c, sep)] = 0;
-			if (c == ' ')
-				strcat (sep, "\t");
-			stf_parse_options_csv_set_separators (res, sep, NULL);
-		}
-	}
-
-	if (1) {
-		/* Separated */
-		gboolean dups =
-			res->sep.chr &&
-			strchr (res->sep.chr, ' ') != NULL;
-		gboolean trim =
-			res->sep.chr &&
-			strchr (res->sep.chr, ' ') != NULL;
-
-		stf_parse_options_set_type (res, PARSE_TYPE_CSV);
-		stf_parse_options_set_trim_spaces (res, TRIM_TYPE_LEFT | TRIM_TYPE_RIGHT);
-		stf_parse_options_csv_set_indicator_2x_is_single (res, TRUE);
-		stf_parse_options_csv_set_duplicates (res, dups);
-		stf_parse_options_csv_set_trim_seps (res, trim);
-
-		stf_parse_options_csv_set_stringindicator (res, '"');
-	} else {
-		/* Fixed-width */
-	}
-
-	stf_parse_general_free (lines);
-	g_string_chunk_free (lines_chunk);
-
-	return res;
-}
diff --git a/lib/stf/stf-parse.h b/lib/stf/stf-parse.h
deleted file mode 100644
index 71e5510..0000000
--- a/lib/stf/stf-parse.h
+++ /dev/null
@@ -1,112 +0,0 @@
-#ifndef STF_PARSE_H
-#define STF_PARSE_H
-
-#include <glib.h>
-
-#define SHEET_MAX_ROWS          (16*16*16*16)   /* 0, 1, ... */
-#define SHEET_MAX_COLS          (4*4*4*4)       /* 0, 1, ... */
-
-typedef enum {
-	PARSE_TYPE_NOTSET    = 1 << 0,
-	PARSE_TYPE_CSV       = 1 << 1,
-	PARSE_TYPE_FIXED     = 1 << 2
-} StfParseType_t;
-
-/* Additive.  */
-typedef enum {
-	TRIM_TYPE_NEVER      = 0,
-	TRIM_TYPE_LEFT       = 1 << 0,
-	TRIM_TYPE_RIGHT      = 2 << 1
-} StfTrimType_t;
-
-typedef struct {
-	StfParseType_t       parsetype;             /* The type of import to do */
-	StfTrimType_t        trim_spaces;           /* Trim spaces in fields? */
-
-	GSList *             terminator;            /* Line terminators */
-	char *               locale;
-
-	struct {
-		guchar       min, max;
-	} compiled_terminator;
-     
-	/* CSV related */
-	struct {
-		GSList *str;
-		char   *chr;
-	} sep;
-	gunichar             stringindicator;       /* String indicator */
-	gboolean             indicator_2x_is_single;/* 2 quote chars form a single non-terminating quote */
-	gboolean             duplicates;            /* See two text separators as one? */
-	gboolean             trim_seps;             /* Ignore initial seps.  */
-     
-	/* Fixed width related */
-	GArray              *splitpositions;        /* Positions where text will be split vertically */
-     
-	int                  rowcount;              /* Number of rows parsed */
-	int                  colcount;              /* Number of columns parsed */
-        gboolean             *col_import_array;     /* 0/1 array indicating  */
-	                                            /* which cols to import  */
-	unsigned int         col_import_array_len;
-	GPtrArray            *formats       ;       /* Contains GnmFormat *s */
-	gboolean             cols_exceeded;         /* This is set to TRUE if */
-	                                            /* we tried to import more than */
-	                                            /* SHEET_MAX_COLS columns */
-} StfParseOptions_t;
-
-/* CREATION/DESTRUCTION of stf options struct */
-
-StfParseOptions_t  *stf_parse_options_new                             (void);
-void                stf_parse_options_free                            (StfParseOptions_t *parseoptions);
-
-StfParseOptions_t  *stf_parse_options_guess                           (char const *data);
-
-/* MANIPULATION of stf options struct */
-
-void stf_parse_options_set_type                        (StfParseOptions_t *parseoptions,
-							StfParseType_t const parsetype);
-void stf_parse_options_clear_line_terminator           (StfParseOptions_t *parseoptions);
-void stf_parse_options_add_line_terminator             (StfParseOptions_t *parseoptions,
-							char const *terminator);
-void stf_parse_options_set_trim_spaces                 (StfParseOptions_t *parseoptions,
-							StfTrimType_t const trim_spaces);
-void stf_parse_options_csv_set_separators              (StfParseOptions_t *parseoptions,
-							char const *character, GSList const *string);
-void stf_parse_options_csv_set_stringindicator         (StfParseOptions_t *parseoptions,
-							gunichar const stringindicator);
-void stf_parse_options_csv_set_indicator_2x_is_single  (StfParseOptions_t *parseoptions,
-							gboolean const indic_2x);
-void stf_parse_options_csv_set_duplicates              (StfParseOptions_t *parseoptions,
-							gboolean const duplicates);
-void stf_parse_options_csv_set_trim_seps               (StfParseOptions_t *parseoptions,
-							gboolean const trim_seps);
-void stf_parse_options_fixed_splitpositions_clear      (StfParseOptions_t *parseoptions);
-void stf_parse_options_fixed_splitpositions_add        (StfParseOptions_t *parseoptions,
-							int position);
-void stf_parse_options_fixed_splitpositions_remove     (StfParseOptions_t *parseoptions,
-							int position);
-int stf_parse_options_fixed_splitpositions_count       (StfParseOptions_t *parseoptions);
-int stf_parse_options_fixed_splitpositions_nth         (StfParseOptions_t *parseoptions, int n);
-
-/* USING the stf structs to actually do some parsing, these are the lower-level functions and utility functions */
-
-GPtrArray	*stf_parse_general			(StfParseOptions_t *parseoptions,
-							 GStringChunk *lines_chunk,
-							 char const *data,
-							 char const *data_end);
-void		 stf_parse_general_free			(GPtrArray *lines);
-GPtrArray	*stf_parse_lines			(StfParseOptions_t *parseoptions,
-							 GStringChunk *lines_chunk,
-							 char const *data,
-							 int maxlines,
-							 gboolean with_lineno);
-
-void		 stf_parse_options_fixed_autodiscover	(StfParseOptions_t *parseoptions,
-							 char const *data,
-							 char const *data_end);
-
-char const	*stf_parse_find_line			(StfParseOptions_t *parseoptions,
-							 char const *data,
-							 int line);
-
-#endif
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 7525756..1d9b7c3 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -96,6 +96,7 @@ src/backend/xml/gnc-schedxaction-xml-v2.cpp
 src/backend/xml/gnc-tax-table-xml-v2.cpp
 src/backend/xml/gnc-transaction-xml-v2.cpp
 src/backend/xml/gnc-vendor-xml-v2.cpp
+src/backend/xml/gnc-xml-backend.cpp
 src/backend/xml/gnc-xml-helper.cpp
 src/backend/xml/io-example-account.cpp
 src/backend/xml/io-gncxml-gen.cpp
@@ -427,22 +428,23 @@ src/import-export/csv-exp/gnc-plugin-csv-export.c
 [type: gettext/gsettings]src/import-export/csv-exp/gschemas/org.gnucash.dialogs.export.csv.gschema.xml.in.in
 src/import-export/csv-imp/assistant-csv-account-import.c
 src/import-export/csv-imp/assistant-csv-account-import.glade
-src/import-export/csv-imp/assistant-csv-fixed-trans-import.c
+src/import-export/csv-imp/assistant-csv-fixed-trans-import.cpp
 src/import-export/csv-imp/assistant-csv-fixed-trans-import.glade
-src/import-export/csv-imp/assistant-csv-trans-import.c
+src/import-export/csv-imp/assistant-csv-trans-import.cpp
 src/import-export/csv-imp/assistant-csv-trans-import.glade
 src/import-export/csv-imp/csv-account-import.c
-src/import-export/csv-imp/csv-fixed-trans-import.c
+src/import-export/csv-imp/csv-fixed-trans-import.cpp
 src/import-export/csv-imp/gnc-csv-account-map.c
 src/import-export/csv-imp/gnc-csv-gnumeric-popup.c
-src/import-export/csv-imp/gnc-csv-imp-trans.cpp
-src/import-export/csv-imp/gnc-csv-model.c
 src/import-export/csv-imp/gnc-csv-tokenizer.cpp
 src/import-export/csv-imp/gnc-csv-trans-settings.c
+src/import-export/csv-imp/gnc-dummy-tokenizer.cpp
 src/import-export/csv-imp/gnc-fw-tokenizer.cpp
 src/import-export/csv-imp/gncmod-csv-import.c
 src/import-export/csv-imp/gnc-plugin-csv-import.c
 src/import-export/csv-imp/gnc-tokenizer.cpp
+src/import-export/csv-imp/gnc-trans-props.cpp
+src/import-export/csv-imp/gnc-tx-import.cpp
 [type: gettext/gsettings]src/import-export/csv-imp/gschemas/org.gnucash.dialogs.import.csv.gschema.xml.in.in
 src/import-export/dialog-import.glade
 src/import-export/gncmod-generic-import.c
diff --git a/src/import-export/csv-exp/Makefile.am b/src/import-export/csv-exp/Makefile.am
index 1f6963b..bff332c 100644
--- a/src/import-export/csv-exp/Makefile.am
+++ b/src/import-export/csv-exp/Makefile.am
@@ -25,10 +25,9 @@ libgncmod_csv_export_la_LIBADD = \
   ${top_builddir}/src/engine/libgncmod-engine.la \
   ${top_builddir}/src/core-utils/libgnc-core-utils.la \
   ${top_builddir}/src/gnc-module/libgnc-module.la \
-  ${top_builddir}/lib/stf/libgnc-stf.la \
   ${top_builddir}/lib/libc/libc-missing.la \
   ${top_builddir}/src/libqof/qof/libgnc-qof.la \
-  ${GOFFICE_LIBS} \
+  ${GTK_LIBS} \
   ${GLIB_LIBS}
 
 AM_CPPFLAGS = \
@@ -48,7 +47,7 @@ AM_CPPFLAGS = \
   -I${top_srcdir}/lib \
   ${GUILE_CFLAGS} \
   ${GLIB_CFLAGS} \
-  $(GOFFICE_CFLAGS)
+  $(GTK_CFLAGS)
 
 uidir = $(GNC_UI_DIR)
 ui_DATA = \
diff --git a/src/import-export/csv-imp/CMakeLists.txt b/src/import-export/csv-imp/CMakeLists.txt
index 4b5fe58..2a978d3 100644
--- a/src/import-export/csv-imp/CMakeLists.txt
+++ b/src/import-export/csv-imp/CMakeLists.txt
@@ -4,13 +4,12 @@ ADD_SUBDIRECTORY(test)
 SET(csv_import_SOURCES
   gncmod-csv-import.c
   assistant-csv-account-import.c
-  assistant-csv-fixed-trans-import.c
+  assistant-csv-fixed-trans-import.cpp
   assistant-csv-trans-import.cpp
   gnc-plugin-csv-import.c
   csv-account-import.c
-  csv-fixed-trans-import.c
+  csv-fixed-trans-import.cpp
   gnc-csv-account-map.c
-  gnc-csv-model.c
   gnc-csv-gnumeric-popup.c
   gnc-csv-tokenizer.cpp
   gnc-csv-trans-settings.c
@@ -19,7 +18,6 @@ SET(csv_import_SOURCES
   gnc-tokenizer.cpp
   gnc-trans-props.cpp
   gnc-tx-import.cpp
-  ${CMAKE_SOURCE_DIR}/lib/stf/stf-parse.c
   ${CMAKE_SOURCE_DIR}/lib/goffice/go-charmap-sel.c
   ${CMAKE_SOURCE_DIR}/lib/goffice/go-optionmenu.c
   ${CMAKE_SOURCE_DIR}/lib/goffice/go-glib-extras.c
@@ -36,7 +34,6 @@ SET(csv_import_noinst_HEADERS
   csv-account-import.h
   csv-fixed-trans-import.h
   gnc-csv-account-map.h
-  gnc-csv-model.h
   gnc-csv-gnumeric-popup.h
   gnc-csv-tokenizer.hpp
   gnc-csv-trans-settings.h
@@ -45,7 +42,6 @@ SET(csv_import_noinst_HEADERS
   gnc-tokenizer.hpp
   gnc-trans-props.hpp
   gnc-tx-import.hpp
-  ${CMAKE_SOURCE_DIR}/lib/stf/stf-parse.h
   ${CMAKE_SOURCE_DIR}/lib/goffice/go-charmap-sel.h
   ${CMAKE_SOURCE_DIR}/lib/goffice/go-optionmenu.h
   ${CMAKE_SOURCE_DIR}/lib/goffice/go-glib-extras.h
@@ -53,14 +49,15 @@ SET(csv_import_noinst_HEADERS
 
 ADD_LIBRARY(gncmod-csv-import ${csv_import_noinst_HEADERS} ${csv_import_SOURCES})
 
-TARGET_LINK_LIBRARIES(gncmod-csv-import ${GOFFICE_LDFLAGS} ${Boost_LIBRARIES} gncmod-generic-import gncmod-gnome-utils
+TARGET_LINK_LIBRARIES(gncmod-csv-import ${Boost_LIBRARIES} gncmod-generic-import gncmod-gnome-utils
                  gncmod-app-utils gncmod-engine gnc-core-utils gnc-module)
 
 
 TARGET_COMPILE_DEFINITIONS(gncmod-csv-import PRIVATE -DG_LOG_DOMAIN=\"gnc.import.csv\")
 
 TARGET_INCLUDE_DIRECTORIES(gncmod-csv-import PRIVATE
-     ${CMAKE_SOURCE_DIR}/lib ${GOFFICE_INCLUDE_DIRS}
+     ${CMAKE_SOURCE_DIR}/lib
+     ${CMAKE_SOURCE_DIR}/lib/goffice
 )
 
 INSTALL(TARGETS gncmod-csv-import
diff --git a/src/import-export/csv-imp/Makefile.am b/src/import-export/csv-imp/Makefile.am
index 0014c88..66b97aa 100644
--- a/src/import-export/csv-imp/Makefile.am
+++ b/src/import-export/csv-imp/Makefile.am
@@ -5,13 +5,12 @@ pkglib_LTLIBRARIES=libgncmod-csv-import.la
 libgncmod_csv_import_la_SOURCES = \
   gncmod-csv-import.c \
   assistant-csv-account-import.c \
-  assistant-csv-fixed-trans-import.c \
+  assistant-csv-fixed-trans-import.cpp \
   assistant-csv-trans-import.cpp \
   gnc-plugin-csv-import.c \
   csv-account-import.c \
-  csv-fixed-trans-import.c \
+  csv-fixed-trans-import.cpp \
   gnc-csv-account-map.c \
-  gnc-csv-model.c \
   gnc-csv-tokenizer.cpp \
   gnc-csv-gnumeric-popup.c \
   gnc-dummy-tokenizer.cpp \
@@ -29,7 +28,6 @@ noinst_HEADERS = \
   csv-account-import.h \
   csv-fixed-trans-import.h \
   gnc-csv-account-map.h \
-  gnc-csv-model.h \
   gnc-csv-tokenizer.hpp \
   gnc-csv-gnumeric-popup.h \
   gnc-dummy-tokenizer.hpp \
@@ -49,11 +47,10 @@ libgncmod_csv_import_la_LIBADD = \
   ${top_builddir}/src/core-utils/libgnc-core-utils.la \
   ${top_builddir}/src/gnc-module/libgnc-module.la \
   ${top_builddir}/lib/goffice/libgnc-goffice.la \
-  ${top_builddir}/lib/stf/libgnc-stf.la \
   ${top_builddir}/lib/libc/libc-missing.la \
   ${top_builddir}/src/libqof/qof/libgnc-qof.la \
-  ${GOFFICE_LIBS} \
   ${GLIB_LIBS} \
+  ${GTK_LIBS} \
   ${BOOST_LDFLAGS} \
   -lboost_regex \
   -lboost_locale
@@ -69,10 +66,11 @@ AM_CPPFLAGS = \
   -I${top_srcdir}/src/import-export \
   -I${top_srcdir}/src/libqof/qof \
   -I${top_srcdir}/lib/libc \
+  -I${top_srcdir}/lib/goffice \
   -I${top_srcdir}/lib \
   ${GUILE_CFLAGS} \
   ${GLIB_CFLAGS} \
-  $(GOFFICE_CFLAGS) \
+  ${GTK_CFLAGS} \
   ${BOOST_CPPFLAGS}
 
 uidir = $(GNC_UI_DIR)
diff --git a/src/import-export/csv-imp/assistant-csv-fixed-trans-import.c b/src/import-export/csv-imp/assistant-csv-fixed-trans-import.cpp
similarity index 97%
rename from src/import-export/csv-imp/assistant-csv-fixed-trans-import.c
rename to src/import-export/csv-imp/assistant-csv-fixed-trans-import.cpp
index 355fefc..a92bd50 100644
--- a/src/import-export/csv-imp/assistant-csv-fixed-trans-import.c
+++ b/src/import-export/csv-imp/assistant-csv-fixed-trans-import.cpp
@@ -25,6 +25,12 @@
     @brief CSV Import Assistant
     @author Copyright (c) 2012 Robert Fewell
 */
+
+#include <guid.hpp>
+
+extern "C"
+{
+
 #include "config.h"
 
 #include <gtk/gtk.h>
@@ -39,7 +45,9 @@
 
 #include "assistant-csv-fixed-trans-import.h"
 #include "csv-fixed-trans-import.h"
-#include "gnc-csv-model.h"
+}
+
+#include "gnc-tx-import.hpp"
 
 #define GNC_PREFS_GROUP "dialogs.import.csv"
 #define ASSISTANT_CSV_FIXED_TRANS_IMPORT_CM_CLASS "assistant-csv-fixed-trans-import"
@@ -49,6 +57,8 @@ static QofLogModule log_module = GNC_MOD_ASSISTANT;
 
 /*************************************************************************/
 
+extern "C"
+{
 void csv_fixed_trans_import_assistant_prepare (GtkAssistant *assistant, GtkWidget *page, gpointer user_data);
 void csv_fixed_trans_import_assistant_finish (GtkAssistant *gtkassistant, gpointer user_data);
 void csv_fixed_trans_import_assistant_cancel (GtkAssistant *gtkassistant, gpointer user_data);
@@ -64,6 +74,7 @@ void csv_fixed_trans_import_sep_cb (GtkWidget *radio, gpointer user_data );
 void csv_fixed_trans_import_hrows_cb (GtkWidget *spin, gpointer user_data );
 
 void csv_fixed_trans_import_file_chooser_confirm_cb (GtkWidget *button, CsvFTImportInfo *info);
+}
 
 static gchar *gnc_input_dialog (GtkWidget *parent, const gchar *title, const gchar *msg, const gchar *default_input);
 
@@ -93,7 +104,7 @@ static gchar *mnemonic_escape (const gchar *source)
     g_return_val_if_fail (source != NULL, NULL);
 
     p = (guchar *) source;
-    q = dest = g_malloc (strlen (source) * 2 + 1);
+    q = dest = (char *)g_malloc (strlen (source) * 2 + 1);
 
     while (*p)
     {
@@ -201,7 +212,7 @@ csv_fixed_trans_import_file_chooser_confirm_cb (GtkWidget *button, CsvFTImportIn
  *******************************************************/
 void csv_fixed_trans_import_hrows_cb (GtkWidget *spin, gpointer user_data)
 {
-    CsvFTImportInfo *info = user_data;
+    CsvFTImportInfo *info = (CsvFTImportInfo*)user_data;
 
     GtkTreeIter iter;
     gboolean valid;
@@ -242,10 +253,10 @@ void csv_fixed_trans_import_hrows_cb (GtkWidget *spin, gpointer user_data)
  *******************************************************/
 void csv_fixed_trans_import_sep_cb (GtkWidget *radio, gpointer user_data)
 {
-    CsvFTImportInfo *info = user_data;
+    CsvFTImportInfo *info = (CsvFTImportInfo*)user_data;
     const gchar *name;
     gchar *temp;
-    gchar *sep = NULL;
+    const gchar *sep = NULL;
 
     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(radio)))
     {
@@ -322,7 +333,7 @@ static void
 load_settings (CsvFTImportInfo *info)
 {
     info->header_rows = 0;
-    info->error = "";
+    info->error = g_strdup("");
     info->starting_dir = NULL;
     info->file_name = NULL;
     info->date_format = 0;
@@ -362,7 +373,7 @@ gnc_input_dialog (GtkWidget *parent, const gchar *title, const gchar *msg, const
 
     /* Create the widgets */
     dialog = gtk_dialog_new_with_buttons (title, GTK_WINDOW(parent),
-                                          GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+                                          (GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
                                           GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
                                           GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
                                           NULL);
@@ -422,7 +433,7 @@ void
 csv_fixed_trans_import_file_page_prepare (GtkAssistant *assistant,
                                         gpointer user_data)
 {
-    CsvFTImportInfo *info = user_data;
+    CsvFTImportInfo *info = (CsvFTImportInfo*)user_data;
     gint num = gtk_assistant_get_current_page (assistant);
     GtkWidget *page = gtk_assistant_get_nth_page (assistant, num);
 
@@ -441,7 +452,7 @@ void
 csv_fixed_trans_import_view_page_prepare (GtkAssistant *assistant,
         gpointer user_data)
 {
-    CsvFTImportInfo *info = user_data;
+    CsvFTImportInfo *info = (CsvFTImportInfo*)user_data;
 
     gtk_list_store_clear (info->store);
 
@@ -462,7 +473,7 @@ void
 csv_fixed_trans_import_finish_page_prepare (GtkAssistant *assistant,
         gpointer user_data)
 {
-    CsvFTImportInfo *info = user_data;
+    CsvFTImportInfo *info = (CsvFTImportInfo*)user_data;
     gint num = gtk_assistant_get_current_page (assistant);
     GtkWidget *page = gtk_assistant_get_nth_page (assistant, num);
     gchar *text;
@@ -497,7 +508,7 @@ void
 csv_fixed_trans_import_summary_page_prepare (GtkAssistant *assistant,
         gpointer user_data)
 {
-    CsvFTImportInfo *info = user_data;
+    CsvFTImportInfo *info = (CsvFTImportInfo*)user_data;
     gchar *text, *errtext, *mtext;
 
     /* Before creating accounts, if this is a new book, let user specify
@@ -567,7 +578,7 @@ csv_fixed_trans_import_assistant_prepare (GtkAssistant *assistant, GtkWidget *pa
 static void
 csv_fixed_trans_import_assistant_destroy_cb (GtkObject *object, gpointer user_data)
 {
-    CsvFTImportInfo *info = user_data;
+    CsvFTImportInfo *info = (CsvFTImportInfo*)user_data;
     gnc_unregister_gui_component_by_data (ASSISTANT_CSV_FIXED_TRANS_IMPORT_CM_CLASS, info);
     g_free (info);
 }
@@ -575,21 +586,21 @@ csv_fixed_trans_import_assistant_destroy_cb (GtkObject *object, gpointer user_da
 void
 csv_fixed_trans_import_assistant_cancel (GtkAssistant *assistant, gpointer user_data)
 {
-    CsvFTImportInfo *info = user_data;
+    CsvFTImportInfo *info = (CsvFTImportInfo*)user_data;
     gnc_close_gui_component_by_data (ASSISTANT_CSV_FIXED_TRANS_IMPORT_CM_CLASS, info);
 }
 
 void
 csv_fixed_trans_import_assistant_close (GtkAssistant *assistant, gpointer user_data)
 {
-    CsvFTImportInfo *info = user_data;
+    CsvFTImportInfo *info = (CsvFTImportInfo*)user_data;
     gnc_close_gui_component_by_data (ASSISTANT_CSV_FIXED_TRANS_IMPORT_CM_CLASS, info);
 }
 
 void
 csv_fixed_trans_import_assistant_finish (GtkAssistant *assistant, gpointer user_data)
 {
-    CsvFTImportInfo *info = user_data;
+    CsvFTImportInfo *info = (CsvFTImportInfo*)user_data;
 
     gtk_list_store_clear (info->store);
     csv_fixed_trans_import_read_file (info->file_name, info->regexp->str, info->store, 0 );
@@ -599,7 +610,7 @@ csv_fixed_trans_import_assistant_finish (GtkAssistant *assistant, gpointer user_
 static void
 csv_fixed_trans_import_close_handler (gpointer user_data)
 {
-    CsvFTImportInfo *info = user_data;
+    CsvFTImportInfo *info = (CsvFTImportInfo*)user_data;
 
     g_free (info->starting_dir);
     g_free (info->file_name);
diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index 1ae08f0..dc89c0c 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -54,7 +54,7 @@ extern "C"
 #include "gnc-csv-account-map.h"
 
 #include "gnc-csv-gnumeric-popup.h"
-#include <goffice/go-charmap-sel.h>
+#include "go-charmap-sel.h"
 }
 
 #include "gnc-tx-import.hpp"
diff --git a/src/import-export/csv-imp/csv-fixed-trans-import.c b/src/import-export/csv-imp/csv-fixed-trans-import.cpp
similarity index 93%
rename from src/import-export/csv-imp/csv-fixed-trans-import.c
rename to src/import-export/csv-imp/csv-fixed-trans-import.cpp
index 64a74cc..a6762e5 100644
--- a/src/import-export/csv-imp/csv-fixed-trans-import.c
+++ b/src/import-export/csv-imp/csv-fixed-trans-import.cpp
@@ -25,6 +25,10 @@
  * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
 \********************************************************************/
 
+#include <guid.hpp>
+
+extern "C"
+{
 #include "config.h"
 
 #include <glib.h>
@@ -36,11 +40,12 @@
 #include "Account.h"
 #include "gnc-component-manager.h"
 #include "csv-fixed-trans-import.h"
-#include "gnc-csv-model.h"
 #include "gnc-ui-util.h"
 #include "engine-helpers.h"
 #include "gnc-gdate-utils.h"
+}
 
+#include "gnc-trans-props.hpp"
 /* This static indicates the debugging module that this .o belongs to. */
 static QofLogModule log_module = GNC_MOD_ASSISTANT;
 
@@ -91,7 +96,7 @@ csv_fixed_trans_import_read_file (const gchar *filename, const gchar *parser_reg
     GMatchInfo *match_info = NULL;
     GRegex     *regexpat = NULL;
     GError     *err;
-    gint       row = 0;
+    guint       row = 0;
     gboolean   match_found = FALSE;
 
     // model
@@ -109,7 +114,7 @@ csv_fixed_trans_import_read_file (const gchar *filename, const gchar *parser_reg
     // compile the regular expression and check for errors
     err = NULL;
     regexpat =
-        g_regex_new (parser_regexp, G_REGEX_OPTIMIZE, 0, &err);
+        g_regex_new (parser_regexp, G_REGEX_OPTIMIZE, static_cast<GRegexMatchFlags>(0), &err);
     if (err != NULL)
     {
         GtkWidget *dialog;
@@ -131,7 +136,7 @@ csv_fixed_trans_import_read_file (const gchar *filename, const gchar *parser_reg
         return RESULT_ERROR_IN_REGEXP;
     }
 
-    g_regex_match (regexpat, contents, 0, &match_info);
+    g_regex_match (regexpat, contents, static_cast<GRegexMatchFlags>(0), &match_info);
     while (g_match_info_matches (match_info))
     {
         match_found = TRUE;
@@ -278,10 +283,15 @@ csv_fixed_trans_test_one_line (CsvFTImportInfo *info)
 
         if (g_strcmp0 (row_type, "T") == 0) // We have the Transaction line
         {
-            if (parse_date (date, info->date_format) == -1) // invalid date
-                date_ok = FALSE;
-            else
+            try
+            {
                 date_ok = TRUE;
+                parse_date (date, info->date_format);
+            }
+            catch (...)
+            {
+                date_ok = FALSE;
+            }
             trans_found = TRUE;
         }
 
@@ -342,9 +352,9 @@ check_for_existing_trans (CsvFTImportInfo *info, Account *acc, Transaction *tran
     qof_query_set_book (q, book);
 
     /* Sort by transaction date */
-    p1 = g_slist_prepend (NULL, TRANS_DATE_POSTED);
-    p1 = g_slist_prepend (p1, SPLIT_TRANS);
-    p2 = g_slist_prepend (NULL, QUERY_DEFAULT_SORT);
+    p1 = g_slist_prepend (NULL, gpointer(TRANS_DATE_POSTED));
+    p1 = g_slist_prepend (p1, gpointer(SPLIT_TRANS));
+    p2 = g_slist_prepend (NULL, gpointer(QUERY_DEFAULT_SORT));
     qof_query_set_sort_order (q, p1, p2, NULL);
 
     xaccQueryAddSingleAccountMatch (q, acc, QOF_QUERY_AND);
@@ -380,7 +390,7 @@ check_for_existing_trans (CsvFTImportInfo *info, Account *acc, Transaction *tran
     {
         Split *split;
         GList *first_split = g_list_first (splits);
-        split = first_split->data; 
+        split = static_cast<Split*>(first_split->data);
         if (xaccTransGetTxnType (trans) == xaccTransGetTxnType (xaccSplitGetParent (split)))
             ret = TRUE;
         else
@@ -475,9 +485,17 @@ csv_fixed_trans_import (CsvFTImportInfo *info)
             new_trans = NULL; // Reset new_trans to NULL
             trow = row; //record the row the Transaction was on
 
-            if (parse_date (date, info->date_format) == -1) // invalid date
+            time64 tdate = 0;
+            time64 idate = 0;
+            try
             {
-                save_error_text (info, row, _("Date is invalid for Transaction"));
+                tdate = parse_date (date, info->date_format);
+                if (g_strcmp0 (type, "I") == 0) // Invoice Transaction Type
+                    idate = parse_date (sdate, info->date_format);
+            }
+            catch (...)
+            {
+                save_error_text (info, row, _("Date or invoice date is invalid for Transaction"));
                 trans_error = TRUE;
             }
 
@@ -503,7 +521,7 @@ csv_fixed_trans_import (CsvFTImportInfo *info)
 
                 xaccTransBeginEdit (new_trans);
                 xaccTransSetCurrency (new_trans, trans_commodity);
-                xaccTransSetDatePostedSecsNormalized (new_trans, parse_date (date, info->date_format));
+                xaccTransSetDatePostedSecsNormalized (new_trans, tdate);
                 if (g_strcmp0 (description, "") != 0)
                     xaccTransSetDescription (new_trans, description);
                 if (g_strcmp0 (notes, "") != 0)
@@ -514,7 +532,7 @@ csv_fixed_trans_import (CsvFTImportInfo *info)
                 if (g_strcmp0 (type, "I") == 0) // Invoice Transaction Type
                 {
                     Timespec ts;
-                    timespecFromTime64 (&ts, parse_date (sdate, info->date_format));
+                    timespecFromTime64 (&ts, idate);
                     xaccTransSetTxnType (new_trans, 'I');
                     xaccTransSetDateDueTS (new_trans, &ts);
                 }
@@ -562,6 +580,18 @@ csv_fixed_trans_import (CsvFTImportInfo *info)
                 split_error = TRUE;
             }
 
+            time64 rdate = 0;
+            try
+            {
+                if (g_strcmp0 (reconcile, _("y")) == 0) // Reconciled
+                    rdate = parse_date (sdate, info->date_format);
+            }
+            catch (...)
+            {
+                save_error_text (info, row, _("Reconcile date is invalid for Split"));
+                split_error = TRUE;
+            }
+
             // Lets get some numbers
             num_error = parse_number_string (info, amount_num, &amount);
             rate_error = parse_number_string (info, rate, &price_rate);
@@ -622,7 +652,7 @@ csv_fixed_trans_import (CsvFTImportInfo *info)
                 else if (g_strcmp0 (reconcile, _("y")) == 0) // Reconciled
                 {
                     Timespec ts;
-                    timespecFromTime64 (&ts, parse_date (sdate, info->date_format));
+                    timespecFromTime64 (&ts, rdate);
                     xaccSplitSetDateReconciledTS (split, &ts);
                     rec = 'y';
                 }
diff --git a/src/import-export/csv-imp/gnc-csv-model.c b/src/import-export/csv-imp/gnc-csv-model.c
deleted file mode 100644
index 04244da..0000000
--- a/src/import-export/csv-imp/gnc-csv-model.c
+++ /dev/null
@@ -1,1345 +0,0 @@
-/********************************************************************\
- * 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 2 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, contact:                        *
- *                                                                  *
- * Free Software Foundation           Voice:  +1-617-542-5942       *
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
- * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
- *                                                                  *
-\********************************************************************/
-
-#include "gnc-csv-model.h"
-
-#include <platform.h>
-#if PLATFORM(WINDOWS)
-#include <windows.h>
-#endif
-
-#include <glib/gi18n.h>
-#include <goffice/go-glib-extras.h>
-
-#include "gnc-csv-account-map.h"
-
-#include "gnc-ui-util.h"
-#include "engine-helpers.h"
-
-#include <string.h>
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <regex.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#include <math.h>
-
-GQuark
-gnc_csv_imp_error_quark (void)
-{
-  return g_quark_from_static_string ("g-csv-imp-error-quark");
-}
-
-G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT;
-
-const int num_date_formats = 5;
-const gchar* date_format_user[] = {N_("y-m-d"),
-                                   N_("d-m-y"),
-                                   N_("m-d-y"),
-                                   N_("d-m"),
-                                   N_("m-d")
-                                  };
-
-const int num_currency_formats = 3;
-const gchar* currency_format_user[] = {N_("Locale"),
-                                       N_("Period: 123,456.78"),
-                                       N_("Comma: 123.456,78")
-                                      };
-
-/* This array contains all of the different strings for different column types. */
-gchar* gnc_csv_column_type_strs[GNC_CSV_NUM_COL_TYPES] = {N_("None"),
-                                                          N_("Date"),
-                                                          N_("Num"),
-                                                          N_("Description"),
-                                                          N_("Notes"),
-                                                          N_("Account"),
-                                                          N_("Deposit"),
-                                                          N_("Withdrawal"),
-                                                          N_("Balance"),
-                                                          N_("Memo"),
-                                                          N_("Other Account"),
-                                                          N_("Other Memo")
-                                                         };
-
-/** A set of sensible defaults for parsing CSV files.
- * @return StfParseOptions_t* for parsing a file with comma separators
- */
-static StfParseOptions_t* default_parse_options (void)
-{
-    StfParseOptions_t* options = stf_parse_options_new();
-    stf_parse_options_set_type (options, PARSE_TYPE_CSV);
-    stf_parse_options_csv_set_separators (options, ",", NULL);
-    return options;
-}
-
-/** Parses a string into a date, given a format. The format must
- * include the year. This function should only be called by
- * parse_date.
- * @param date_str The string containing a date being parsed
- * @param format An index specifying a format in date_format_user
- * @return The parsed value of date_str on success or -1 on failure
- */
-static time64 parse_date_with_year (const char* date_str, int format)
-{
-
-    int i, j, mem_length, year = -1, month = -1, day = -1;
-    Timespec ts;
-    /* Buffer for containing individual parts (e.g. year, month, day) of a date */
-    char date_segment[5];
-
-    /* The compiled regular expression */
-    regex_t preg = {0};
-
-    /* An array containing indices specifying the matched substrings in date_str */
-    regmatch_t pmatch[4] = { {0}, {0}, {0}, {0} };
-
-    /* The regular expression for parsing dates */
-    const char* regex = "^ *([0-9]+) *[-/.'] *([0-9]+) *[-/.'] *([0-9]+).*$|^ *([0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]).*$";
-
-    /* We get our matches using the regular expression. */
-    regcomp (&preg, regex, REG_EXTENDED);
-    regexec (&preg, date_str, 4, pmatch, 0);
-    regfree (&preg);
-
-    /* If there wasn't a match, there was an error. */
-    if (pmatch[0].rm_eo == 0)
-        return -1;
-
-    /* If this is a string without separators ... */
-    if (pmatch[1].rm_so == -1)
-    {
-        /* ... we will fill in the indices based on the user's selection. */
-        int k = 0; /* k traverses date_str by keeping track of where separators "should" be. */
-        j = 1; /* j traverses pmatch. */
-        for (i = 0; date_format_user[format][i]; i++)
-        {
-            char segment_type = date_format_user[format][i];
-            /* Only do something if this is a meaningful character */
-            if (segment_type == 'y' || segment_type == 'm' || segment_type == 'd')
-            {
-                pmatch[j].rm_so = k;
-                switch (segment_type)
-                {
-                case 'm':
-                case 'd':
-                    k += 2;
-                    break;
-
-                case 'y':
-                    k += 4;
-                    break;
-                }
-
-                pmatch[j].rm_eo = k;
-                j++;
-            }
-        }
-    }
-
-    /* j traverses pmatch (index 0 contains the entire string, so we
-     * start at index 1 for the first meaningful match). */
-    j = 1;
-    /* Go through the date format and interpret the matches in order of
-     * the sections in the date format. */
-    for (i = 0; date_format_user[format][i]; i++)
-    {
-        char segment_type = date_format_user[format][i];
-        /* Only do something if this is a meaningful character */
-        if (segment_type == 'y' || segment_type == 'm' || segment_type == 'd')
-        {
-            /* Copy the matching substring into date_segment so that we can
-             * convert it into an integer. */
-            mem_length = pmatch[j].rm_eo - pmatch[j].rm_so;
-            memcpy (date_segment, date_str + pmatch[j].rm_so, mem_length);
-            date_segment[mem_length] = '\0';
-
-            /* Set the appropriate member of retvalue. Save the original
-             * values so that we can check if they change when we use gnc_mktime
-             * below. */
-            switch (segment_type)
-            {
-            case 'y':
-                year = atoi (date_segment);
-
-                /* Handle two-digit years. */
-                if (year < 100)
-                {
-                    /* We allow two-digit years in the range 1969 - 2068. */
-                    if (year < 69)
-                        year += 2000;
-                    else
-                        year += 1900;
-                }
-                break;
-
-            case 'm':
-                month =atoi (date_segment);
-                break;
-
-            case 'd':
-                day = atoi (date_segment);
-                break;
-            }
-            j++;
-        }
-    }
-    ts = gnc_dmy2timespec_neutral(day, month, year);
-    return ts.tv_sec;
-}
-
-/** Parses a string into a date, given a format. The format cannot
- * include the year. This function should only be called by
- * parse_date.
- * @param date_str The string containing a date being parsed
- * @param format An index specifying a format in date_format_user
- * @return The parsed value of date_str on success or -1 on failure
- */
-static time64 parse_date_without_year (const char* date_str, int format)
-{
-    Timespec ts;
-    int i, j, mem_length, year = -1, month = -1, day = -1;
-
-    /* Buffer for containing individual parts (e.g. year, month, day) of a date */
-    gchar* date_segment;
-
-    /* The compiled regular expression */
-    regex_t preg = {0};
-
-    /* An array containing indices specifying the matched substrings in date_str */
-    regmatch_t pmatch[3] = { {0}, {0}, {0} };
-
-    /* The regular expression for parsing dates */
-    const char* regex = "^ *([0-9]+) *[-/.'] *([0-9]+).*$";
-
-    /* We get our matches using the regular expression. */
-    regcomp (&preg, regex, REG_EXTENDED);
-    regexec (&preg, date_str, 3, pmatch, 0);
-    regfree (&preg);
-    /* Set day, month, and year to today. We'll replace day & month with the
-     * values from the string.
-     */
-    gnc_timespec2dmy(timespec_now(), &day, &month, &year);
-    /* If there wasn't a match, there was an error. */
-    if (pmatch[0].rm_eo == 0)
-        return -1;
-
-    /* j traverses pmatch (index 0 contains the entire string, so we
-     * start at index 1 for the first meaningful match). */
-    j = 1;
-    /* Go through the date format and interpret the matches in order of
-     * the sections in the date format. */
-    for (i = 0; date_format_user[format][i]; i++)
-    {
-        char segment_type = date_format_user[format][i];
-        /* Only do something if this is a meaningful character */
-        if (segment_type == 'm' || segment_type == 'd')
-        {
-            /* Copy the matching substring into date_segment so that we can
-             * convert it into an integer. */
-            mem_length = pmatch[j].rm_eo - pmatch[j].rm_so;
-            date_segment = g_new (gchar, mem_length);
-            memcpy(date_segment, date_str + pmatch[j].rm_so, mem_length);
-            date_segment[mem_length] = '\0';
-
-            /* Set the appropriate member of retvalue. Save the original
-             * values so that we can check if they change when we use gnc_mktime
-             * below. */
-            switch (segment_type)
-            {
-            case 'm':
-                month = atoi (date_segment);
-                break;
-
-            case 'd':
-                day = atoi (date_segment);
-                break;
-            }
-            g_free (date_segment);
-            j++;
-        }
-    }
-    if (month > 12 || day > 31)
-        return -1;
-    ts = gnc_dmy2timespec_neutral(day, month, year);
-    return ts.tv_sec;
-}
-
-/** Parses a string into a date, given a format. This function
- * requires only knowing the order in which the year, month and day
- * appear. For example, 01-02-2003 will be parsed the same way as
- * 01/02/2003.
- * @param date_str The string containing a date being parsed
- * @param format An index specifying a format in date_format_user
- * @return The parsed value of date_str on success or -1 on failure
- */
-time64 parse_date (const char* date_str, int format)
-{
-    if (strchr (date_format_user[format], 'y'))
-        return parse_date_with_year (date_str, format);
-    else
-        return parse_date_without_year (date_str, format);
-}
-
-/** Constructor for GncCsvParseData.
- * @return Pointer to a new GncCSvParseData
- */
-GncCsvParseData* gnc_csv_new_parse_data (void)
-{
-    GncCsvParseData* parse_data = g_new(GncCsvParseData, 1);
-    parse_data->encoding = "UTF-8";
-    /* All of the data pointers are initially NULL. This is so that, if
-     * gnc_csv_parse_data_free is called before all of the data is
-     * initialized, only the data that needs to be freed is freed. */
-    parse_data->raw_mapping = NULL;
-    parse_data->raw_str.begin = parse_data->raw_str.end
-                                = parse_data->file_str.begin = parse_data->file_str.end = NULL;
-    parse_data->orig_lines = NULL;
-    parse_data->orig_row_lengths = NULL;
-    parse_data->column_types = NULL;
-    parse_data->error_lines = parse_data->transactions = NULL;
-    parse_data->options = default_parse_options();
-    parse_data->date_format = -1;
-    parse_data->currency_format = 0;
-    parse_data->chunk = g_string_chunk_new(100 * 1024);
-    parse_data->start_row = 0;
-    parse_data->end_row = 1000;
-    parse_data->skip_rows = FALSE;
-    return parse_data;
-}
-
-/** Destructor for GncCsvParseData.
- * @param parse_data Parse data whose memory will be freed
- */
-void gnc_csv_parse_data_free (GncCsvParseData* parse_data)
-{
-    /* All non-NULL pointers have been initialized and must be freed. */
-
-    if (parse_data->raw_mapping != NULL)
-    {
-        g_mapped_file_unref (parse_data->raw_mapping);
-    }
-
-    if (parse_data->file_str.begin != NULL)
-        g_free (parse_data->file_str.begin);
-
-    if (parse_data->orig_lines != NULL)
-        stf_parse_general_free (parse_data->orig_lines);
-
-    if (parse_data->orig_row_lengths != NULL)
-        g_array_free (parse_data->orig_row_lengths, FALSE);
-
-    if (parse_data->options != NULL)
-        stf_parse_options_free (parse_data->options);
-
-    if (parse_data->column_types != NULL)
-        g_array_free (parse_data->column_types, TRUE);
-
-    if (parse_data->error_lines != NULL)
-        g_list_free (parse_data->error_lines);
-
-    if (parse_data->transactions != NULL)
-    {
-        GList* transactions = parse_data->transactions;
-        /* We have to free the GncCsvTransLine's that are at each node in
-         * the list before freeing the entire list. */
-        do
-        {
-            g_free (transactions->data);
-            transactions = g_list_next (transactions);
-        }
-        while (transactions != NULL);
-        g_list_free (parse_data->transactions);
-    }
-
-    g_string_chunk_free (parse_data->chunk);
-    g_free (parse_data);
-}
-
-/** Converts raw file data using a new encoding. This function must be
- * called after gnc_csv_load_file only if gnc_csv_load_file guessed
- * the wrong encoding.
- * @param parse_data Data that is being parsed
- * @param encoding Encoding that data should be translated using
- * @param error Will point to an error on failure
- * @return 0 on success, 1 on failure
- */
-int gnc_csv_convert_encoding (GncCsvParseData* parse_data, const char* encoding,
-                             GError** error)
-{
-    gsize bytes_read, bytes_written;
-
-    /* If parse_data->file_str has already been initialized it must be
-     * freed first. (This should always be the case, since
-     * gnc_csv_load_file should always be called before this
-     * function.) */
-    if (parse_data->file_str.begin != NULL)
-        g_free(parse_data->file_str.begin);
-
-    /* Do the actual translation to UTF-8. */
-    parse_data->file_str.begin = g_convert (parse_data->raw_str.begin,
-                                           parse_data->raw_str.end - parse_data->raw_str.begin,
-                                           "UTF-8", encoding, &bytes_read, &bytes_written,
-                                           error);
-    /* Handle errors that occur. */
-    if (parse_data->file_str.begin == NULL)
-        return 1;
-
-    /* On success, save the ending pointer of the translated data and
-     * the encoding type and return 0. */
-    parse_data->file_str.end = parse_data->file_str.begin + bytes_written;
-    parse_data->encoding = (gchar*)encoding;
-    return 0;
-}
-
-/** Loads a file into a GncCsvParseData. This is the first function
- * that must be called after creating a new GncCsvParseData. If this
- * fails because the file couldn't be opened, no more functions can be
- * called on the parse data until this succeeds (or until it fails
- * because of an encoding guess error). If it fails because the
- * encoding could not be guessed, gnc_csv_convert_encoding must be
- * called until it succeeds.
- * @param parse_data Data that is being parsed
- * @param filename Name of the file that should be opened
- * @param error Will contain an error if there is a failure
- * @return 0 on success, 1 on failure
- */
-int gnc_csv_load_file (GncCsvParseData* parse_data, const char* filename,
-                      GError** error)
-{
-    const char* guess_enc = NULL;
-
-    /* Get the raw data first and handle an error if one occurs. */
-    parse_data->raw_mapping = g_mapped_file_new (filename, FALSE, NULL);
-    if (parse_data->raw_mapping == NULL)
-    {
-        /* TODO Handle file opening errors more specifically,
-         * e.g. inexistent file versus no read permission. */
-        parse_data->raw_str.begin = NULL;
-        g_set_error (error, GNC_CSV_IMP_ERROR, GNC_CSV_IMP_ERROR_OPEN, "%s", _("File opening failed."));
-        return 1;
-    }
-
-    /* Copy the mapping's contents into parse-data->raw_str. */
-    parse_data->raw_str.begin = g_mapped_file_get_contents (parse_data->raw_mapping);
-    parse_data->raw_str.end = parse_data->raw_str.begin + g_mapped_file_get_length (parse_data->raw_mapping);
-
-    /* Make a guess at the encoding of the data. */
-    if (g_mapped_file_get_length (parse_data->raw_mapping) != 0)
-        guess_enc = go_guess_encoding ((const char*)(parse_data->raw_str.begin),
-                                      (size_t)(parse_data->raw_str.end - parse_data->raw_str.begin),
-                                      "UTF-8", NULL);
-    if (guess_enc == NULL)
-    {
-        g_set_error (error, GNC_CSV_IMP_ERROR, GNC_CSV_IMP_ERROR_ENCODING, "%s", _("Unknown encoding."));
-        return 1;
-    }
-    /* Convert using the guessed encoding into parse_data->file_str and
-     * handle any errors that occur. */
-    gnc_csv_convert_encoding (parse_data, guess_enc, error);
-    if (parse_data->file_str.begin == NULL)
-    {
-        g_set_error (error, GNC_CSV_IMP_ERROR, GNC_CSV_IMP_ERROR_ENCODING, "%s", _("Unknown encoding."));
-        return 1;
-    }
-    else
-        return 0;
-}
-
-/** Parses a file into cells. This requires having an encoding that
- * works (see gnc_csv_convert_encoding). parse_data->options should be
- * set according to how the user wants before calling this
- * function. (Note: this function must be called with guessColTypes as
- * TRUE before it is ever called with it as FALSE.) (Note: if
- * guessColTypes is TRUE, all the column types will be GNC_CSV_NONE
- * right now.)
- * @param parse_data Data that is being parsed
- * @param guessColTypes TRUE to guess what the types of columns are based on the cell contents
- * @param error Will contain an error if there is a failure
- * @return 0 on success, 1 on failure
- */
-int gnc_csv_parse (GncCsvParseData* parse_data, gboolean guessColTypes, GError** error)
-{
-    /* max_cols is the number of columns in the row with the most columns. */
-    int i, max_cols = 0;
-
-    if (parse_data->orig_lines != NULL)
-    {
-        stf_parse_general_free (parse_data->orig_lines);
-    }
-
-    /* If everything is fine ... */
-    if (parse_data->file_str.begin != NULL)
-    {
-        /* Do the actual parsing. */
-        parse_data->orig_lines = stf_parse_general (parse_data->options, parse_data->chunk,
-                                 parse_data->file_str.begin,
-                                 parse_data->file_str.end);
-    }
-    /* If we couldn't get the encoding right, we just want an empty array. */
-    else
-    {
-        parse_data->orig_lines = g_ptr_array_new();
-    }
-
-    /* Record the original row lengths of parse_data->orig_lines. */
-    if (parse_data->orig_row_lengths != NULL)
-        g_array_free (parse_data->orig_row_lengths, FALSE);
-
-    parse_data->orig_row_lengths =
-        g_array_sized_new (FALSE, FALSE, sizeof(int), parse_data->orig_lines->len);
-
-    g_array_set_size (parse_data->orig_row_lengths, parse_data->orig_lines->len);
-    parse_data->orig_max_row = 0;
-    for (i = 0; i < parse_data->orig_lines->len; i++)
-    {
-        int length = ((GPtrArray*)parse_data->orig_lines->pdata[i])->len;
-        parse_data->orig_row_lengths->data[i] = length;
-        if (length > parse_data->orig_max_row)
-            parse_data->orig_max_row = length;
-    }
-
-    /* If it failed, generate an error. */
-    if (parse_data->orig_lines == NULL)
-    {
-        g_set_error (error, GNC_CSV_IMP_ERROR, GNC_CSV_IMP_ERROR_PARSE, "Parsing failed.");
-        return 1;
-    }
-
-    /* Now that we have data, let's set max_cols. */
-    for (i = 0; i < parse_data->orig_lines->len; i++)
-    {
-        if (max_cols < ((GPtrArray*)(parse_data->orig_lines->pdata[i]))->len)
-            max_cols = ((GPtrArray*)(parse_data->orig_lines->pdata[i]))->len;
-    }
-
-    if (guessColTypes)
-    {
-        /* Free parse_data->column_types if it's already been created. */
-        if (parse_data->column_types != NULL)
-            g_array_free (parse_data->column_types, TRUE);
-
-        /* Create parse_data->column_types and fill it with guesses based
-         * on the contents of each column. */
-        parse_data->column_types = g_array_sized_new (FALSE, FALSE, sizeof(int),
-                                   max_cols);
-        g_array_set_size (parse_data->column_types, max_cols);
-        /* TODO Make it actually guess. */
-        for (i = 0; i < parse_data->column_types->len; i++)
-        {
-            parse_data->column_types->data[i] = GNC_CSV_NONE;
-        }
-    }
-    else
-    {
-        /* If we don't need to guess column types, we will simply set any
-         * new columns that are created that didn't exist before to "None"
-         * since we don't want gibberish to appear. Note:
-         * parse_data->column_types should have already been
-         * initialized, so we don't check for it being NULL. */
-        int i = parse_data->column_types->len;
-        g_array_set_size (parse_data->column_types, max_cols);
-        for (; i < parse_data->column_types->len; i++)
-        {
-            parse_data->column_types->data[i] = GNC_CSV_NONE;
-        }
-    }
-    return 0;
-}
-
-/** A struct containing TransProperties that all describe a single transaction. */
-typedef struct
-{
-    int date_format; /**< The format for parsing dates */
-    int currency_format; /**< The format for currency */
-    Account* account; /**< The account the transaction belongs to */
-    GList* properties; /**< List of TransProperties */
-} TransPropertyList;
-
-/** A struct encapsulating a property of a transaction. */
-typedef struct
-{
-    int type;                /**< A value from the GncCsvColumnType enum except
-                               * GNC_CSV_NONE and GNC_CSV_NUM_COL_TYPES */
-    void* value;             /**< Pointer to the data that will be used to configure a transaction */
-    TransPropertyList* list; /**< The list the property belongs to */
-} TransProperty;
-
-/** Constructor for TransProperty.
- * @param type The type of the new property (see TransProperty.type for possible values)
- */
-static TransProperty* trans_property_new (int type, TransPropertyList* list)
-{
-    TransProperty* prop = g_new (TransProperty, 1);
-    prop->type = type;
-    prop->list = list;
-    prop->value = NULL;
-    return prop;
-}
-
-/** Destructor for TransProperty.
- * @param prop The property to be freed
- */
-static void trans_property_free (TransProperty* prop)
-{
-    switch (prop->type)
-    {
-        /* The types for "Date" and "Balance" (time64 and gnc_numeric,
-         * respectively) are typically not pointed to, we have to free
-         * them, unlike types like char* ("Description"). */
-    case GNC_CSV_DATE:
-    case GNC_CSV_BALANCE:
-    case GNC_CSV_DEPOSIT:
-    case GNC_CSV_WITHDRAWAL:
-        if (prop->value != NULL)
-            g_free(prop->value);
-        break;
-    }
-    g_free (prop);
-}
-
-/** Sets the value of the property by parsing str. Note: this should
- * only be called once on an instance of TransProperty, as calling it
- * more than once can cause memory leaks.
- * @param prop The property being set
- * @param str The string to be parsed
- * @return TRUE on success, FALSE on failure
- */
-static gboolean trans_property_set (TransProperty* prop, char* str)
-{
-    char *endptr, *possible_currency_symbol, *str_dupe;
-    gnc_numeric val;
-    int reti;
-    regex_t regex;
-    switch (prop->type)
-    {
-    case GNC_CSV_DATE:
-        prop->value = g_new(time64, 1);
-        *((time64*)(prop->value)) = parse_date(str, prop->list->date_format);
-        return *((time64*)(prop->value)) != -1;
-
-    case GNC_CSV_DESCRIPTION:
-    case GNC_CSV_NOTES:
-    case GNC_CSV_MEMO:
-    case GNC_CSV_OMEMO:
-    case GNC_CSV_NUM:
-        prop->value = g_strdup (str);
-        return TRUE;
-
-    case GNC_CSV_OACCOUNT:
-        prop->value = gnc_csv_account_map_search (str);
-        return TRUE;
-
-    case GNC_CSV_BALANCE:
-    case GNC_CSV_DEPOSIT:
-    case GNC_CSV_WITHDRAWAL:
-        str_dupe = g_strdup (str); /* First, we make a copy so we can't mess up real data. */
-        /* If a cell is empty or just spaces make its value = "0" */
-        reti = regcomp(&regex, "[0-9]", 0);
-        reti = regexec(&regex, str_dupe, 0, NULL, 0);
-        if (reti == REG_NOMATCH)
-        {
-            g_free (str_dupe);
-            str_dupe = g_strdup ("0");
-        }
-        /* Go through str_dupe looking for currency symbols. */
-        for (possible_currency_symbol = str_dupe; *possible_currency_symbol;
-                possible_currency_symbol = g_utf8_next_char (possible_currency_symbol))
-        {
-            if (g_unichar_type (g_utf8_get_char (possible_currency_symbol)) == G_UNICODE_CURRENCY_SYMBOL)
-            {
-                /* If we find a currency symbol, save the position just ahead
-                 * of the currency symbol (next_symbol), and find the null
-                 * terminator of the string (last_symbol). */
-                char *next_symbol = g_utf8_next_char (possible_currency_symbol), *last_symbol = next_symbol;
-                while (*last_symbol)
-                    last_symbol = g_utf8_next_char (last_symbol);
-
-                /* Move all of the string (including the null byte, which is
-                 * why we have +1 in the size parameter) following the
-                 * currency symbol back one character, thereby overwriting the
-                 * currency symbol. */
-                memmove (possible_currency_symbol, next_symbol, last_symbol - next_symbol + 1);
-                break;
-            }
-        }
-
-        /* Currency format */
-        switch (prop->list->currency_format)
-        {
-        case 0:
-            /* Currency locale */
-            if (!(xaccParseAmount (str_dupe, TRUE, &val, &endptr)))
-            {
-                g_free (str_dupe);
-                return FALSE;
-            }
-            break;
-        case 1:
-            /* Currency decimal period */
-            if (!(xaccParseAmountExtended (str_dupe, TRUE, '-', '.', ',', "\003\003", "$+", &val, &endptr)))
-            {
-                g_free (str_dupe);
-                return FALSE;
-            }
-            break;
-        case 2:
-            /* Currency decimal comma */
-            if (!(xaccParseAmountExtended (str_dupe, TRUE, '-', ',', '.', "\003\003", "$+", &val, &endptr)))
-            {
-                g_free (str_dupe);
-                return FALSE;
-            }
-            break;
-        }
-
-        prop->value = g_new (gnc_numeric, 1);
-        *((gnc_numeric*)(prop->value)) = val;
-        g_free (str_dupe);
-        return TRUE;
-
-    }
-    return FALSE; /* We should never actually get here. */
-}
-
-/** Constructor for TransPropertyList.
- * @param account The account with which transactions should be built
- * @param date_format An index from date_format_user for how date properties should be parsed
- * @return A pointer to a new TransPropertyList
- */
-static TransPropertyList* trans_property_list_new (Account* account, int date_format, int currency_format)
-{
-    TransPropertyList* list = g_new (TransPropertyList, 1);
-    list->account = account;
-    list->date_format = date_format;
-    list->currency_format = currency_format;
-    list->properties = NULL;
-    return list;
-}
-
-/** Destructor for TransPropertyList.
- * @param list The list to be freed
- */
-static void trans_property_list_free (TransPropertyList* list)
-{
-    /* Free all of the properties in this list before freeing the list itself. */
-    GList* properties_begin = list->properties;
-    while (list->properties != NULL)
-    {
-        trans_property_free ((TransProperty*)(list->properties->data));
-        list->properties = g_list_next (list->properties);
-    }
-    g_list_free (properties_begin);
-    g_free (list);
-}
-
-/** Adds a property to the list it's linked with.
- * (The TransPropertyList is not passed as a parameter because the property is
- * associated with a list when it's constructed.)
- * @param property The property to be added to its list
- */
-static void trans_property_list_add (TransProperty* property)
-{
-    property->list->properties = g_list_append (property->list->properties, property);
-}
-
-/** Adds a split to a transaction.
- * @param trans The transaction to add a split to
- * @param account The account used for the split
- * @param book The book where the split should be stored
- * @param amount The amount of the split
- */
-static void trans_add_split (Transaction* trans, Account* account, QofBook* book,
-                            gnc_numeric amount, const char *num, const char *memo)
-{
-    Split* split = xaccMallocSplit (book);
-    xaccSplitSetAccount (split, account);
-    xaccSplitSetParent (split, trans);
-    xaccSplitSetAmount (split, amount);
-    xaccSplitSetValue (split, amount);
-    xaccSplitSetMemo (split, memo);
-    /* set tran-num and/or split-action per book option */
-    gnc_set_num_action (trans, split, num, NULL);
-}
-
-/** Adds a other split to a transaction.
- * @param trans The transaction to add a split to
- * @param account The account used for the other split
- * @param book The book where the split should be stored
- * @param amount The amount of the split
- */
-static void trans_add_osplit (Transaction* trans, Account* account, QofBook* book,
-                            gnc_numeric amount, const char *num, const char *memo)
-{
-    Split *osplit = xaccMallocSplit (book);
-    xaccSplitSetAccount (osplit, account);
-    xaccSplitSetParent (osplit, trans);
-    xaccSplitSetAmount (osplit, amount);
-    xaccSplitSetValue (osplit, gnc_numeric_neg (amount));
-    xaccSplitSetMemo (osplit, memo);
-}
-
-/** Tests a TransPropertyList for having enough essential properties.
- * Essential properties are "Date" and one of the following: "Balance", "Deposit", or
- * "Withdrawal".
- * @param list The list we are checking
- * @param error Contains an error message on failure
- * @return TRUE if there are enough essentials; FALSE otherwise
- */
-static gboolean trans_property_list_verify_essentials (TransPropertyList* list, gchar** error)
-{
-    int i;
-    /* possible_errors lists the ways in which a list can fail this test. */
-    enum PossibleErrorTypes {NO_DATE, NO_AMOUNT, NUM_OF_POSSIBLE_ERRORS};
-    gchar* possible_errors[NUM_OF_POSSIBLE_ERRORS] =
-    {
-        N_("No date column."),
-        N_("No balance, deposit, or withdrawal column.")
-    };
-    int possible_error_lengths[NUM_OF_POSSIBLE_ERRORS] = {0};
-    GList *properties_begin = list->properties, *errors_list = NULL;
-
-    /* Go through each of the properties and erase possible errors. */
-    while (list->properties)
-    {
-        switch (((TransProperty*)(list->properties->data))->type)
-        {
-        case GNC_CSV_DATE:
-            possible_errors[NO_DATE] = NULL;
-            break;
-
-        case GNC_CSV_BALANCE:
-        case GNC_CSV_DEPOSIT:
-        case GNC_CSV_WITHDRAWAL:
-            possible_errors[NO_AMOUNT] = NULL;
-            break;
-        }
-        list->properties = g_list_next (list->properties);
-    }
-    list->properties = properties_begin;
-
-    /* Accumulate a list of the actual errors. */
-    for (i = 0; i < NUM_OF_POSSIBLE_ERRORS; i++)
-    {
-        if (possible_errors[i] != NULL)
-        {
-            errors_list = g_list_append (errors_list, GINT_TO_POINTER(i));
-            /* Since we added an error, we want to also store its length for
-             * when we construct the full error string. */
-            possible_error_lengths[i] = strlen (_(possible_errors[i]));
-        }
-    }
-
-    /* If there are no errors, we can quit now. */
-    if (errors_list == NULL)
-        return TRUE;
-    else
-    {
-        /* full_error_size is the full length of the error message. */
-        int full_error_size = 0, string_length = 0;
-        GList* errors_list_begin = errors_list;
-        gchar *error_message, *error_message_begin;
-
-        /* Find the value for full_error_size. */
-        while (errors_list)
-        {
-            /* We add an extra 1 to account for spaces in between messages. */
-            full_error_size += possible_error_lengths[GPOINTER_TO_INT(errors_list->data)] + 1;
-            errors_list = g_list_next (errors_list);
-        }
-        errors_list = errors_list_begin;
-
-        /* Append the error messages one after another. */
-        error_message = error_message_begin = g_new (gchar, full_error_size);
-        while (errors_list)
-        {
-            i = GPOINTER_TO_INT(errors_list->data);
-            string_length = possible_error_lengths[i];
-
-            /* Copy the error message and put a space after it. */
-            strncpy(error_message, _(possible_errors[i]), string_length);
-            error_message += string_length;
-            *error_message = ' ';
-            error_message++;
-
-            errors_list = g_list_next (errors_list);
-        }
-        *error_message = '\0'; /* Replace the last space with the null byte. */
-        g_list_free (errors_list_begin);
-
-        *error = error_message_begin;
-        return FALSE;
-    }
-}
-
-/** Create a Transaction from a TransPropertyList.
- * @param list The list of properties
- * @param error Contains an error on failure
- * @return On success, a GncCsvTransLine; on failure, the trans pointer is NULL
- */
-static GncCsvTransLine* trans_property_list_to_trans (TransPropertyList* list, gchar** error)
-{
-    GncCsvTransLine* trans_line = g_new (GncCsvTransLine, 1);
-    GList* properties_begin = list->properties;
-    QofBook* book = gnc_account_get_book (list->account);
-    gnc_commodity* currency = xaccAccountGetCommodity (list->account);
-    gnc_numeric amount = double_to_gnc_numeric (0.0, xaccAccountGetCommoditySCU (list->account),
-                         GNC_HOW_RND_ROUND_HALF_UP);
-    gchar *num = NULL;
-    gchar *memo = NULL;
-    gchar *omemo = NULL;
-    Account *oaccount = NULL;
-
-    /* This flag is set to TRUE if we can use the "Deposit" or "Withdrawal" column. */
-    gboolean amount_set = FALSE;
-
-    /* The balance is 0 by default. */
-    trans_line->balance_set = FALSE;
-    trans_line->balance = amount;
-    trans_line->num = NULL;
-
-    /* We make the line_no -1 just to mark that it hasn't been set. We
-     * may get rid of line_no soon anyway, so it's not particularly
-     * important. */
-    trans_line->line_no = -1;
-
-    /* Make sure this is a transaction with all the columns we need. */
-    if (!trans_property_list_verify_essentials (list, error))
-    {
-        g_free(trans_line);
-        return NULL;
-    }
-
-    trans_line->trans = xaccMallocTransaction (book);
-    xaccTransBeginEdit (trans_line->trans);
-    xaccTransSetCurrency (trans_line->trans, currency);
-
-    /* Go through each of the properties and edit the transaction accordingly. */
-    list->properties = properties_begin;
-    while (list->properties != NULL)
-    {
-        TransProperty* prop = (TransProperty*)(list->properties->data);
-        switch (prop->type)
-        {
-        case GNC_CSV_DATE:
-            xaccTransSetDatePostedSecsNormalized (trans_line->trans, *((time64*)(prop->value)));
-            break;
-
-        case GNC_CSV_DESCRIPTION:
-            xaccTransSetDescription (trans_line->trans, (char*)(prop->value));
-            break;
-
-        case GNC_CSV_NOTES:
-            xaccTransSetNotes (trans_line->trans, (char*)(prop->value));
-            break;
-
-        case GNC_CSV_OACCOUNT:
-            oaccount = ((Account*)(prop->value));
-            break;
-
-        case GNC_CSV_MEMO:
-            memo = g_strdup ((char*)(prop->value));
-            break;
-
-        case GNC_CSV_OMEMO:
-            omemo = g_strdup ((char*)(prop->value));
-            break;
-
-        case GNC_CSV_NUM:
-            /* the 'num' is saved and passed to 'trans_add_split' below where
-             * 'gnc_set_num_action' is used to set tran-num and/or split-action
-             * per book option */
-            num = g_strdup ((char*)(prop->value));
-            /* the 'num' is also saved and used in 'gnc_csv_parse_to_trans' when
-             * it calls 'trans_add_split' after deleting the splits added below
-             * when a balance is used by the user */
-            trans_line->num = g_strdup ((char*)(prop->value));
-            break;
-
-        case GNC_CSV_DEPOSIT: /* Add deposits to the existing amount. */
-            if (prop->value != NULL)
-            {
-                amount = gnc_numeric_add (*((gnc_numeric*)(prop->value)),
-                                         amount,
-                                         xaccAccountGetCommoditySCU (list->account),
-                                         GNC_HOW_RND_ROUND_HALF_UP);
-                amount_set = TRUE;
-                /* We will use the "Deposit" and "Withdrawal" columns in preference to "Balance". */
-                trans_line->balance_set = FALSE;
-            }
-            break;
-
-        case GNC_CSV_WITHDRAWAL: /* Withdrawals are just negative deposits. */
-            if (prop->value != NULL)
-            {
-                amount = gnc_numeric_add (gnc_numeric_neg(*((gnc_numeric*)(prop->value))),
-                                         amount,
-                                         xaccAccountGetCommoditySCU (list->account),
-                                         GNC_HOW_RND_ROUND_HALF_UP);
-                amount_set = TRUE;
-                /* We will use the "Deposit" and "Withdrawal" columns in preference to "Balance". */
-                trans_line->balance_set = FALSE;
-            }
-            break;
-
-        case GNC_CSV_BALANCE: /* The balance gets stored in a separate field in trans_line. */
-            /* We will use the "Deposit" and "Withdrawal" columns in preference to "Balance". */
-            if (!amount_set && prop->value != NULL)
-            {
-                /* This gets put into the actual transaction at the end of gnc_csv_parse_to_trans. */
-                trans_line->balance = *((gnc_numeric*)(prop->value));
-                trans_line->balance_set = TRUE;
-            }
-            break;
-        }
-        list->properties = g_list_next (list->properties);
-    }
-
-    /* Add a split with the cumulative amount value. */
-    trans_add_split (trans_line->trans, list->account, book, amount, num, memo);
-
-    if (oaccount)
-        trans_add_osplit (trans_line->trans, oaccount, book, amount, num, omemo);
-
-    if (num)
-        g_free (num);
-    if (memo)
-        g_free (memo);
-    if (omemo)
-        g_free (omemo);
-
-    return trans_line;
-}
-
-/** Creates a list of transactions from parsed data. Transactions that
- * could be created from rows are placed in parse_data->transactions;
- * rows that fail are placed in parse_data->error_lines. (Note: there
- * is no way for this function to "fail," i.e. it only returns 0, so
- * it may be changed to a void function in the future.)
- * @param parse_data Data that is being parsed
- * @param account Account with which transactions are created
- * @param redo_errors TRUE to convert only error data, FALSE for all data
- * @return 0 on success, 1 on failure
- */
-int gnc_csv_parse_to_trans (GncCsvParseData* parse_data, Account* account,
-                           gboolean redo_errors)
-{
-    gboolean hasBalanceColumn;
-    int i, j, max_cols = 0;
-    GArray* column_types = parse_data->column_types;
-    GList *error_lines = NULL, *begin_error_lines = NULL;
-    Account *home_account = NULL;
-
-    /* last_transaction points to the last element in
-     * parse_data->transactions, or NULL if it's empty. */
-    GList* last_transaction = NULL;
-
-    /* Free parse_data->error_lines and parse_data->transactions if they
-     * already exist. */
-    if (redo_errors) /* If we're redoing errors, we save freeing until the end. */
-        begin_error_lines = error_lines = parse_data->error_lines;
-    else
-    {
-        if (parse_data->error_lines != NULL)
-            g_list_free(parse_data->error_lines);
-
-        if (parse_data->transactions != NULL)
-            g_list_free (parse_data->transactions);
-    }
-    parse_data->error_lines = NULL;
-
-    if (redo_errors) /* If we're looking only at error data ... */
-    {
-        if (parse_data->transactions == NULL)
-            last_transaction = NULL;
-        else
-        {
-            /* Move last_transaction to the end. */
-            last_transaction = parse_data->transactions;
-            while (g_list_next (last_transaction) != NULL)
-            {
-                last_transaction = g_list_next (last_transaction);
-            }
-        }
-        /* ... we use only the lines in error_lines. */
-        if (error_lines == NULL)
-            i = parse_data->orig_lines->len; /* Don't go into the for loop. */
-        else
-            i = GPOINTER_TO_INT(error_lines->data);
-    }
-    else /* Otherwise, we look at all the data. */
-    {
-        /* The following while-loop effectively behaves like the following for-loop:
-         * for(i = 0; i < parse_data->orig_lines->len; i++). */
-        i = parse_data->start_row;
-        last_transaction = NULL;
-    }
-
-    /* set parse_data->end_row to number of lines */
-    if (parse_data->end_row > parse_data->orig_lines->len)
-        parse_data->end_row = parse_data->orig_lines->len;
-
-    while (i < parse_data->end_row)
-    {
-        GPtrArray* line = parse_data->orig_lines->pdata[i];
-        /* This flag is TRUE if there are any errors in this row. */
-        gboolean errors = FALSE;
-        gchar* error_message = NULL;
-        TransPropertyList* list;
-        GncCsvTransLine* trans_line = NULL;
-
-        home_account = account;
-
-        // If account = NULL, we should have an Account column
-        if (home_account == NULL)
-        {
-            for (j = 0; j < line->len; j++)
-            {
-                /* Look for "Account" columns. */
-                if (column_types->data[j] == GNC_CSV_ACCOUNT)
-                {
-                    home_account = gnc_csv_account_map_search (line->pdata[j]);
-                }
-            }
-        }
-
-        if (home_account == NULL)
-        {
-            error_message = g_strdup_printf (_("Account column could not be understood."));
-            errors = TRUE;
-        }
-        else
-        {
-            list = trans_property_list_new (home_account, parse_data->date_format, parse_data->currency_format);
-
-            for (j = 0; j < line->len; j++)
-            {
-                /* We do nothing in "None" or "Account" columns. */
-                if ((column_types->data[j] != GNC_CSV_NONE) && (column_types->data[j] != GNC_CSV_ACCOUNT))
-                {
-                    /* Affect the transaction appropriately. */
-                    TransProperty* property = trans_property_new (column_types->data[j], list);
-                    gboolean succeeded = trans_property_set (property, line->pdata[j]);
-
-                    /* TODO Maybe move error handling to within TransPropertyList functions? */
-                    if (succeeded)
-                        trans_property_list_add (property);
-                    else
-                    {
-                        errors = TRUE;
-                        error_message = g_strdup_printf (_("%s column could not be understood."),
-                                                        _(gnc_csv_column_type_strs[property->type]));
-                        trans_property_free (property);
-                        break;
-                    }
-                }
-            }
-
-            /* If we had success, add the transaction to parse_data->transaction. */
-            if (!errors)
-            {
-                trans_line = trans_property_list_to_trans (list, &error_message);
-                errors = trans_line == NULL;
-            }
-            trans_property_list_free (list);
-        }
-
-        /* If there were errors, add this line to parse_data->error_lines. */
-        if (errors)
-        {
-            parse_data->error_lines = g_list_append (parse_data->error_lines,
-                                                    GINT_TO_POINTER(i));
-            /* If there's already an error message, we need to replace it. */
-            if (line->len > (int)(parse_data->orig_row_lengths->data[i]))
-            {
-                g_free(line->pdata[line->len - 1]);
-                line->pdata[line->len - 1] = error_message;
-            }
-            else
-            {
-                /* Put the error message at the end of the line. */
-                g_ptr_array_add (line, error_message);
-            }
-        }
-        else
-        {
-            /* If all went well, add this transaction to the list. */
-            trans_line->line_no = i;
-
-            /* We keep the transactions sorted by date. We start at the end
-             * of the list and go backward, simply because the file itself
-             * is probably also sorted by date (but we need to handle the
-             * exception anyway). */
-
-            /* If we can just put it at the end, do so and increment last_transaction. */
-            if (last_transaction == NULL ||
-                    xaccTransGetDate (((GncCsvTransLine*)(last_transaction->data))->trans) <= xaccTransGetDate (trans_line->trans))
-            {
-                parse_data->transactions = g_list_append (parse_data->transactions, trans_line);
-                /* If this is the first transaction, we need to get last_transaction on track. */
-                if (last_transaction == NULL)
-                    last_transaction = parse_data->transactions;
-                else /* Otherwise, we can just continue. */
-                    last_transaction = g_list_next (last_transaction);
-            }
-            /* Otherwise, search backward for the correct spot. */
-            else
-            {
-                GList* insertion_spot = last_transaction;
-                while (insertion_spot != NULL &&
-                        xaccTransGetDate (((GncCsvTransLine*)(insertion_spot->data))->trans) > xaccTransGetDate (trans_line->trans))
-                {
-                    insertion_spot = g_list_previous (insertion_spot);
-                }
-                /* Move insertion_spot one location forward since we have to
-                 * use the g_list_insert_before function. */
-                if (insertion_spot == NULL) /* We need to handle the case of inserting at the beginning of the list. */
-                    insertion_spot = parse_data->transactions;
-                else
-                    insertion_spot = g_list_next (insertion_spot);
-
-                parse_data->transactions = g_list_insert_before (parse_data->transactions, insertion_spot, trans_line);
-            }
-        }
-
-        /* Increment to the next row. */
-        if (redo_errors)
-        {
-            /* Move to the next error line in the list. */
-            error_lines = g_list_next (error_lines);
-            if (error_lines == NULL)
-                i = parse_data->orig_lines->len; /* Don't continue the for loop. */
-            else
-                i = GPOINTER_TO_INT(error_lines->data);
-        }
-        else
-        {
-            if (parse_data->skip_rows == FALSE)
-                i++;
-            else
-                i = i + 2;
-        }
-    }
-
-    /* If we have a balance column, set the appropriate amounts on the transactions. */
-    hasBalanceColumn = FALSE;
-    for (i = 0; i < parse_data->column_types->len; i++)
-    {
-        if (parse_data->column_types->data[i] == GNC_CSV_BALANCE)
-        {
-            hasBalanceColumn = TRUE;
-            break;
-        }
-    }
-
-    if (hasBalanceColumn) // This is only used if we have one home account
-    {
-        Split      *split, *osplit;
-        gnc_numeric balance_offset;
-        GList      *transactions = parse_data->transactions;
-
-        if (account != NULL)
-            home_account = account;
-
-        /* balance_offset is how much the balance currently in the account
-         * differs from what it will be after the transactions are
-         * imported. This will be sum of all the previous transactions for
-         * any given transaction. */
-        balance_offset = double_to_gnc_numeric (0.0, xaccAccountGetCommoditySCU (home_account),
-                                     GNC_HOW_RND_ROUND_HALF_UP);
-
-        while (transactions != NULL)
-        {
-            GncCsvTransLine* trans_line = (GncCsvTransLine*)transactions->data;
-            if (trans_line->balance_set)
-            {
-                time64 date = xaccTransGetDate (trans_line->trans);
-                /* Find what the balance should be by adding the offset to the actual balance. */
-                gnc_numeric existing_balance = gnc_numeric_add (balance_offset,
-                                               xaccAccountGetBalanceAsOfDate (home_account, date),
-                                               xaccAccountGetCommoditySCU (home_account),
-                                               GNC_HOW_RND_ROUND_HALF_UP);
-
-                /* The amount of the transaction is the difference between the new and existing balance. */
-                gnc_numeric amount = gnc_numeric_sub (trans_line->balance,
-                                                     existing_balance,
-                                                     xaccAccountGetCommoditySCU (home_account),
-                                                     GNC_HOW_RND_ROUND_HALF_UP);
-
-                // Find home account split
-                split  = xaccTransFindSplitByAccount (trans_line->trans, home_account);
-                xaccSplitSetAmount (split, amount);
-                xaccSplitSetValue (split, amount);
-
-                // If we have two splits, change other side
-                if (xaccTransCountSplits (trans_line->trans) == 2)
-                {
-                    osplit = xaccSplitGetOtherSplit (split);
-                    xaccSplitSetAmount (split, amount);
-                    xaccSplitSetValue (split, gnc_numeric_neg (amount));
-                }
-
-                if (trans_line->num)
-                    g_free (trans_line->num);
-
-                /* This new transaction needs to be added to the balance offset. */
-                balance_offset = gnc_numeric_add (balance_offset,
-                                                 amount,
-                                                 xaccAccountGetCommoditySCU (home_account),
-                                                 GNC_HOW_RND_ROUND_HALF_UP);
-            }
-            transactions = g_list_next (transactions);
-        }
-    }
-
-    if (redo_errors) /* Now that we're at the end, we do the freeing. */
-        g_list_free (begin_error_lines);
-
-    /* We need to resize parse_data->column_types since errors may have added columns. */
-    for (i = 0; i < parse_data->orig_lines->len; i++)
-    {
-        if (max_cols < ((GPtrArray*)(parse_data->orig_lines->pdata[i]))->len)
-            max_cols = ((GPtrArray*)(parse_data->orig_lines->pdata[i]))->len;
-    }
-    i = parse_data->column_types->len;
-    parse_data->column_types = g_array_set_size (parse_data->column_types, max_cols);
-    for (; i < max_cols; i++)
-    {
-        parse_data->column_types->data[i] = GNC_CSV_NONE;
-    }
-    return 0;
-}
-
-
-gboolean
-gnc_csv_parse_check_for_column_type (GncCsvParseData* parse_data, gint type)
-{
-    GArray* column_types = parse_data->column_types;
-    gboolean ret = FALSE;
-    int j, ncols = column_types->len; /* ncols is the number of columns in the data. */
-
-    for (j = 0; j < ncols; j++)
-    {
-        if (column_types->data[j] == type)
-            ret = TRUE;
-    }
-    return ret;
-}
diff --git a/src/import-export/csv-imp/gnc-csv-model.h b/src/import-export/csv-imp/gnc-csv-model.h
deleted file mode 100644
index 4c78176..0000000
--- a/src/import-export/csv-imp/gnc-csv-model.h
+++ /dev/null
@@ -1,149 +0,0 @@
-/********************************************************************\
- * 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 2 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, contact:                        *
- *                                                                  *
- * Free Software Foundation           Voice:  +1-617-542-5942       *
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
- * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
-\********************************************************************/
-
-/** @file
-     @brief CSV import GUI
-     *
-     gnc-csv-model.h
-     @author Copyright (c) 2007 Benny Sperisen <lasindi at gmail.com>
- */
-
-#ifndef GNC_CSV_MODEL_H
-#define GNC_CSV_MODEL_H
-
-#include "config.h"
-
-#include "Account.h"
-#include "Transaction.h"
-
-#include "stf/stf-parse.h"
-
-/** Enumeration for column types. These are the different types of
- * columns that can exist in a CSV/Fixed-Width file. There should be
- * no two columns with the same type except for the GNC_CSV_NONE
- * type. */
-enum GncCsvColumnType {
-    GNC_CSV_NONE,
-    GNC_CSV_DATE,
-    GNC_CSV_NUM,
-    GNC_CSV_DESCRIPTION,
-    GNC_CSV_NOTES,
-    GNC_CSV_ACCOUNT,
-    GNC_CSV_DEPOSIT,
-    GNC_CSV_WITHDRAWAL,
-    GNC_CSV_BALANCE,
-    GNC_CSV_MEMO,
-    GNC_CSV_OACCOUNT,
-    GNC_CSV_OMEMO,
-    GNC_CSV_NUM_COL_TYPES
-};
-
-/** Error domain for the csv importer. */
-#define GNC_CSV_IMP_ERROR gnc_csv_imp_error_quark ()
-GQuark gnc_csv_imp_error_quark (void);
-
-/** Enumeration for error types. These are the different types of
- * errors that various functions used for the CSV/Fixed-Width importer
- * can have. */
-enum GncCsvErrorType {
-    GNC_CSV_IMP_ERROR_OPEN,
-    GNC_CSV_IMP_ERROR_ENCODING,
-    GNC_CSV_IMP_ERROR_PARSE
-};
-
-/** Struct for containing a string. This struct simply contains
- * pointers to the beginning and end of a string. We need this because
- * the STF code that gnc_csv_parse calls requires these pointers. */
-typedef struct
-{
-    char* begin;
-    char* end;
-} GncCsvStr;
-
-/* TODO We now sort transactions by date, not line number, so we
- * should probably get rid of this struct and uses of it. */
-
-/** Struct pairing a transaction with a line number. This struct is
- * used to keep the transactions in order. When rows are separated
- * into "valid" and "error" lists (in case some of the rows have cells
- * that are unparseable), we want the user to still be able to
- * "correct" the error list. If we keep the line numbers of valid
- * transactions, we can then put transactions created from the newly
- * corrected rows into the right places. */
-typedef struct
-{
-    int line_no;
-    Transaction* trans;
-    gnc_numeric balance;  /**< The (supposed) balance after this transaction takes place */
-    gboolean balance_set; /**< TRUE if balance has been set from user data, FALSE otherwise */
-    gchar *num;           /**< Saves the 'num'for use if balance has been set from user data */
-} GncCsvTransLine;
-
-/* A set of currency formats that the user sees. */
-extern const int num_currency_formats;
-extern const gchar* currency_format_user[];
-
-/* A set of date formats that the user sees. */
-extern const int num_date_formats;
-extern const gchar* date_format_user[];
-
-/* This array contains all of the different strings for different column types. */
-extern gchar* gnc_csv_column_type_strs[];
-
-/** Struct containing data for parsing a CSV/Fixed-Width file. */
-typedef struct
-{
-    gchar* encoding;
-    GMappedFile* raw_mapping;   /**< The mapping containing raw_str */
-    GncCsvStr raw_str;          /**< Untouched data from the file as a string */
-    GncCsvStr file_str;         /**< raw_str translated into UTF-8 */
-    GPtrArray* orig_lines;      /**< file_str parsed into a two-dimensional array of strings */
-    GArray* orig_row_lengths;   /**< The lengths of rows in orig_lines
-                                      before error messages are appended */
-    int orig_max_row;           /**< Holds the maximum value in orig_row_lengths */
-    GStringChunk* chunk;        /**< A chunk of memory in which the contents of orig_lines is stored */
-    StfParseOptions_t* options; /**< Options controlling how file_str should be parsed */
-    GArray* column_types;       /**< Array of values from the GncCsvColumnType enumeration */
-    GList* error_lines;         /**< List of row numbers in orig_lines that have errors */
-    GList* transactions;        /**< List of GncCsvTransLine*s created using orig_lines and column_types */
-    int date_format;            /**< The format of the text in the date columns from date_format_internal. */
-    int start_row;              /**< The start row to generate transactions from. */
-    int end_row;                /**< The end row to generate transactions from. */
-    gboolean skip_rows;         /**< Skip Alternate Rows from start row. */
-    int currency_format;        /**< The currency format, 0 for locale, 1 for comma dec and 2 for period */
-} GncCsvParseData;
-
-GncCsvParseData* gnc_csv_new_parse_data (void);
-
-void gnc_csv_parse_data_free (GncCsvParseData* parse_data);
-
-int gnc_csv_load_file (GncCsvParseData* parse_data, const char* filename,
-                      GError** error);
-
-int gnc_csv_convert_encoding (GncCsvParseData* parse_data, const char* encoding, GError** error);
-
-int gnc_csv_parse (GncCsvParseData* parse_data, gboolean guessColTypes, GError** error);
-
-int gnc_csv_parse_to_trans (GncCsvParseData* parse_data, Account* account, gboolean redo_errors);
-
-time64 parse_date (const char* date_str, int format);
-
-gboolean gnc_csv_parse_check_for_column_type (GncCsvParseData* parse_data, gint type);
-
-#endif
diff --git a/src/import-export/csv-imp/gnc-trans-props.hpp b/src/import-export/csv-imp/gnc-trans-props.hpp
index 0cf99a6..864cbf5 100644
--- a/src/import-export/csv-imp/gnc-trans-props.hpp
+++ b/src/import-export/csv-imp/gnc-trans-props.hpp
@@ -34,6 +34,8 @@ extern "C" {
 }
 
 #include <string>
+#include <map>
+#include <memory>
 #include <boost/optional.hpp>
 
 /** Enumeration for column types. These are the different types of
diff --git a/src/import-export/csv-imp/gnc-tx-import.cpp b/src/import-export/csv-imp/gnc-tx-import.cpp
index 94e40e9..e35172a 100644
--- a/src/import-export/csv-imp/gnc-tx-import.cpp
+++ b/src/import-export/csv-imp/gnc-tx-import.cpp
@@ -42,20 +42,20 @@ extern "C" {
 
 G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT;
 
-//const int num_date_formats = 5;
-//const gchar* date_format_user[] = {N_("y-m-d"),
-//                                   N_("d-m-y"),
-//                                   N_("m-d-y"),
-//                                   N_("d-m"),
-//                                   N_("m-d")
-//                                  };
-//
-//const int num_currency_formats = 3;
-//const gchar* currency_format_user[] = {N_("Locale"),
-//                                       N_("Period: 123,456.78"),
-//                                       N_("Comma: 123.456,78")
-//                                      };
-//
+const int num_date_formats = 5;
+const gchar* date_format_user[] = {N_("y-m-d"),
+                                   N_("d-m-y"),
+                                   N_("m-d-y"),
+                                   N_("d-m"),
+                                   N_("m-d")
+                                  };
+
+const int num_currency_formats = 3;
+const gchar* currency_format_user[] = {N_("Locale"),
+                                       N_("Period: 123,456.78"),
+                                       N_("Comma: 123.456,78")
+                                      };
+
 
 
 /** Constructor for GncTxImport.
diff --git a/src/import-export/csv-imp/test/CMakeLists.txt b/src/import-export/csv-imp/test/CMakeLists.txt
index 0074739..faccf98 100644
--- a/src/import-export/csv-imp/test/CMakeLists.txt
+++ b/src/import-export/csv-imp/test/CMakeLists.txt
@@ -10,18 +10,8 @@ SET(CSV_IMP_TEST_INCLUDE_DIRS
 )
 SET(CSV_IMP_TEST_LIBS gncmod-csv-import gncmod-engine gnc-qof test-core)
 
-SET(test_csv_imp_SOURCES
-  test-csv-imp.c
-  utest-gnc-csv-model.c
-)
-
 # This test does not run in Win32
 IF (NOT WIN32)
-  GNC_ADD_TEST(test-csv-imp "${test_csv_imp_SOURCES}"
-    CSV_IMP_TEST_INCLUDE_DIRS CSV_IMP_TEST_LIBS
-    SRCDIR=${CMAKE_CURRENT_SOURCE_DIR}
-  )
-
   SET(MODULEPATH ${CMAKE_SOURCE_DIR}/src/import-export/csv-imp)
   SET(gtest_csv_imp_LIBS gncmod-csv-import ${GLIB2_LDFLAGS} ${GTEST_LIB})
   SET(gtest_csv_imp_INCLUDES
diff --git a/src/import-export/csv-imp/test/Makefile.am b/src/import-export/csv-imp/test/Makefile.am
index 05738e3..f489e59 100644
--- a/src/import-export/csv-imp/test/Makefile.am
+++ b/src/import-export/csv-imp/test/Makefile.am
@@ -10,65 +10,28 @@ MODULEPATH = src/import-export/csv-imp
 
 #The test program. You'll need to add to this if you have more than one module above.
 
-check_PROGRAMS = test-csv-imp
+check_PROGRAMS = test-tokenizer \
+                 test-tx-import
 
 TESTS = ${check_PROGRAMS}
 
-test_csv_impdir = ${top_srcdir}/${MODULEPATH}/test
-
 #Program files for tests go here. It's probably best to have one for
 #each file in the parent directory. Include
 #test_foo_support.c if you have one and aren't building the
 #support library.
-test_csv_imp_SOURCES = \
-  test-csv-imp.c \
-  utest-gnc-csv-model.c
-
-test_csv_imp_HEADERS =
+test_tokenizer_SOURCES = \
+    test-tokenizer.cpp
 
-IMP_TEST_LDFLAGS = \
-  ${top_builddir}/${MODULEPATH}/libgncmod-csv-import.la \
-  ${top_builddir}/src/import-export/libgncmod-generic-import.la \
-  ${top_builddir}/src/gnome/libgnc-gnome.la \
-  ${top_builddir}/src/gnome-utils/libgncmod-gnome-utils.la \
-  ${top_builddir}/src/register/ledger-core/libgncmod-ledger-core.la \
-  ${top_builddir}/src/report/report-gnome/libgncmod-report-gnome.la \
-  ${top_builddir}/src/app-utils/libgncmod-app-utils.la \
-  ${top_builddir}/src/backend/xml/libgnc-backend-xml-utils.la \
-  ${top_builddir}/src/engine/libgncmod-engine.la \
-  ${top_builddir}/src/core-utils/libgnc-core-utils.la \
-  ${top_builddir}/src/gnc-module/libgnc-module.la \
-  ${top_builddir}/src/libqof/qof/libgnc-qof.la \
-  ${top_builddir}/lib/stf/libgnc-stf.la \
-  ${GLIB_LIBS} \
-  $(BOOST_LDFLAGS)
+test_tx_import_SOURCES = \
+    test-tx-import.cpp
 
-#The tests might require more libraries, but try to keep them
-#as independent as possible.
-test_csv_imp_LDADD = \
-   $(IMP_TEST_LDFLAGS)
+if !GOOGLE_TEST_LIBS
+nodist_test_tokenizer_SOURCES = \
+        ${GTEST_SRC}/src/gtest_main.cc
 
-test_csv_imp_CFLAGS = \
-	-DTESTPROG=test_csv-imp \
-	${DEFAULT_INCLUDES} \
-	-I$(top_srcdir)/${MODULEPATH}/ \
-  -I${top_srcdir}/src/test-core \
-  -I${top_srcdir}/src \
-  -I${top_srcdir}/src/import-export \
-  -I${top_srcdir}/src/gnome \
-  -I${top_srcdir}/src/register/ledger-core \
-  -I${top_srcdir}/src/register/register-gnome \
-  -I${top_srcdir}/src/register/register-core \
-  -I${top_srcdir}/src/gnome-utils \
-  -I${top_srcdir}/src/app-utils \
-  -I${top_srcdir}/src/engine \
-  -I${top_srcdir}/src/core-utils \
-  -I${top_srcdir}/src/gnc-module \
-  -I${top_srcdir}/src/libqof/qof \
-  -I${top_srcdir}/lib/libc \
-  -I${top_srcdir}/lib \
-  ${GTK_CFLAGS} \
-  ${GLIB_CFLAGS}
+nodist_test_tx_import_SOURCES = \
+        ${GTEST_SRC}/src/gtest_main.cc
+endif
 
 IMP_TEST_CPPFLAGS =  \
 	${DEFAULT_INCLUDES} \
@@ -92,9 +55,39 @@ IMP_TEST_CPPFLAGS =  \
   ${GLIB_CFLAGS} \
   $(BOOST_CPPFLAGS)
 
-test_csv_imp_CPPFLAGS = \
-	-DTESTPROG=test_csv-imp \
-	$(IMP_TEST_CPPFLAGS)
+test_tokenizer_CPPFLAGS = \
+  -I$(GTEST_HEADERS) \
+  $(IMP_TEST_CPPFLAGS)
+
+test_tx_import_CPPFLAGS = \
+  -I$(GTEST_HEADERS) \
+  $(IMP_TEST_CPPFLAGS)
+
+#The tests might require more libraries, but try to keep them
+#as independent as possible.
+IMP_TEST_LDFLAGS = \
+  ${top_builddir}/${MODULEPATH}/libgncmod-csv-import.la \
+  ${top_builddir}/src/import-export/libgncmod-generic-import.la \
+  ${top_builddir}/src/gnome/libgnc-gnome.la \
+  ${top_builddir}/src/gnome-utils/libgncmod-gnome-utils.la \
+  ${top_builddir}/src/register/ledger-core/libgncmod-ledger-core.la \
+  ${top_builddir}/src/report/report-gnome/libgncmod-report-gnome.la \
+  ${top_builddir}/src/app-utils/libgncmod-app-utils.la \
+  ${top_builddir}/src/backend/xml/libgnc-backend-xml-utils.la \
+  ${top_builddir}/src/engine/libgncmod-engine.la \
+  ${top_builddir}/src/core-utils/libgnc-core-utils.la \
+  ${top_builddir}/src/gnc-module/libgnc-module.la \
+  ${top_builddir}/src/libqof/qof/libgnc-qof.la \
+  ${GLIB_LIBS} \
+  $(BOOST_LDFLAGS)
+
+test_tokenizer_LDADD = \
+  ${IMP_TEST_LDFLAGS} \
+  $(GTEST_LIBS)
+
+test_tx_import_LDADD = \
+  ${IMP_TEST_LDFLAGS} \
+  $(GTEST_LIBS)
 
 GNC_TEST_DEPS = \
 --library-dir    ${top_builddir}/${MODULEPATH} \
@@ -121,40 +114,6 @@ TESTS_ENVIRONMENT = \
   G_DEBUG= \
   $(shell ${abs_top_srcdir}/src/gnc-test-env.pl --noexports ${GNC_TEST_DEPS})
 
-test_tokenizer_SOURCES = \
-    test-tokenizer.cpp
-
-if !GOOGLE_TEST_LIBS
-nodist_test_tokenizer_SOURCES = \
-        ${GTEST_SRC}/src/gtest_main.cc
-endif
-
-test_tokenizer_CPPFLAGS = \
-  -I$(GTEST_HEADERS) \
-  $(IMP_TEST_CPPFLAGS)
-
-test_tokenizer_LDADD = \
-  ${IMP_TEST_LDFLAGS} \
-  $(GTEST_LIBS)
-
-test_tx_import_SOURCES = \
-    test-tx-import.cpp
-
-if !GOOGLE_TEST_LIBS
-nodist_test_tx_import_SOURCES = \
-        ${GTEST_SRC}/src/gtest_main.cc
-endif
-
-test_tx_import_CPPFLAGS = \
-  -I$(GTEST_HEADERS) \
-  $(IMP_TEST_CPPFLAGS)
-
-test_tx_import_LDADD = \
-  ${IMP_TEST_LDFLAGS} \
-  $(GTEST_LIBS)
-
-check_PROGRAMS += test-tokenizer \
-                  test-tx-import
 
 EXTRA_DIST= \
   sample1.csv
diff --git a/src/import-export/csv-imp/test/test-csv-imp.c b/src/import-export/csv-imp/test/test-csv-imp.c
deleted file mode 100644
index 6665de1..0000000
--- a/src/import-export/csv-imp/test/test-csv-imp.c
+++ /dev/null
@@ -1,60 +0,0 @@
-/********************************************************************
- * testmain.c: GLib g_test test execution file.			    *
- * Copyright 2011 John Ralls <jralls at ceridwen.us>		    *
- *                                                                  *
- * 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 2 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, contact:                        *
- *                                                                  *
- * Free Software Foundation           Voice:  +1-617-542-5942       *
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
- * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
-\********************************************************************/
-
-#include <glib.h>
-#include "config.h"
-#include <qof.h>
-#include "backend/xml/gnc-backend-xml.h"
-#include "gnc-module/gnc-module.h"
-#include "engine/gnc-engine.h"
-#include <engine/TransLog.h>
-
-/* Declare the test suite assembly functions (see test-suite.c) for
- * each sub-suite; avoids having header files. */
-
-extern GTestSuite *test_suite_gnc_csv_model();
-
-int
-main (int   argc,
-      char *argv[])
-{
-    qof_init(); 			/* Initialize the GObject system */
-    qof_log_init_filename_special("stderr"); /* Init the log system */
-    g_test_init ( &argc, &argv, NULL ); 	/* initialize test program */
-    qof_log_set_level("gnc", (QofLogLevel)G_LOG_LEVEL_DEBUG);
-    g_test_bug_base("https://bugzilla.gnome.org/show_bug.cgi?id="); /* init the bugzilla URL */
-    /* Disable the transaction log */
-    xaccLogDisable();
-
-    gnc_module_system_init();
-    gnc_engine_init_static(argc, argv);
-    qof_load_backend_library ("../../../backend/xml/.libs/",
-                              "gncmod-backend-xml");
-
-    /* Add test functions and suites. See
-     * http://library.gnome.org/devel/glib/stable/glib-Testing.html for
-     * details. Unfortunately, GLib-Testing doesn't provide the automatic
-     * registration features of more sophisticated frameworks. */
-    test_suite_gnc_csv_model();
-
-    return g_test_run();
-}
diff --git a/src/import-export/csv-imp/test/utest-gnc-csv-model.c b/src/import-export/csv-imp/test/utest-gnc-csv-model.c
deleted file mode 100644
index 8deea38..0000000
--- a/src/import-export/csv-imp/test/utest-gnc-csv-model.c
+++ /dev/null
@@ -1,462 +0,0 @@
-/********************************************************************
- * utest-gnc-csv-model.c: GLib g_test test suite for gnc-csv-model.c.		    *
- * Copyright 2015 Geert Janssens <geert.gnucash at kobaltwit.be>		    *
- *                                                                  *
- * 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 2 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, you can retrieve it from        *
- * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html            *
- * or contact:                                                      *
- *                                                                  *
- * Free Software Foundation           Voice:  +1-617-542-5942       *
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
- * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
- ********************************************************************/
-#include <config.h>
-#include <string.h>
-#include <glib.h>
-#include <unittest-support.h>
-/* Add specific headers for this class */
-#include "import-export/csv-imp/gnc-csv-model.h"
-
-typedef struct
-{
-    GncCsvParseData* parse_data;
-} Fixture;
-
-typedef struct
-{
-    int          date_fmt;
-    const gchar *date_str;
-    int          exp_year;
-    int          exp_month;
-    int          exp_day;
-} parse_date_data;
-
-typedef struct
-{
-    const gchar *csv_line;
-    int          num_fields;
-    const gchar *fields [8];
-} parse_test_data;
-
-static const gchar* samplefile1 = "sample1.csv";
-
-static parse_test_data comma_separated [] = {
-        { "Date,Num,Description,Notes,Account,Deposit,Withdrawal,Balance", 8, { "Date","Num","Description","Notes","Account","Deposit","Withdrawal","Balance" } },
-        { "05/01/15,45,Acme Inc.,,Miscellaneous,,\"1,100.00\",", 8, { "05/01/15","45","Acme Inc.","","Miscellaneous","","1,100.00","" } },
-        { "05/01/15,45,Acme Inc.,,Miscellaneous,", 4, { "05/01/15","45","Acme Inc.","",NULL,NULL,NULL,NULL } },
-        { NULL, 0, { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } },
-};
-
-static parse_test_data semicolon_separated [] = {
-        { "Date;Num;Description;Notes;Account;Deposit;Withdrawal;Balance", 8, { "Date","Num","Description","Notes","Account","Deposit","Withdrawal","Balance" } },
-        { "05/01/15;45;Acme Inc.;;Miscellaneous;;\"1,100.00\";", 8, { "05/01/15","45","Acme Inc.","","Miscellaneous","","1,100.00","" } },
-        { "05/01/15;45;Acme Inc.;;Miscellaneous;", 4, { "05/01/15","45","Acme Inc.","",NULL,NULL,NULL,NULL } },
-        { NULL, 0, { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } },
-};
-
-static char* get_filepath(const char* filename, gboolean test_existence)
-{
-    char *result;
-
-    const char *srcdir = g_getenv("SRCDIR");
-    if (!srcdir)
-    {
-        g_test_message("No env variable SRCDIR exists, assuming \".\"\n");
-        srcdir = ".";
-    }
-
-    result = g_strdup_printf("%s/%s", srcdir, filename);
-
-    g_test_message("Using file path %s\n", result);
-
-    // Test whether the file really exists
-    if (test_existence)
-        g_assert(g_file_test(result, G_FILE_TEST_EXISTS));
-
-    return result;
-}
-
-static void
-setup( Fixture *fixture, gconstpointer pData )
-{
-    fixture->parse_data = gnc_csv_new_parse_data ();
-}
-
-static void
-setup_one_file( Fixture *fixture, gconstpointer pData )
-{
-    const gchar *filename = (const gchar*) pData;
-    char *filepath = get_filepath (filename, TRUE);
-    GError *the_error = NULL;
-    int resultcode = 0;
-
-    fixture->parse_data = gnc_csv_new_parse_data ();
-    resultcode = gnc_csv_load_file (fixture->parse_data, filepath,
-                                    &the_error);
-    g_assert (resultcode == 0);
-    g_free(filepath);
-}
-
-static void
-teardown( Fixture *fixture, gconstpointer pData )
-{
-    gnc_csv_parse_data_free (fixture->parse_data);
-}
-
-static const gchar *suitename = "/import-export/csv-imp/gnc-csv-model";
-void test_suite_gnc_csv_model ( void );
-
-/* parse_date_with_year
-time64 parse_date_with_year (const char* date_str, int format)// Local: 1:0:0
-*/
-// Internal helper only - no tests.
-/* parse_date_without_year
-static time64 parse_date_without_year (const char* date_str, int format)// Local: 1:0:0
-*/
-// Internal helper only - no tests.
-
-/* parse_date
-time64 parse_date (const char* date_str, int format)// C: 14 in 7 SCM: 9 in 2 Local: 1:0:0
-*/
-static void
-test_parse_date (void)
-{
-    time64 rawtime = gnc_time (NULL);
-    struct tm *tm = gnc_gmtime (&rawtime);
-    int curr_year = tm->tm_year;
-
-
-    /* Note: tm_year = year - 1900 and tm_mon = 0-11
-     * I'm writing the expected values as subtractions for easier
-     * comparison with the date string under test
-     */
-    parse_date_data test_dates[] =
-    {
-        // supported combinations  -/.'
-        { 0, "2013-08-01", 2013 - 1900,  8 - 1,  1},
-        { 0,  "2013-8-01", 2013 - 1900,  8 - 1,  1},
-        { 0,  "2013-08-1", 2013 - 1900,  8 - 1,  1},
-        { 0,   "2013-8-1", 2013 - 1900,  8 - 1,  1},
-        { 0,   "13-08-01", 2013 - 1900,  8 - 1,  1},
-        { 0,    "13-8-01", 2013 - 1900,  8 - 1,  1},
-        { 0,    "13-08-1", 2013 - 1900,  8 - 1,  1},
-        { 0,     "13-8-1", 2013 - 1900,  8 - 1,  1},
-        { 0, "2009/11/04", 2009 - 1900, 11 - 1,  4},
-        { 0,  "1985.3.12", 1985 - 1900,  3 - 1, 12},
-        { 0,      "3'6'8", 2003 - 1900,  6 - 1,  8},
-        { 0,   "20130801", 2013 - 1900,  8 - 1,  1},
-        { 1, "01-08-2013", 2013 - 1900,  8 - 1,  1},
-        { 1,  "01-8-2013", 2013 - 1900,  8 - 1,  1},
-        { 1,  "1-08-2013", 2013 - 1900,  8 - 1,  1},
-        { 1,   "1-8-2013", 2013 - 1900,  8 - 1,  1},
-        { 1,   "01-08-13", 2013 - 1900,  8 - 1,  1},
-        { 1,    "01-8-13", 2013 - 1900,  8 - 1,  1},
-        { 1,    "1-08-13", 2013 - 1900,  8 - 1,  1},
-        { 1,     "1-8-13", 2013 - 1900,  8 - 1,  1},
-        { 1, "04/11/2009", 2009 - 1900, 11 - 1,  4},
-        { 1,  "12.3.1985", 1985 - 1900,  3 - 1, 12},
-        { 1,      "8'6'3", 2003 - 1900,  6 - 1,  8},
-        { 1,   "01082013", 2013 - 1900,  8 - 1,  1},
-        { 2, "08-01-2013", 2013 - 1900,  8 - 1,  1},
-        { 2,  "8-01-2013", 2013 - 1900,  8 - 1,  1},
-        { 2,  "08-1-2013", 2013 - 1900,  8 - 1,  1},
-        { 2,   "8-1-2013", 2013 - 1900,  8 - 1,  1},
-        { 2,   "08-01-13", 2013 - 1900,  8 - 1,  1},
-        { 2,    "8-01-13", 2013 - 1900,  8 - 1,  1},
-        { 2,    "08-1-13", 2013 - 1900,  8 - 1,  1},
-        { 2,     "8-1-13", 2013 - 1900,  8 - 1,  1},
-        { 2, "11/04/2009", 2009 - 1900, 11 - 1,  4},
-        { 2,  "3.12.1985", 1985 - 1900,  3 - 1, 12},
-        { 2,      "6'8'3", 2003 - 1900,  6 - 1,  8},
-        { 2,   "08012013", 2013 - 1900,  8 - 1,  1},
-        { 3,      "01-08",   curr_year,  8 - 1,  1},
-        { 3,       "01-8",   curr_year,  8 - 1,  1},
-        { 3,       "1-08",   curr_year,  8 - 1,  1},
-        { 3,        "1-8",   curr_year,  8 - 1,  1},
-        { 3,      "04/11",   curr_year, 11 - 1,  4},
-        { 3,       "12.3",   curr_year,  3 - 1, 12},
-        { 3,        "8'6",   curr_year,  6 - 1,  8},
-        { 4,      "08-01",   curr_year,  8 - 1,  1},
-        { 4,       "8-01",   curr_year,  8 - 1,  1},
-        { 4,       "08-1",   curr_year,  8 - 1,  1},
-        { 4,        "8-1",   curr_year,  8 - 1,  1},
-        { 4,      "11/04",   curr_year, 11 - 1,  4},
-        { 4,       "3.12",   curr_year,  3 - 1, 12},
-        { 4,        "6'8",   curr_year,  6 - 1,  8},
-
-        // ambiguous date formats
-        // current parser doesn't know how to disambiguate
-        // and hence refuses to parse
-        // can possibly improved with a smarter parser
-        { 0,     "130801",          -1,     -1, -1},
-        { 1,     "010813",          -1,     -1, -1},
-        { 2,     "080113",          -1,     -1, -1},
-        { 3,       "0108",          -1,     -1, -1},
-        { 4,       "0801",          -1,     -1, -1},
-
-        // Combinations that don't make sense
-        // but can still be entered by a user
-        // Should ideally all result in refusal to parse...
-        { 0,      "08-01",          -1,     -1, -1},
-        { 0,       "0801",          -1,     -1, -1},
-        { 1,      "01-08",          -1,     -1, -1},
-        { 1,       "0108",          -1,     -1, -1},
-        { 2,      "08-01",          -1,     -1, -1},
-        { 2,       "0801",          -1,     -1, -1},
-        { 3, "01-08-2013",   curr_year,  8 - 1,  1}, // BAD behavior !
-        { 3,   "01-08-13",   curr_year,  8 - 1,  1}, // BAD behavior !
-        { 3,   "08-08-08",   curr_year,  8 - 1,  8}, // BAD behavior !
-        { 3,   "01082013",          -1,     -1, -1},
-        { 3,     "010813",          -1,     -1, -1},
-        { 3,   "20130108",          -1,     -1, -1},
-        { 4, "08-01-2013",   curr_year,  8 - 1,  1}, // BAD behavior !
-        { 4,   "08-01-13",   curr_year,  8 - 1,  1}, // BAD behavior !
-        { 4, "2013-08-01",          -1,     -1, -1},
-        { 4,   "09-08-01",   curr_year,  9 - 1,  8}, // BAD behavior !
-        { 4,   "08012013",          -1,     -1, -1},
-        { 4,     "080113",          -1,     -1, -1},
-        { 4,   "20130801",          -1,     -1, -1},
-
-        // Sentinel to mark the end of available tests
-        { 0,         NULL,           0,      0,  0},
-
-    };
-    int i = 0;
-
-    gnc_tm_free(tm);
-    while (test_dates[i].date_str)
-    {
-        gboolean success = TRUE;
-        int got_year = 0, got_month = 0, got_day = 0;
-
-        rawtime = parse_date (test_dates[i].date_str, test_dates[i].date_fmt);
-        if (rawtime == -1)
-            got_year = got_month = got_day = -1;
-        else
-        {
-            tm = gnc_gmtime (&rawtime);
-            got_year = tm->tm_year;
-            got_month = tm->tm_mon;
-            got_day = tm->tm_mday;
-            gnc_tm_free(tm);
-        }
-
-        if ((got_year  != test_dates[i].exp_year) ||
-            (got_month != test_dates[i].exp_month) ||
-            (got_day   != test_dates[i].exp_day))
-        {
-            g_error ("Parse_date failed for date '%s' and format '%d'.\n"
-                            "Expected result: year %d, month %d, day %d\n"
-                            "Obtained result: year %d, month %d, day %d",
-                            test_dates[i].date_str,
-                            test_dates[i].date_fmt,
-                            test_dates[i].exp_year,
-                            test_dates[i].exp_month,
-                            test_dates[i].exp_day,
-                            got_year, got_month, got_day);
-        }
-
-        i++;
-    }
-
-
-}
-/* gnc_csv_new_parse_data
-GncCsvParseData* gnc_csv_new_parse_data (void)// C: 1 in 1  Local: 0:0:0
-*/
-static void
-test_gnc_csv_new_parse_data (void)
-{
-    GncCsvParseData* parse_data = gnc_csv_new_parse_data ();
-    g_assert (parse_data != NULL);
-    g_assert (parse_data->chunk != NULL);
-    gnc_csv_parse_data_free (parse_data);
-}
-
-/* gnc_csv_parse_data_free
-void gnc_csv_parse_data_free (GncCsvParseData* parse_data)// C: 3 in 1  Local: 0:0:0
-*/
-// Basic freeing of memory - no test
-
-/* gnc_csv_convert_encoding
-int gnc_csv_convert_encoding (GncCsvParseData* parse_data, const char* encoding,// C: 1  Local: 1:0:0
-*/
-/* static void
-test_gnc_csv_convert_encoding (Fixture *fixture, gconstpointer pData)
-{
-}*/
-/* gnc_csv_load_file
-int gnc_csv_load_file (GncCsvParseData* parse_data, const char* filename,// C: 1  Local: 0:0:0
-*/
-static void
-test_gnc_csv_load_file (Fixture *fixture, gconstpointer pData)
-{
-
-    char *file1 = get_filepath ("notexist.csv", FALSE);
-    char *file2 = get_filepath ("sample1.csv", TRUE);
-    GError *the_error = NULL;
-    int resultcode = 0;
-
-    /* Test loading of a non-existing file */
-    resultcode = gnc_csv_load_file (fixture->parse_data, file1,
-                                    &the_error);
-    g_assert ((the_error->domain == GNC_CSV_IMP_ERROR) &&
-              (the_error->code == GNC_CSV_IMP_ERROR_OPEN));
-
-    /* Test loading of a valid csv file */
-    resultcode = gnc_csv_load_file (fixture->parse_data, file2,
-                                    &the_error);
-    g_assert (resultcode == 0);
-}
-/* gnc_csv_parse
-int gnc_csv_parse (GncCsvParseData* parse_data, gboolean guessColTypes, GError** error)// C: 13 in 1  Local: 0:0:0
-*/
-static void
-test_gnc_csv_parse_from_file (Fixture *fixture, gconstpointer pData)
-{
-    GError *the_error = NULL;
-    int resultcode = 0;
-
-    /* Test basic parsing of the loaded file
-     * A few fields are sampled in the parsed data. */
-    resultcode = gnc_csv_parse (fixture->parse_data, TRUE, &the_error);
-    g_assert (g_strcmp0 ((char*)((GPtrArray*)(fixture->parse_data->orig_lines->pdata[0]))->pdata[0],
-                         "Date") == 0);
-    g_assert (g_strcmp0 ((char*)((GPtrArray*)(fixture->parse_data->orig_lines->pdata[1]))->pdata[6],
-                         "1,100.00") == 0);
-}
-
-/* Test parsing for several different prepared strings
- * These tests bypass file loading, rather taking a
- * prepared set of strings as input. This makes it
- * easier to add test cases without having to create new test files
- * each time to load from.
- * Note this bypasses encoding configuration, which should be tested
- * independently.
- */
-
-/* This helper function will run the parse step on the given data
- * with the parser as configured by the calling test function.
- * This allows the same code to be used with different csv test strings
- * and parser option combinations.
- */
-static void
-test_gnc_csv_parse_helper (GncCsvParseData *parse_data, gconstpointer pData)
-{
-    parse_test_data *test_data = (parse_test_data *) pData;
-    GError *the_error = NULL;
-    int resultcode = 0;
-    int i = 0;
-
-    while (test_data[i].csv_line)
-    {
-        int j;
-        parse_test_data cur_line = test_data[i];
-
-        g_test_message("Using string %s\n", cur_line.csv_line);
-        g_free (parse_data->file_str.begin);
-        parse_data->file_str.begin = g_strdup (cur_line.csv_line);
-        parse_data->file_str.end = parse_data->file_str.begin + strlen (parse_data->file_str.begin);
-        resultcode = gnc_csv_parse (parse_data, TRUE, &the_error);
-        g_assert (resultcode == 0);
-        for (j=0; j < cur_line.num_fields; j++)
-        {
-            g_assert (g_strcmp0 ((char*)((GPtrArray*)(parse_data->orig_lines->pdata[0]))->pdata[j],
-                     (cur_line.fields[j])) == 0);
-        }
-
-        i++;
-    }
-}
-
-static void
-test_gnc_csv_parse_comma_sep (Fixture *fixture, gconstpointer pData)
-{
-    GSList* sep_list = NULL;
-
-    sep_list = g_slist_append (sep_list, ",");
-    stf_parse_options_csv_set_separators (fixture->parse_data->options, NULL, sep_list);
-    g_slist_free (sep_list);
-
-    test_gnc_csv_parse_helper (fixture->parse_data, pData);
-}
-
-static void
-test_gnc_csv_parse_semicolon_sep (Fixture *fixture, gconstpointer pData)
-{
-    GSList* sep_list = NULL;
-
-    sep_list = g_slist_append (sep_list, ";");
-    stf_parse_options_csv_set_separators (fixture->parse_data->options, NULL, sep_list);
-    g_slist_free (sep_list);
-
-    test_gnc_csv_parse_helper (fixture->parse_data, pData);
-}
-
-/* trans_property_free
-static void trans_property_free (TransProperty* prop)// Local: 2:0:0
-*/
-// Internal helper function - no test
-/* trans_property_set
-static gboolean trans_property_set (TransProperty* prop, char* str)// Local: 1:0:0
-*/
-// Internal helper function - no test
-/* trans_property_list_free
-static void trans_property_list_free (TransPropertyList* list)// Local: 1:0:0
-*/
-// Internal helper function - no test
-/* trans_property_list_add
-static void trans_property_list_add (TransProperty* property)// Local: 1:0:0
-*/
-// Internal helper function - no test
-/* trans_add_split
-static void trans_add_split (Transaction* trans, Account* account, QofBook* book,// Local: 2:0:0
-*/
-// Internal helper function - no test
-/* trans_property_list_verify_essentials
-static gboolean trans_property_list_verify_essentials (TransPropertyList* list, gchar** error)// Local: 1:0:0
-*/
-// Internal helper function - no test
-/* gnc_csv_parse_to_trans
-int gnc_csv_parse_to_trans (GncCsvParseData* parse_data, Account* account,// C: 2 in 1  Local: 0:0:0
-*/
-/* static void
-test_gnc_csv_parse_to_trans (Fixture *fixture, gconstpointer pData)
-{
-}*/
-
-
-void
-test_suite_gnc_csv_model (void)
-{
-
-// GNC_TEST_ADD (suitename, "parse date with year", Fixture, NULL, setup, test_parse_date_with_year, teardown);
-// GNC_TEST_ADD (suitename, "parse date without year", Fixture, NULL, setup, test_parse_date_without_year, teardown);
-GNC_TEST_ADD_FUNC (suitename, "parse date", test_parse_date);
-GNC_TEST_ADD_FUNC (suitename, "gnc csv new parse data", test_gnc_csv_new_parse_data);
-// GNC_TEST_ADD (suitename, "gnc csv parse data free", Fixture, NULL, setup, test_gnc_csv_parse_data_free, teardown);
-// GNC_TEST_ADD (suitename, "gnc csv convert encoding", Fixture, NULL, setup, test_gnc_csv_convert_encoding, teardown);
-GNC_TEST_ADD (suitename, "gnc csv load file", Fixture, NULL, setup, test_gnc_csv_load_file, teardown);
-GNC_TEST_ADD (suitename, "gnc csv parse from file", Fixture, samplefile1, setup_one_file, test_gnc_csv_parse_from_file, teardown);
-GNC_TEST_ADD (suitename, "parse comma", Fixture, comma_separated, setup, test_gnc_csv_parse_comma_sep, teardown);
-GNC_TEST_ADD (suitename, "parse semicolon", Fixture, semicolon_separated, setup, test_gnc_csv_parse_semicolon_sep, teardown);
-// GNC_TEST_ADD (suitename, "trans property free", Fixture, NULL, setup, test_trans_property_free, teardown);
-// GNC_TEST_ADD (suitename, "trans property set", Fixture, NULL, setup, test_trans_property_set, teardown);
-// GNC_TEST_ADD (suitename, "trans property list free", Fixture, NULL, setup, test_trans_property_list_free, teardown);
-// GNC_TEST_ADD (suitename, "trans property list add", Fixture, NULL, setup, test_trans_property_list_add, teardown);
-// GNC_TEST_ADD (suitename, "trans add split", Fixture, NULL, setup, test_trans_add_split, teardown);
-// GNC_TEST_ADD (suitename, "trans property list verify essentials", Fixture, NULL, setup, test_trans_property_list_verify_essentials, teardown);
-// GNC_TEST_ADD (suitename, "gnc csv parse to trans", Fixture, NULL, setup, test_gnc_csv_parse_to_trans, teardown);
-
-}

commit 861bff3f3ba46d8fb0b531f892091b8c44543b6e
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sat Dec 3 17:45:03 2016 +0100

    Fix error handling in the multi-split case (and probaly in the other case as well)
    
    Dereferencing an iterator and then assigning it to another variable
    apparently copies the object the iterator points at, instead
    of making a reference to it. C++ beginner mistakes...
    Also do the multi-split parent dance before handling errors. Otherwise
    child lines would be mistakenly added to the first working parent split
    instead of also being skipped until the parent is fixed.

diff --git a/src/import-export/csv-imp/gnc-trans-props.cpp b/src/import-export/csv-imp/gnc-trans-props.cpp
index 7fd2c21..2e08dcb 100644
--- a/src/import-export/csv-imp/gnc-trans-props.cpp
+++ b/src/import-export/csv-imp/gnc-trans-props.cpp
@@ -248,7 +248,7 @@ std::string GncPreTrans::verify_essentials (void)
 {
     /* Make sure this transaction has the minimum required set of properties defined */
     if (!m_date)
-        return N_("No date column.");
+        return _("No date column.");
     else
         return std::string();
 }
@@ -349,7 +349,7 @@ std::string GncPreSplit::verify_essentials (void)
 {
     /* Make sure this split has the minimum required set of properties defined. */
     if (!m_deposit && !m_withdrawal && !m_balance)
-        return N_("No balance, deposit, or withdrawal column.");
+        return _("No balance, deposit, or withdrawal column.");
     else
         return std::string();
 }
diff --git a/src/import-export/csv-imp/gnc-tx-import.cpp b/src/import-export/csv-imp/gnc-tx-import.cpp
index 03cdef8..94e40e9 100644
--- a/src/import-export/csv-imp/gnc-tx-import.cpp
+++ b/src/import-export/csv-imp/gnc-tx-import.cpp
@@ -212,13 +212,13 @@ void GncTxImport::tokenize (bool guessColTypes)
  * @param parsed_line The line we are checking
  * @exception std::invalid_argument in an essential property is missing
  */
-static void trans_properties_verify_essentials (parse_line_t& parsed_line)
+static void trans_properties_verify_essentials (std::vector<parse_line_t>::iterator& parsed_line)
 {
     std::string error_message;
     std::shared_ptr<GncPreTrans> trans_props;
     std::shared_ptr<GncPreSplit> split_props;
 
-    std::tie(std::ignore, error_message, trans_props, split_props) = parsed_line;
+    std::tie(std::ignore, error_message, trans_props, split_props) = *parsed_line;
 
     auto trans_error = trans_props->verify_essentials();
     auto split_error = split_props->verify_essentials();
@@ -243,13 +243,13 @@ static void trans_properties_verify_essentials (parse_line_t& parsed_line)
  * @param parsed_line The current line being parsed
  * @return On success, a shared pointer to a DraftTransaction object; on failure a nullptr
  */
-std::shared_ptr<DraftTransaction> GncTxImport::trans_properties_to_trans (parse_line_t& parsed_line)
+std::shared_ptr<DraftTransaction> GncTxImport::trans_properties_to_trans (std::vector<parse_line_t>::iterator& parsed_line)
 {
     auto created_trans = false;
     std::string error_message;
     std::shared_ptr<GncPreTrans> trans_props;
     std::shared_ptr<GncPreSplit> split_props;
-    std::tie(std::ignore, error_message, trans_props, split_props) = parsed_line;
+    std::tie(std::ignore, error_message, trans_props, split_props) = *parsed_line;
     auto account = split_props->get_account();
 
     QofBook* book = gnc_account_get_book (account);
@@ -334,13 +334,13 @@ void GncTxImport::adjust_balances (void)
 
 }
 
-void GncTxImport::create_transaction (parse_line_t& parsed_line)
+void GncTxImport::create_transaction (std::vector<parse_line_t>::iterator& parsed_line)
 {
     StrVec line;
     std::string error_message;
     std::shared_ptr<GncPreTrans> trans_props;
     std::shared_ptr<GncPreSplit> split_props;
-    std::tie(line, error_message, trans_props, split_props) = parsed_line;
+    std::tie(line, error_message, trans_props, split_props) = *parsed_line;
     error_message.clear();
 
     /* Convert all tokens in this line into transaction/split properties. */
@@ -364,18 +364,42 @@ void GncTxImport::create_transaction (parse_line_t& parsed_line)
             else
                 split_props->set_property(*col_types_it, *line_it, currency_format);
         }
-        catch (const std::invalid_argument&)
+        catch (const std::exception& e)
         {
             parse_errors = true;
+            if (!error_message.empty())
+                error_message += "\n";
             error_message += _(gnc_csv_col_type_strs[*col_types_it]);
             error_message += _(" column could not be understood.");
-            error_message += "\n";
             PINFO("User warning: %s", error_message.c_str());
         }
     }
 
+    /* For multi-split input data, we need to check whether this line is part of a transaction that
+     * has already be started by a previous line. */
+    if (multi_split)
+    {
+        if (trans_props->is_part_of(parent))
+        {
+            /* This line is part of an already started transaction
+             * continue with that one instead to make sure the split from this line
+             * gets added to the proper transaction */
+            std::get<2>(*parsed_line) = parent;
+
+            /* Check if the parent line is ready for conversion. If not,
+             * this child line can't be converted either.
+             */
+            if (!parent->verify_essentials().empty())
+                error_message = _("First line of this transaction has errors.");
+        }
+        else
+            /* This line starts a new transaction, set it as parent for
+             * subsequent lines. */
+            parent = trans_props;
+    }
+
     if (!error_message.empty())
-        throw std::invalid_argument(error_message);
+        throw std::invalid_argument (error_message);
 
     // Add an ACCOUNT property with the default account if no account column was set by the user
     auto line_acct = split_props->get_account();
@@ -395,24 +419,6 @@ void GncTxImport::create_transaction (parse_line_t& parsed_line)
         }
     }
 
-    /* For multi-split input data, we need to check whether this line is part of a transaction that
-     * has already be started by a previous line. */
-    if (multi_split)
-    {
-        if (trans_props->is_part_of(parent))
-        {
-            /* So this line is part of a already started transaction
-             * continue with that one instead to make sure the split from this line
-             * gets added to the proper transaction */
-            //trans_props = parent;
-            std::get<2>(parsed_line) = parent;
-        }
-        else
-            /* This line starts a new transaction, set it as parent for
-             * subsequent lines. */
-            parent = trans_props;
-    }
-
     /* If column parsing was successful, convert trans properties into a draft transaction. */
     try
     {
@@ -478,24 +484,23 @@ void GncTxImport::create_transactions (Account* account,
             parsed_lines_it < parsed_lines_max;
             ++parsed_lines_it, odd_line = !odd_line)
     {
-        auto parsed_line = *parsed_lines_it;
-
         /* Skip current line if:
            1. only looking for lines with error AND no error on current line
            OR
            2. looking for all lines AND
               skip_rows is enabled AND
               current line is an odd line */
-        if ((redo_errors && std::get<1>(parsed_line).empty()) ||
+        if ((redo_errors && std::get<1>(*parsed_lines_it).empty()) ||
            (!redo_errors && skip_alt_lines && odd_line))
             continue;
 
         try
         {
-            create_transaction (parsed_line);
+            create_transaction (parsed_lines_it);
         }
-        catch (const std::invalid_argument&)
+        catch (const std::invalid_argument& e)
         {
+            std::get<1>(*parsed_lines_it) = e.what();
             continue;
         }
     }
diff --git a/src/import-export/csv-imp/gnc-tx-import.hpp b/src/import-export/csv-imp/gnc-tx-import.hpp
index 3d2f1fc..7d7eefc 100644
--- a/src/import-export/csv-imp/gnc-tx-import.hpp
+++ b/src/import-export/csv-imp/gnc-tx-import.hpp
@@ -130,7 +130,7 @@ private:
      *  to convert a single tokenized line into a transaction using
      *  the column types the user has set.
      */
-    void create_transaction (parse_line_t& parsed_line);
+    void create_transaction (std::vector<parse_line_t>::iterator& parsed_line);
 
     /** A helper function used by create_transactions. If the input data has
      *  a balance column (an no deposit and withdrawal columns)
@@ -142,7 +142,7 @@ private:
     /* Internal helper function that does the actual conversion from property lists
      * to real (possibly unbalanced) transaction with splits.
      */
-    std::shared_ptr<DraftTransaction> trans_properties_to_trans (parse_line_t& parsed_line);
+    std::shared_ptr<DraftTransaction> trans_properties_to_trans (std::vector<parse_line_t>::iterator& parsed_line);
 
     GncImpFileFormat file_fmt = GncImpFileFormat::UNKNOWN;
 

commit 8e20c6404e28c16726e28d1cb2b1f17de9420ae0
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Fri Dec 2 14:25:20 2016 +0100

    Introduce multi-split imports in the csv importer
    
    This should be the start for round-trip export-import.
    Full roundtrip is not supported yet though. Missing
    is multi-currency/commodity imports.

diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index 62ce7f9..1ae08f0 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -90,6 +90,7 @@ typedef struct
     GtkWidget       *skip_rows;                     /**< The widget for Skip alternate rows from start row */
     GtkWidget       *csv_button;                    /**< The widget for the CSV button */
     GtkWidget       *fixed_button;                  /**< The widget for the Fixed Width button */
+    GtkWidget       *multi_split_cbutton;           /**< The widget for Multi-split */
     int              home_account_number;           /**< The number of unique home account strings */
 
     GncTxImport     *parse_data;                    /**< The actual data we are previewing */
@@ -159,6 +160,7 @@ void csv_import_trans_srow_cb (GtkWidget *spin, gpointer user_data);
 void csv_import_trans_erow_cb (GtkWidget *spin, gpointer user_data);
 void csv_import_trans_skip_errors_cb (GtkWidget *cb, gpointer user_data);
 void csv_import_trans_skiprows_cb (GtkWidget *checkbox, gpointer user_data);
+void csv_import_trans_multisplit_cb (GtkWidget *checkbox, gpointer user_data);
 void csv_import_trans_auto_cb (GtkWidget *cb, gpointer user_data);
 void csv_import_trans_file_chooser_confirm_cb (GtkWidget *button, CsvImportTrans *info);
 
@@ -245,6 +247,10 @@ csv_import_trans_load_settings (CsvImportTrans *info)
     info->parse_data->skip_alt_lines = info->settings_data->skip_alt_rows;
     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->skip_rows), info->settings_data->skip_alt_rows);
 
+    // Set Multi-split indicator
+    info->parse_data->multi_split = info->settings_data->multi_split;
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->multi_split_cbutton), info->settings_data->multi_split);
+
     // Set Import Format
     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->csv_button), info->settings_data->csv_format);
     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->fixed_button), !info->settings_data->csv_format);
@@ -553,6 +559,7 @@ csv_import_trans_save_settings_cb (GtkWidget *button, CsvImportTrans *info)
         info->settings_data->date_active = gtk_combo_box_get_active (GTK_COMBO_BOX(info->date_format_combo));
         info->settings_data->currency_active = gtk_combo_box_get_active (GTK_COMBO_BOX(info->currency_format_combo));
         info->settings_data->encoding = go_charmap_sel_get_encoding (info->encselector);
+        info->settings_data->multi_split = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(info->multi_split_cbutton));
 
         /* This section deals with the Treeview column names */
         columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(info->ctreeview));
@@ -826,6 +833,20 @@ void csv_import_trans_skip_errors_cb (GtkWidget *cb, gpointer user_data)
 
 
 /*******************************************************
+ * csv_import_trans_multisplit_cb
+ *
+ * call back for import multi-split checkbox
+ *******************************************************/
+void csv_import_trans_multisplit_cb (GtkWidget *checkbox, gpointer user_data)
+{
+    CsvImportTrans *info = (CsvImportTrans*) user_data;
+
+    /* Set the skip_alt_lines variable */
+    info->parse_data->multi_split = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(checkbox));
+}
+
+
+/*******************************************************
  * csv_import_trans_skiprows_cb
  *
  * call back for import skip rows checkbox
@@ -1603,6 +1624,8 @@ bool get_list_of_accounts (CsvImportTrans* info, GtkTreeModel *store)
                 if (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL(datastore), &iter2, NULL, j))
                 {
                     gtk_tree_model_get (datastore, &iter2, i + 1, &accstr, -1);
+                    if (!accstr || *accstr == '\0')
+                        continue;
 
                     // Append the entry
                     if (!check_for_duplicates (GTK_LIST_STORE(store), accstr))
@@ -1843,6 +1866,9 @@ void gnc_csv_reset_preview_setting (CsvImportTrans *info, bool block)
     // Reset Skip Rows
     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->skip_rows), FALSE);
 
+    // Reset Multi-split
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->multi_split_cbutton), FALSE);
+
     // Reset Import Format
     g_signal_handlers_block_by_func (info->csv_button, (gpointer) separated_or_fixed_selected, info);
     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->csv_button), TRUE);
@@ -1984,6 +2010,7 @@ csv_import_trans_assistant_preview_page_prepare (GtkAssistant *assistant,
         gtk_widget_set_sensitive (info->start_row_spin, FALSE);
         gtk_widget_set_sensitive (info->end_row_spin, FALSE);
         gtk_widget_set_sensitive (info->skip_rows, FALSE);
+        gtk_widget_set_sensitive (info->multi_split_cbutton, FALSE);
         info->parse_data->skip_alt_lines = FALSE;
 
         /* Show the skip errors check button */
@@ -2800,6 +2827,7 @@ csv_import_trans_assistant_create (CsvImportTrans *info)
         info->end_row_spin = GTK_WIDGET(gtk_builder_get_object (builder, "end_row"));
         info->skip_rows = GTK_WIDGET(gtk_builder_get_object (builder, "skip_rows"));
         info->check_butt = GTK_WIDGET(gtk_builder_get_object (builder, "check_butt"));
+        info->multi_split_cbutton = GTK_WIDGET(gtk_builder_get_object (builder, "multi_split_button"));
 
         /* Load the separator buttons from the glade builder file into the
          * info->sep_buttons array. */
diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.glade b/src/import-export/csv-imp/assistant-csv-trans-import.glade
index 887a4cb..54096fa 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.glade
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.glade
@@ -377,6 +377,7 @@ Select location and file name for the Import, then click 'OK'...
 When Multi-split is enabled the importer will assume multiple consecutive lines together hold the information for one transaction. Each line provides information for exactly one split. The first line should also provide the information for the transaction.
 To know which lines belong to the same transaction, the importer will compare the provided transaction information in each line. If that information is empty or the same as the first transaction line the importer will consider this line part of the same transaction.</property>
                                     <property name="draw_indicator">True</property>
+                                    <signal name="toggled" handler="csv_import_trans_multisplit_cb" swapped="no"/>
                                   </object>
                                   <packing>
                                     <property name="expand">True</property>
diff --git a/src/import-export/csv-imp/gnc-csv-trans-settings.c b/src/import-export/csv-imp/gnc-csv-trans-settings.c
index 7f70efc..c4d909e 100644
--- a/src/import-export/csv-imp/gnc-csv-trans-settings.c
+++ b/src/import-export/csv-imp/gnc-csv-trans-settings.c
@@ -38,6 +38,7 @@
 #define CSV_ALT_ROWS     "AltRows"
 #define CSV_SKIP_START   "SkipStartRows"
 #define CSV_SKIP_END     "SkipEndRows"
+#define CSV_MULTI_SPLIT  "MultiSplit"
 
 /* The following two key names are only used by gnucash 2.6
  * They have been superseded by CSV_SKIP_START and CSV_SKIP_END.
@@ -70,6 +71,7 @@ CsvSettings * gnc_csv_trans_new_settings_data (void)
 
     settings_data->header_rows = 1;
     settings_data->skip_alt_rows = FALSE;
+    settings_data->multi_split = FALSE;
     settings_data->csv_format = TRUE;
 
     settings_data->encoding = "UTF-8";
@@ -235,6 +237,10 @@ gnc_csv_trans_load_settings (CsvSettings *settings_data, gchar *group)
     settings_data->skip_alt_rows = (key_error) ? FALSE : key_boolean;
     error |= handle_load_error (&key_error, group);
 
+    key_boolean = g_key_file_get_boolean (keyfile, group, CSV_MULTI_SPLIT, &key_error);
+    settings_data->multi_split = (key_error) ? FALSE : key_boolean;
+    error |= handle_load_error (&key_error, group);
+
     key_boolean = g_key_file_get_boolean (keyfile, group, CSV_FORMAT, &key_error);
     settings_data->csv_format = (key_error) ? TRUE : key_boolean;
     error |= handle_load_error (&key_error, group);
@@ -334,6 +340,7 @@ gnc_csv_trans_save_settings (CsvSettings *settings_data, gchar *settings_name)
     // Start Saving the settings
     g_key_file_set_string (keyfile, group, CSV_NAME, settings_name);
 
+    g_key_file_set_boolean (keyfile, group, CSV_MULTI_SPLIT, settings_data->multi_split);
     g_key_file_set_integer (keyfile, group, CSV_SKIP_START, settings_data->header_rows);
     g_key_file_set_integer (keyfile, group, CSV_SKIP_END, settings_data->footer_rows);
     g_key_file_set_boolean (keyfile, group, CSV_ALT_ROWS, settings_data->skip_alt_rows);
diff --git a/src/import-export/csv-imp/gnc-csv-trans-settings.h b/src/import-export/csv-imp/gnc-csv-trans-settings.h
index d689c91..08438c2 100644
--- a/src/import-export/csv-imp/gnc-csv-trans-settings.h
+++ b/src/import-export/csv-imp/gnc-csv-trans-settings.h
@@ -42,6 +42,7 @@ typedef struct
     int           footer_rows;                  // Number of footer rows
     gboolean      csv_format;                   // CSV import Format
     gboolean      skip_alt_rows;                // Skip alternate rows
+    gboolean      multi_split;                  // Assume multiple lines per transaction
 
     const gchar  *encoding;                     // File encoding
 
diff --git a/src/import-export/csv-imp/gnc-trans-props.cpp b/src/import-export/csv-imp/gnc-trans-props.cpp
index 5699b92..7fd2c21 100644
--- a/src/import-export/csv-imp/gnc-trans-props.cpp
+++ b/src/import-export/csv-imp/gnc-trans-props.cpp
@@ -49,6 +49,7 @@ std::map<GncTransPropType, const char*> gnc_csv_col_type_strs = {
         { GncTransPropType::DATE, N_("Date") },
         { GncTransPropType::NUM, N_("Num") },
         { GncTransPropType::DESCRIPTION, N_("Description") },
+        { GncTransPropType::UNIQUE_ID, N_("Transaction ID") },
         { GncTransPropType::NOTES, N_("Notes") },
         { GncTransPropType::ACCOUNT, N_("Account") },
         { GncTransPropType::DEPOSIT, N_("Deposit") },
@@ -228,6 +229,13 @@ void GncPreTrans::set_property (GncTransPropType prop_type, const std::string& v
                 m_notes = boost::none;
             break;
 
+        case GncTransPropType::UNIQUE_ID:
+            if (!value.empty())
+                m_differ = value;
+            else
+                m_differ = boost::none;
+            break;
+
         default:
             /* Issue a warning for all other prop_types. */
             PWARN ("%d is an invalid property for a transaction", static_cast<int>(prop_type));
@@ -266,6 +274,16 @@ Transaction* GncPreTrans::create_trans (QofBook* book, gnc_commodity* currency)
     return trans;
 }
 
+bool GncPreTrans::is_part_of (std::shared_ptr<GncPreTrans> parent)
+{
+    if (!parent)
+        return false;
+
+    return (!m_date || m_date == parent->m_date) &&
+            (!m_desc || m_desc == parent->m_desc) &&
+            (!m_notes || m_notes == parent->m_notes) &&
+            (!m_differ || m_differ == parent->m_differ);
+}
 
 void GncPreSplit::set_property (GncTransPropType prop_type, const std::string& value, int currency_format)
 {
diff --git a/src/import-export/csv-imp/gnc-trans-props.hpp b/src/import-export/csv-imp/gnc-trans-props.hpp
index e631458..0cf99a6 100644
--- a/src/import-export/csv-imp/gnc-trans-props.hpp
+++ b/src/import-export/csv-imp/gnc-trans-props.hpp
@@ -45,7 +45,8 @@ enum class GncTransPropType {
     DATE,
     DESCRIPTION,
     NOTES,
-    TRANS_PROPS = NOTES,
+    UNIQUE_ID,
+    TRANS_PROPS = UNIQUE_ID,
 
     // num is strictly speaking a trans prop and not a split prop
     // however due to the num/action swap user option, it can only be
@@ -77,10 +78,27 @@ public:
     std::string verify_essentials (void);
     Transaction *create_trans (QofBook* book, gnc_commodity* currency);
 
+    /** Check whether the harvested transaction properties for this instance
+     *  match those of another one (the "parent"). Note this function is *not*
+     *  symmetrical. This instance can have empty properties and still be considered
+     *  part of the parent if the other properties match the parent's.
+     *  A fully empty instance will will equally be considered part of the parent.
+     *
+     *  This function is intended to discover multi-split transaction lines in an import
+     *  file where the first line defines the transaction (with a first split) and subsequent
+     *  lines add splits. These subsequent lines can either have all transaction related
+     *  columns be empty or the same as the first line.
+     *
+     *  @param parent the parent transaction property object to test against
+     *  @returns true if this object is considered to be part of the parent, false otherwise.
+     */
+    bool is_part_of (std::shared_ptr<GncPreTrans> parent);
+
 private:
     boost::optional<time64> m_date;
     boost::optional<std::string> m_desc;
     boost::optional<std::string> m_notes;
+    boost::optional<std::string> m_differ;
     bool created = false;
 };
 
diff --git a/src/import-export/csv-imp/gnc-tx-import.cpp b/src/import-export/csv-imp/gnc-tx-import.cpp
index bdcd448..03cdef8 100644
--- a/src/import-export/csv-imp/gnc-tx-import.cpp
+++ b/src/import-export/csv-imp/gnc-tx-import.cpp
@@ -72,6 +72,7 @@ GncTxImport::GncTxImport(GncImpFileFormat format)
     skip_end_lines = 0;
     skip_alt_lines = FALSE;
     parse_errors = false;
+    multi_split = false;
 
     file_fmt = format;
     tokenizer = gnc_tokenizer_factory(file_fmt);
@@ -242,8 +243,9 @@ static void trans_properties_verify_essentials (parse_line_t& parsed_line)
  * @param parsed_line The current line being parsed
  * @return On success, a shared pointer to a DraftTransaction object; on failure a nullptr
  */
-static std::shared_ptr<DraftTransaction> trans_properties_to_trans (parse_line_t& parsed_line)
+std::shared_ptr<DraftTransaction> GncTxImport::trans_properties_to_trans (parse_line_t& parsed_line)
 {
+    auto created_trans = false;
     std::string error_message;
     std::shared_ptr<GncPreTrans> trans_props;
     std::shared_ptr<GncPreSplit> split_props;
@@ -255,18 +257,31 @@ static std::shared_ptr<DraftTransaction> trans_properties_to_trans (parse_line_t
 
     auto trans = trans_props->create_trans (book, currency);
 
+    if (trans)
+    {
+        current_draft = std::make_shared<DraftTransaction>(trans);
+        created_trans = true;
+    }
+    else if (multi_split)  // in multi_split mode create_trans will return a nullptr for all but the first split
+        trans = current_draft->trans;
+    else // in non-multi-split mode each line should be a transaction, so not having one here is an error
+        throw std::invalid_argument ("Failed to create transaction from selected columns.");
+
     if (!trans)
         return nullptr;
 
-    auto draft_trans = std::make_shared<DraftTransaction>(trans);
     auto balance = split_props->create_split(trans);
     if (balance)
     {
-        draft_trans->balance_set = true;
-        draft_trans->balance = *balance;
+        current_draft->balance_set = true;
+        current_draft->balance = *balance;
     }
 
-    return draft_trans;
+    /* Only return the draft transaction if we really created a new one
+     * The return value will be added to a list for further processing,
+     * we want each transaction to appear only once in that list.
+     */
+    return created_trans ? current_draft : nullptr;
 }
 
 void GncTxImport::adjust_balances (void)
@@ -341,7 +356,11 @@ void GncTxImport::create_transaction (parse_line_t& parsed_line)
             if (*col_types_it == GncTransPropType::NONE)
                 continue; /* We do nothing with "None"-type columns. */
             else if  (*col_types_it <= GncTransPropType::TRANS_PROPS)
+            {
+                if (multi_split && line_it->empty())
+                    continue; // In multi-split mode, transaction properties can be empty
                 trans_props->set_property(*col_types_it, *line_it, date_format);
+            }
             else
                 split_props->set_property(*col_types_it, *line_it, currency_format);
         }
@@ -351,6 +370,7 @@ void GncTxImport::create_transaction (parse_line_t& parsed_line)
             error_message += _(gnc_csv_col_type_strs[*col_types_it]);
             error_message += _(" column could not be understood.");
             error_message += "\n";
+            PINFO("User warning: %s", error_message.c_str());
         }
     }
 
@@ -370,11 +390,30 @@ void GncTxImport::create_transaction (parse_line_t& parsed_line)
             parse_errors = true;
             error_message = _("No account column selected and no default account specified either.\n"
                                        "This should never happen. Please report this as a bug.");
+            PINFO("User warning: %s", error_message.c_str());
             throw std::invalid_argument(error_message);
         }
     }
 
-    /* If column parsing was successful, convert trans properties into a trans line. */
+    /* For multi-split input data, we need to check whether this line is part of a transaction that
+     * has already be started by a previous line. */
+    if (multi_split)
+    {
+        if (trans_props->is_part_of(parent))
+        {
+            /* So this line is part of a already started transaction
+             * continue with that one instead to make sure the split from this line
+             * gets added to the proper transaction */
+            //trans_props = parent;
+            std::get<2>(parsed_line) = parent;
+        }
+        else
+            /* This line starts a new transaction, set it as parent for
+             * subsequent lines. */
+            parent = trans_props;
+    }
+
+    /* If column parsing was successful, convert trans properties into a draft transaction. */
     try
     {
         trans_properties_verify_essentials (parsed_line);
@@ -394,6 +433,7 @@ void GncTxImport::create_transaction (parse_line_t& parsed_line)
     {
         parse_errors = true;
         error_message = e.what();
+        PINFO("User warning: %s", error_message.c_str());
     }
 }
 
@@ -431,6 +471,7 @@ void GncTxImport::create_transactions (Account* account,
     base_account = account;
     auto odd_line = false;
     parse_errors = false;
+    parent = nullptr;
 
     /* Iterate over all parsed lines */
     for (parsed_lines_it, odd_line;
diff --git a/src/import-export/csv-imp/gnc-tx-import.hpp b/src/import-export/csv-imp/gnc-tx-import.hpp
index 01dd2fb..3d2f1fc 100644
--- a/src/import-export/csv-imp/gnc-tx-import.hpp
+++ b/src/import-export/csv-imp/gnc-tx-import.hpp
@@ -116,6 +116,12 @@ public:
     guint skip_start_lines;     /**< Number of lines to skip at the beginning of the parse data. */
     guint skip_end_lines;       /**< Number of lines to skip at the end of the parse data. */
     gboolean skip_alt_lines;         /**< Skip Alternate Rows from start row. */
+    bool multi_split;           /**< If false, each line in the import data defines exactly one transaction.
+                                     If true, a transaction can span multiple lines, with each line defining exactly one split.
+                                     In this case the first line should hold the transaction related details in
+                                     addition to the first split details. On each following line for the same
+                                     transaction the transaction related columns should be empty or have
+                                     the same value as the first line. */
     int currency_format;        /**< The currency format, 0 for locale, 1 for comma dec and 2 for period */
     bool parse_errors;          /**< Indicates whether the last parse_to_trans run had any errors */
 
@@ -133,12 +139,19 @@ private:
      */
     void adjust_balances (void);
 
+    /* Internal helper function that does the actual conversion from property lists
+     * to real (possibly unbalanced) transaction with splits.
+     */
+    std::shared_ptr<DraftTransaction> trans_properties_to_trans (parse_line_t& parsed_line);
+
     GncImpFileFormat file_fmt = GncImpFileFormat::UNKNOWN;
 
-    /* The variables below are only used during while creating
+    /* The variables below are only used while creating
      * transactions. They keep state information during the conversion.
      */
     Account *base_account = nullptr;
+    std::shared_ptr<GncPreTrans> parent = nullptr;
+    std::shared_ptr<DraftTransaction> current_draft = nullptr;
 };
 
 

commit 4f2980ef4239e2c7b23d02ff4c5215897c686e4c
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Fri Dec 2 12:45:30 2016 +0100

    Rework csv import preview with additional cleanups
    
    - The preview part should be less chaotic now. It already has an option for future multi-split
      import functionality, but that's not used yet
    - rename a few variables
    - change semantics on start/end lines; this is now communicated in number of lines to skip
    - avoid a couple of double value storing (once in the assistant object and onece in the import object)

diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index 8148923..62ce7f9 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -84,18 +84,15 @@ typedef struct
     GtkWidget       *preview_page;                  /**< Assistant preview page widget */
     GtkWidget       *settings_combo;                /**< The Settings Combo */
     GtkWidget       *combo_hbox;                    /**< The Settings Combo hbox */
-    GtkWidget       *check_label;                   /**< The widget for the check label */
     GtkWidget       *check_butt;                    /**< The widget for the check label button */
     GtkWidget       *start_row_spin;                /**< The widget for the start row spinner */
     GtkWidget       *end_row_spin;                  /**< The widget for the end row spinner */
     GtkWidget       *skip_rows;                     /**< The widget for Skip alternate rows from start row */
     GtkWidget       *csv_button;                    /**< The widget for the CSV button */
     GtkWidget       *fixed_button;                  /**< The widget for the Fixed Width button */
-    int              start_row;                     /**< The liststore start row, smallest is 0 */
-    int              end_row;                       /**< The liststore end row, max number of rows -1 */
     int              home_account_number;           /**< The number of unique home account strings */
 
-    GncTxImport *parse_data;                    /**< The actual data we are previewing */
+    GncTxImport     *parse_data;                    /**< The actual data we are previewing */
     CsvSettings     *settings_data;                 /**< The settings to be saved and loaded */
     GOCharmapSel    *encselector;                   /**< The widget for selecting the encoding */
     GtkCheckButton  *sep_buttons[SEP_NUM_OF_TYPES]; /**< Checkbuttons for common separators */
@@ -116,8 +113,6 @@ typedef struct
                                                        * 2. encoding_selected is called twice,
                                                        * each time decrementing this by 1. */
     bool             skip_errors;                   /**< This is false until the user checks the skip errors. */
-    int              num_of_rows;                   /**< The number of rows in the store */
-    int              longest_line;                  /**< The length of the longest row */
     int              fixed_context_col;             /**< The number of the column whose the user has clicked */
     int              fixed_context_dx;              /**< The horizontal coordinate of the pixel in the header of the column
                                                        * the user has clicked */
@@ -233,18 +228,21 @@ csv_import_trans_load_settings (CsvImportTrans *info)
     g_free (group);
 
     // Set start row
-    info->parse_data->start_row = info->settings_data->header_rows;
+    info->parse_data->skip_start_lines = info->settings_data->header_rows;
     GtkAdjustment *adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(info->start_row_spin));
-    gtk_spin_button_set_value (GTK_SPIN_BUTTON(info->start_row_spin), info->settings_data->header_rows);
+    gtk_adjustment_set_upper (adj, info->parse_data->parsed_lines.size());
+    gtk_spin_button_set_value (GTK_SPIN_BUTTON(info->start_row_spin),
+            info->settings_data->header_rows);
 
     // Set end row
-    info->parse_data->end_row = info->num_of_rows - info->settings_data->footer_rows;
+    info->parse_data->skip_end_lines = info->settings_data->footer_rows;
     adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(info->end_row_spin));
-    gtk_adjustment_set_upper (adj, info->num_of_rows);
-    gtk_spin_button_set_value (GTK_SPIN_BUTTON(info->end_row_spin), info->num_of_rows - info->settings_data->footer_rows);
+    gtk_adjustment_set_upper (adj, info->parse_data->parsed_lines.size());
+    gtk_spin_button_set_value (GTK_SPIN_BUTTON(info->end_row_spin),
+            info->settings_data->footer_rows);
 
     // Set Alternate rows
-    info->parse_data->skip_rows = info->settings_data->skip_alt_rows;
+    info->parse_data->skip_alt_lines = info->settings_data->skip_alt_rows;
     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->skip_rows), info->settings_data->skip_alt_rows);
 
     // Set Import Format
@@ -539,7 +537,7 @@ csv_import_trans_save_settings_cb (GtkWidget *button, CsvImportTrans *info)
 
         /* This section deals with the header and rows */
         info->settings_data->header_rows = gtk_spin_button_get_value (GTK_SPIN_BUTTON(info->start_row_spin));
-        info->settings_data->footer_rows = info->num_of_rows - gtk_spin_button_get_value (GTK_SPIN_BUTTON(info->end_row_spin));
+        info->settings_data->footer_rows = gtk_spin_button_get_value (GTK_SPIN_BUTTON(info->end_row_spin));
         info->settings_data->skip_alt_rows = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(info->skip_rows));
         info->settings_data->csv_format = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(info->csv_button));
 
@@ -718,6 +716,17 @@ csv_import_trans_file_chooser_confirm_cb (GtkWidget *button, CsvImportTrans *inf
     info->parse_data = parse_data;
     info->previewing_errors = false; /* We're looking at all the data. */
     info->skip_errors = false; // Set skip_errors to False
+
+    /* Reset a couple of widgets on the preview page */
+    // - leading and trailing lines to skip = 0
+    gtk_spin_button_set_value (GTK_SPIN_BUTTON(info->start_row_spin), 0);
+    gtk_spin_button_set_value (GTK_SPIN_BUTTON(info->end_row_spin), 0);
+
+    /* Get settings store and populate */
+    GtkTreeModel   *settings_store = gtk_combo_box_get_model (GTK_COMBO_BOX(info->settings_combo));
+    gnc_csv_trans_find_settings (settings_store);
+    gtk_combo_box_set_active (GTK_COMBO_BOX(info->settings_combo), 0);
+
     gtk_assistant_set_page_complete (assistant, page, TRUE);
     gtk_assistant_set_current_page (assistant, num + 1);
 }
@@ -731,73 +740,25 @@ csv_import_trans_file_chooser_confirm_cb (GtkWidget *button, CsvImportTrans *inf
 static
 void row_selection_update (CsvImportTrans* info)
 {
-    GtkListStore *store;
     GtkTreeIter iter;
-    bool valid;
-    int i = 0;
-
-    store = GTK_LIST_STORE(gtk_tree_view_get_model (info->treeview));
-
-    /* Start of file */
-    for (i = 0; i <= info->start_row; i++)
-    {
-        /* Modify background color of rows less than start row */
-        if (info->start_row == i)
-        {
-            valid = gtk_tree_model_iter_nth_child (GTK_TREE_MODEL(store), &iter, NULL, i);
-            if (valid)
-                gtk_list_store_set (store, &iter, 0, NULL, -1);
-        }
-        else
-        {
-            valid = gtk_tree_model_iter_nth_child (GTK_TREE_MODEL(store), &iter, NULL, i);
-            if (valid)
-                gtk_list_store_set (store, &iter, 0, "pink", -1);
-            valid = gtk_tree_model_iter_next (GTK_TREE_MODEL(store), &iter);
-            if (valid)
-                gtk_list_store_set (store, &iter, 0, NULL, -1);
-        }
-    }
-
-    /* End of File */
-    for (i = info->num_of_rows - 1; i >= info->end_row; i--)
-    {
-        /* Modify background color of rows more than end row */
-        if (i == info->end_row)
-        {
-            valid = gtk_tree_model_iter_nth_child (GTK_TREE_MODEL(store), &iter, NULL, i);
-            if (valid)
-                gtk_list_store_set (store, &iter, 0, NULL, -1);
-        }
+    auto store = GTK_LIST_STORE(gtk_tree_view_get_model (info->treeview));
+
+    /* Colorize rows that will be skipped */
+    for (uint i = 0; i < info->parse_data->parsed_lines.size(); i++)
+    {
+        const char *color = NULL;
+        if ((i < info->parse_data->skip_start_lines) ||             // start rows to skip
+            (i >= info->parse_data->parsed_lines.size()
+                    - info->parse_data->skip_end_lines) ||          // end rows to skip
+            (((i - info->parse_data->skip_start_lines) % 2 == 1) && // skip every second row...
+             info->parse_data->skip_alt_lines))                   // ...if requested
+            color = "pink";
         else
-        {
-            valid = gtk_tree_model_iter_nth_child (GTK_TREE_MODEL(store), &iter, NULL, i);
-            if (valid)
-                gtk_list_store_set (store, &iter, 0, "pink", -1);
-            valid = gtk_tree_model_iter_nth_child (GTK_TREE_MODEL(store), &iter, NULL, i - 1);
-            if (valid)
-                gtk_list_store_set (store, &iter, 0, NULL, -1);
-        }
-    }
+            color = NULL;                                           // all other rows
 
-    /* Remove background color from the start row to end row */
-    for (i = info->start_row + 1; i <= info->end_row; i++)
-    {
-        valid = gtk_tree_model_iter_nth_child (GTK_TREE_MODEL(store), &iter, NULL, i);
+        bool valid = gtk_tree_model_iter_nth_child (GTK_TREE_MODEL(store), &iter, NULL, i);
         if (valid)
-            gtk_list_store_set (store, &iter, 0, NULL, -1);
-    }
-
-    /* Skip rows */
-    if (info->parse_data->skip_rows)
-    {
-        for (i = info->start_row + 1; i <= info->end_row; i = i + 2)
-        {
-            /* Modify background color of alternate rows from the start row */
-            valid = gtk_tree_model_iter_nth_child (GTK_TREE_MODEL(store), &iter, NULL, i);
-            if (valid)
-                gtk_list_store_set (store, &iter, 0, "pink", -1);
-        }
+            gtk_list_store_set (store, &iter, 0, color, -1);
     }
 }
 
@@ -812,13 +773,13 @@ void csv_import_trans_srow_cb (GtkWidget *spin, gpointer user_data)
     CsvImportTrans *info = (CsvImportTrans*) user_data;
     GtkAdjustment *adj;
 
-    /* Get number of rows for header */
-    info->start_row = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON(spin)) - 1;
-
-    info->parse_data->start_row = info->start_row;
+    /* Get number of lines to skip at the beginning */
+    info->parse_data->skip_start_lines = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON(spin));
 
+    /* And adjust maximum number of lines that can be skipped at the end accordingly */
     adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(info->end_row_spin));
-    gtk_adjustment_set_lower (adj, info->start_row + 1);
+    gtk_adjustment_set_upper (adj, info->parse_data->parsed_lines.size()
+            - info->parse_data->skip_start_lines);
 
     /* Refresh the row highlighting */
     row_selection_update (info);
@@ -835,13 +796,13 @@ void csv_import_trans_erow_cb (GtkWidget *spin, gpointer user_data)
     CsvImportTrans *info = (CsvImportTrans*) user_data;
     GtkAdjustment *adj;
 
-    /* Get number of rows for header */
-    info->end_row = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON(spin)) - 1;
-
-    info->parse_data->end_row = info->end_row + 1;
+    /* Get number of lines to skip at the end */
+    info->parse_data->skip_end_lines = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON(spin));
 
+    /* And adjust maximum number of lines that can be skipped at the beginning accordingly */
     adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(info->start_row_spin));
-    gtk_adjustment_set_upper (adj, info->end_row + 1);
+    gtk_adjustment_set_upper (adj, info->parse_data->parsed_lines.size()
+            - info->parse_data->skip_end_lines);
 
     /* Refresh the row highlighting */
     row_selection_update (info);
@@ -873,8 +834,8 @@ void csv_import_trans_skiprows_cb (GtkWidget *checkbox, gpointer user_data)
 {
     CsvImportTrans *info = (CsvImportTrans*) user_data;
 
-    /* Set the skip_rows variable */
-    info->parse_data->skip_rows = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(checkbox));
+    /* Set the skip_alt_lines variable */
+    info->parse_data->skip_alt_lines = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(checkbox));
 
     /* Refresh the row highlighting */
     row_selection_update (info);
@@ -1470,7 +1431,7 @@ bool preview_settings_valid (CsvImportTrans* info)
     gtk_tree_model_get_iter_first (ctstore, &iter1);
 
     /* Get an iterator for the first required row in the data store. */
-    gtk_tree_model_iter_nth_child (GTK_TREE_MODEL(datastore), &iter2, NULL, info->start_row);
+    gtk_tree_model_iter_nth_child (GTK_TREE_MODEL(datastore), &iter2, NULL, info->parse_data->skip_start_lines);
 
     /* Go through each of the columns. */
     for (i = 0; i < ncols; i++)
@@ -1602,7 +1563,7 @@ check_for_duplicates (GtkListStore *liststore, const gchar *string)
  */
 bool get_list_of_accounts (CsvImportTrans* info, GtkTreeModel *store)
 {
-    int      i, j, ncols = info->parse_data->column_types.size(); /* ncols is the number of columns in the data. */
+    int      i, ncols = info->parse_data->column_types.size(); /* ncols is the number of columns in the data. */
     bool     have_accounts = false;
     gint     home_account_number = 0;
     gint     other_account_number = 0;
@@ -1617,7 +1578,9 @@ bool get_list_of_accounts (CsvImportTrans* info, GtkTreeModel *store)
     /* Get an iterator for the first (and only) row of the column store. */
     gtk_tree_model_get_iter_first (ctstore, &iter1);
 
-    for (j = info->start_row; j <= info->end_row; j++)
+    for (uint j = info->parse_data->skip_start_lines;
+            j < info->parse_data->parsed_lines.size() - info->parse_data->skip_end_lines - 1;
+            j++)
     {
         /* Go through each of the columns. */
         for (i = 0; i < ncols; i++)
@@ -1723,7 +1686,6 @@ static void gnc_csv_preview_update_assist (CsvImportTrans* info)
     g_free (bodytypes);
 
     /* Fill the data liststore with data from the file. */
-    info->num_of_rows = info->parse_data->parsed_lines.size();
     for (auto parse_line : info->parse_data->parsed_lines)
     {
         // When previewing errors skip all lines that don't have errors
@@ -1870,12 +1832,13 @@ void gnc_csv_reset_preview_setting (CsvImportTrans *info, bool block)
 
     // Reset Start Row
     adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(info->start_row_spin));
-    gtk_spin_button_set_value (GTK_SPIN_BUTTON(info->start_row_spin), 1);
+    gtk_spin_button_set_value (GTK_SPIN_BUTTON(info->start_row_spin), 0);
+    gtk_adjustment_set_upper (adj, info->parse_data->parsed_lines.size());
 
     // Reset End Row
     adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(info->end_row_spin));
-    gtk_adjustment_set_upper (adj, info->num_of_rows);
-    gtk_spin_button_set_value (GTK_SPIN_BUTTON(info->end_row_spin), info->num_of_rows);
+    gtk_spin_button_set_value (GTK_SPIN_BUTTON(info->end_row_spin), 0);
+    gtk_adjustment_set_upper (adj, info->parse_data->parsed_lines.size());
 
     // Reset Skip Rows
     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->skip_rows), FALSE);
@@ -1927,7 +1890,6 @@ void gnc_csv_reset_preview_setting (CsvImportTrans *info, bool block)
 static
 void load_settings (CsvImportTrans *info)
 {
-    info->start_row = 0;
     info->match_parse_run = false;
     if (!info->file_name.empty())
         info->file_name.clear();
@@ -1971,8 +1933,6 @@ csv_import_trans_assistant_file_page_prepare (GtkAssistant *assistant,
         gpointer user_data)
 {
     CsvImportTrans *info = (CsvImportTrans*) user_data;
-    GtkAdjustment  *adj;
-    GtkTreeModel   *settings_store;
     gint            num = gtk_assistant_get_current_page (assistant);
     GtkWidget      *page = gtk_assistant_get_nth_page (assistant, num);
 
@@ -1983,19 +1943,6 @@ csv_import_trans_assistant_file_page_prepare (GtkAssistant *assistant,
     if (info->starting_dir.size())
         gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER(info->file_chooser), info->starting_dir.c_str());
 
-    /* Reset start row to first row 1 */
-    adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(info->start_row_spin));
-    gtk_spin_button_set_value (GTK_SPIN_BUTTON(info->start_row_spin), 1);
-
-    /* Reset upper value to 999 */
-    adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(info->end_row_spin));
-    gtk_adjustment_set_upper (adj, 999);
-
-    /* Get settings store and populate */
-    settings_store = gtk_combo_box_get_model (GTK_COMBO_BOX(info->settings_combo));
-    gnc_csv_trans_find_settings (settings_store);
-    gtk_combo_box_set_active (GTK_COMBO_BOX(info->settings_combo), 0);
-
     /* Disable the Forward Assistant Button */
     gtk_assistant_set_page_complete (assistant, page, FALSE);
 }
@@ -2006,13 +1953,11 @@ csv_import_trans_assistant_preview_page_prepare (GtkAssistant *assistant,
         gpointer user_data)
 {
     CsvImportTrans *info = (CsvImportTrans*) user_data;
-    GtkAdjustment *adj;
 
     g_signal_connect (G_OBJECT(info->treeview), "size-allocate",
                      G_CALLBACK(treeview_resized), (gpointer)info);
 
-    // Hide the check button label and toggle
-    gtk_widget_hide (GTK_WIDGET(info->check_label));
+    // Hide the skip errors check button
     gtk_widget_hide (GTK_WIDGET(info->check_butt));
 
     if (info->previewing_errors) // We are looking at errors to display
@@ -2030,19 +1975,18 @@ csv_import_trans_assistant_preview_page_prepare (GtkAssistant *assistant,
         gtk_widget_show (GTK_WIDGET(info->instructions_image));
         gtk_widget_show (GTK_WIDGET(info->instructions_label));
 
-        /* Reset start row */
-        adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(info->start_row_spin));
-        gtk_spin_button_set_value (GTK_SPIN_BUTTON(info->start_row_spin), 1);
+        /* Reset start and end row */
+        gtk_spin_button_set_value (GTK_SPIN_BUTTON(info->start_row_spin), 0);
+        gtk_spin_button_set_value (GTK_SPIN_BUTTON(info->end_row_spin), 0);
 
         /* Set spin buttons and settings combo hbox not sensitive */
         gtk_widget_set_sensitive (info->combo_hbox, FALSE);
         gtk_widget_set_sensitive (info->start_row_spin, FALSE);
         gtk_widget_set_sensitive (info->end_row_spin, FALSE);
         gtk_widget_set_sensitive (info->skip_rows, FALSE);
-        info->parse_data->skip_rows = FALSE;
+        info->parse_data->skip_alt_lines = FALSE;
 
-        /* Show the check button label and toggle */
-        gtk_widget_show (GTK_WIDGET(info->check_label));
+        /* Show the skip errors check button */
         gtk_widget_show (GTK_WIDGET(info->check_butt));
         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->check_butt), FALSE);
     }
@@ -2060,12 +2004,12 @@ csv_import_trans_assistant_preview_page_prepare (GtkAssistant *assistant,
     gnc_csv_preview_update_assist (info);
 
     /* Set the upper limit of spin button to number of rows */
+    GtkAdjustment *adj;
     adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(info->end_row_spin));
-    if (gtk_adjustment_get_upper (adj) != info->num_of_rows)
-    {
-        gtk_adjustment_set_upper (adj, info->num_of_rows);
-        gtk_spin_button_set_value (GTK_SPIN_BUTTON(info->end_row_spin), info->num_of_rows);
-    }
+    gtk_adjustment_set_upper (adj, info->parse_data->parsed_lines.size());
+    adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(info->start_row_spin));
+    gtk_adjustment_set_upper (adj, info->parse_data->parsed_lines.size());
+    if (gtk_adjustment_get_upper (adj) != info->parse_data->parsed_lines.size())
 
     /* Update the row selection highlight */
     row_selection_update (info);
@@ -2816,9 +2760,8 @@ csv_import_trans_assistant_create (CsvImportTrans *info)
                                           "semicolon_cbutton",
                                           "hyphen_cbutton"
         };
-        GtkContainer *date_format_container, *currency_format_container;
+        GtkContainer *date_format_container, *currency_format_container, *encoding_container;
         int           i;
-        GtkTable     *enctable;
         GtkListStore *settings_store;
 
         info->preview_page = GTK_WIDGET(gtk_builder_get_object (builder, "preview_page"));
@@ -2852,18 +2795,12 @@ csv_import_trans_assistant_create (CsvImportTrans *info)
         g_signal_connect (G_OBJECT(del_button), "clicked",
                          G_CALLBACK(csv_import_trans_delete_settings_cb), (gpointer)info);
 
-        /* The table containing info->encselector and the separator configuration widgets */
+        /* The table containing the separator configuration widgets */
         info->start_row_spin = GTK_WIDGET(gtk_builder_get_object (builder, "start_row"));
         info->end_row_spin = GTK_WIDGET(gtk_builder_get_object (builder, "end_row"));
         info->skip_rows = GTK_WIDGET(gtk_builder_get_object (builder, "skip_rows"));
-        info->check_label = GTK_WIDGET(gtk_builder_get_object (builder, "check_label"));
         info->check_butt = GTK_WIDGET(gtk_builder_get_object (builder, "check_butt"));
 
-        info->encselector = GO_CHARMAP_SEL(go_charmap_sel_new(GO_CHARMAP_SEL_TO_UTF8));
-        /* Connect the selector to the encoding_selected event handler. */
-        g_signal_connect (G_OBJECT(info->encselector), "charmap_changed",
-                         G_CALLBACK(encoding_selected), (gpointer)info);
-
         /* Load the separator buttons from the glade builder file into the
          * info->sep_buttons array. */
         for (i = 0; i < SEP_NUM_OF_TYPES; i++)
@@ -2888,12 +2825,16 @@ csv_import_trans_assistant_create (CsvImportTrans *info)
         g_signal_connect (G_OBJECT(info->custom_entry), "changed",
                          G_CALLBACK(sep_button_clicked), (gpointer)info);
 
-        /* Get the table from the Glade builder file. */
-        enctable = GTK_TABLE(gtk_builder_get_object (builder, "enctable"));
-        /* Put the selector in at the top. */
-        gtk_table_attach_defaults (enctable, GTK_WIDGET(info->encselector), 1, 2, 0, 1);
-        /* Show the table in all its glory. */
-        gtk_widget_show_all (GTK_WIDGET(enctable));
+
+        /* Create the encoding selector widget and add it to the assistant */
+        info->encselector = GO_CHARMAP_SEL(go_charmap_sel_new(GO_CHARMAP_SEL_TO_UTF8));
+        /* Connect the selector to the encoding_selected event handler. */
+        g_signal_connect (G_OBJECT(info->encselector), "charmap_changed",
+                         G_CALLBACK(encoding_selected), (gpointer)info);
+
+        encoding_container = GTK_CONTAINER(gtk_builder_get_object (builder, "encoding_container"));
+        gtk_container_add (encoding_container, GTK_WIDGET(info->encselector));
+        gtk_widget_show_all (GTK_WIDGET(encoding_container));
 
         /* The instructions label and image */
         info->instructions_label = GTK_LABEL(gtk_builder_get_object (builder, "instructions_label"));
diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.glade b/src/import-export/csv-imp/assistant-csv-trans-import.glade
index c865c84..887a4cb 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.glade
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.glade
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
-  <requires lib="gtk+" version="2.16"/>
+  <requires lib="gtk+" version="2.24"/>
   <!-- interface-naming-policy project-wide -->
   <object class="GtkListStore" id="account_match_store">
     <columns>
@@ -13,16 +13,12 @@
     </columns>
   </object>
   <object class="GtkAdjustment" id="end_row_adj">
-    <property name="lower">1</property>
-    <property name="upper">100</property>
-    <property name="value">100</property>
+    <property name="upper">1000</property>
     <property name="step_increment">1</property>
     <property name="page_increment">10</property>
   </object>
   <object class="GtkAdjustment" id="start_row_adj">
-    <property name="lower">1</property>
-    <property name="upper">100</property>
-    <property name="value">1</property>
+    <property name="upper">1000</property>
     <property name="step_increment">1</property>
     <property name="page_increment">10</property>
   </object>
@@ -37,6 +33,9 @@
     <signal name="prepare" handler="csv_import_trans_assistant_prepare" swapped="no"/>
     <signal name="cancel" handler="csv_import_trans_assistant_cancel" swapped="no"/>
     <child>
+      <placeholder/>
+    </child>
+    <child>
       <object class="GtkLabel" id="start_page">
         <property name="visible">True</property>
         <property name="can_focus">False</property>
@@ -94,580 +93,590 @@ Select location and file name for the Import, then click 'OK'...
         <property name="border_width">12</property>
         <property name="spacing">2</property>
         <child>
-          <object class="GtkFrame" id="frame5">
+          <object class="GtkHBox" id="hbox1">
             <property name="visible">True</property>
             <property name="can_focus">False</property>
-            <property name="label_xalign">0</property>
-            <property name="shadow_type">in</property>
             <child>
-              <object class="GtkAlignment" id="alignment3">
+              <object class="GtkVBox" id="vbox1">
                 <property name="visible">True</property>
                 <property name="can_focus">False</property>
-                <property name="bottom_padding">5</property>
-                <property name="left_padding">12</property>
+                <property name="spacing">5</property>
                 <child>
-                  <object class="GtkHBox" id="combo_hbox">
+                  <object class="GtkFrame" id="frame6">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
+                    <property name="label_xalign">0</property>
+                    <property name="shadow_type">in</property>
+                    <child>
+                      <object class="GtkAlignment" id="alignment4">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="bottom_padding">5</property>
+                        <property name="left_padding">5</property>
+                        <property name="right_padding">5</property>
+                        <child>
+                          <object class="GtkHBox" id="combo_hbox">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <child>
+                              <placeholder/>
+                            </child>
+                            <child>
+                              <placeholder/>
+                            </child>
+                            <child>
+                              <placeholder/>
+                            </child>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                    <child type="label">
+                      <object class="GtkLabel" id="label12">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="label" translatable="yes"> <b>Load and Save Settings</b></property>
+                        <property name="use_markup">True</property>
+                        <property name="track_visited_links">False</property>
+                      </object>
+                    </child>
                   </object>
+                  <packing>
+                    <property name="expand">True</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
                 </child>
-              </object>
-            </child>
-            <child type="label">
-              <object class="GtkLabel" id="label11">
-                <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <property name="label" translatable="yes"> Load and Save Settings </property>
-                <property name="use_markup">True</property>
-              </object>
-            </child>
-          </object>
-          <packing>
-            <property name="expand">False</property>
-            <property name="fill">False</property>
-            <property name="position">0</property>
-          </packing>
-        </child>
-        <child>
-          <object class="GtkFrame" id="frame4">
-            <property name="visible">True</property>
-            <property name="can_focus">False</property>
-            <property name="label_xalign">0</property>
-            <property name="shadow_type">in</property>
-            <child>
-              <object class="GtkAlignment" id="alignment2">
-                <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <property name="left_padding">12</property>
                 <child>
-                  <object class="GtkVBox" id="vbox4">
+                  <object class="GtkHBox" id="hbox8">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
+                    <property name="spacing">5</property>
                     <child>
-                      <object class="GtkHBox" id="hbox1">
+                      <object class="GtkFrame" id="frame10">
                         <property name="visible">True</property>
                         <property name="can_focus">False</property>
-                        <property name="spacing">12</property>
+                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                        <property name="label_xalign">0</property>
+                        <property name="shadow_type">none</property>
                         <child>
-                          <object class="GtkVBox" id="vbox1">
+                          <object class="GtkAlignment" id="alignment6">
                             <property name="visible">True</property>
                             <property name="can_focus">False</property>
                             <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                            <property name="left_padding">5</property>
+                            <property name="right_padding">5</property>
                             <child>
-                              <object class="GtkHBox" id="hbox6">
+                              <object class="GtkVBox" id="vbox9">
                                 <property name="visible">True</property>
                                 <property name="can_focus">False</property>
                                 <child>
-                                  <object class="GtkLabel" id="label9">
+                                  <object class="GtkRadioButton" id="fixed_button">
+                                    <property name="label" translatable="yes">Fixed-Width</property>
                                     <property name="visible">True</property>
-                                    <property name="can_focus">False</property>
-                                    <property name="label" translatable="yes">Start import on row </property>
+                                    <property name="can_focus">True</property>
+                                    <property name="receives_default">False</property>
+                                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                                    <property name="draw_indicator">True</property>
+                                    <property name="group">csv_button</property>
                                   </object>
                                   <packing>
                                     <property name="expand">False</property>
-                                    <property name="fill">False</property>
+                                    <property name="fill">True</property>
                                     <property name="position">0</property>
                                   </packing>
                                 </child>
                                 <child>
-                                  <object class="GtkSpinButton" id="start_row">
+                                  <object class="GtkRadioButton" id="csv_button">
+                                    <property name="label" translatable="yes">Separators</property>
                                     <property name="visible">True</property>
                                     <property name="can_focus">True</property>
-                                    <property name="invisible_char">●</property>
-                                    <property name="invisible_char_set">True</property>
-                                    <property name="primary_icon_activatable">False</property>
-                                    <property name="secondary_icon_activatable">False</property>
-                                    <property name="primary_icon_sensitive">True</property>
-                                    <property name="secondary_icon_sensitive">True</property>
-                                    <property name="adjustment">start_row_adj</property>
-                                    <property name="numeric">True</property>
-                                    <signal name="value-changed" handler="csv_import_trans_srow_cb" swapped="no"/>
+                                    <property name="receives_default">False</property>
+                                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                                    <property name="active">True</property>
+                                    <property name="draw_indicator">True</property>
                                   </object>
                                   <packing>
                                     <property name="expand">False</property>
-                                    <property name="fill">False</property>
+                                    <property name="fill">True</property>
                                     <property name="position">1</property>
                                   </packing>
                                 </child>
                                 <child>
-                                  <object class="GtkLabel" id="label10">
-                                    <property name="visible">True</property>
-                                    <property name="can_focus">False</property>
-                                    <property name="label" translatable="yes"> and stop on row </property>
-                                  </object>
-                                  <packing>
-                                    <property name="expand">False</property>
-                                    <property name="fill">False</property>
-                                    <property name="position">2</property>
-                                  </packing>
-                                </child>
-                                <child>
-                                  <object class="GtkSpinButton" id="end_row">
-                                    <property name="visible">True</property>
-                                    <property name="can_focus">True</property>
-                                    <property name="invisible_char">●</property>
-                                    <property name="invisible_char_set">True</property>
-                                    <property name="primary_icon_activatable">False</property>
-                                    <property name="secondary_icon_activatable">False</property>
-                                    <property name="primary_icon_sensitive">True</property>
-                                    <property name="secondary_icon_sensitive">True</property>
-                                    <property name="adjustment">end_row_adj</property>
-                                    <property name="numeric">True</property>
-                                    <signal name="value-changed" handler="csv_import_trans_erow_cb" swapped="no"/>
-                                  </object>
-                                  <packing>
-                                    <property name="expand">False</property>
-                                    <property name="fill">False</property>
-                                    <property name="position">3</property>
-                                  </packing>
-                                </child>
-                              </object>
-                              <packing>
-                                <property name="expand">False</property>
-                                <property name="fill">False</property>
-                                <property name="position">0</property>
-                              </packing>
-                            </child>
-                            <child>
-                              <object class="GtkCheckButton" id="skip_rows">
-                                <property name="label" translatable="yes">Skip alternate rows from the start row</property>
-                                <property name="visible">True</property>
-                                <property name="can_focus">True</property>
-                                <property name="receives_default">False</property>
-                                <property name="draw_indicator">True</property>
-                                <signal name="toggled" handler="csv_import_trans_skiprows_cb" swapped="no"/>
-                              </object>
-                              <packing>
-                                <property name="expand">True</property>
-                                <property name="fill">True</property>
-                                <property name="position">1</property>
-                              </packing>
-                            </child>
-                            <child>
-                              <object class="GtkHSeparator" id="hseparator2">
-                                <property name="visible">True</property>
-                                <property name="can_focus">False</property>
-                              </object>
-                              <packing>
-                                <property name="expand">False</property>
-                                <property name="fill">False</property>
-                                <property name="padding">5</property>
-                                <property name="position">2</property>
-                              </packing>
-                            </child>
-                            <child>
-                              <object class="GtkHBox" id="hbox4">
-                                <property name="visible">True</property>
-                                <property name="can_focus">False</property>
-                                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                                <child>
-                                  <object class="GtkLabel" id="label1">
+                                  <object class="GtkTable" id="table2">
                                     <property name="visible">True</property>
                                     <property name="can_focus">False</property>
                                     <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                                    <property name="label" translatable="yes">Data type: </property>
+                                    <property name="n_rows">3</property>
+                                    <property name="n_columns">3</property>
+                                    <property name="column_spacing">3</property>
+                                    <property name="row_spacing">3</property>
+                                    <property name="homogeneous">True</property>
+                                    <child>
+                                      <object class="GtkCheckButton" id="space_cbutton">
+                                        <property name="label" translatable="yes">Space</property>
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">True</property>
+                                        <property name="receives_default">False</property>
+                                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                                        <property name="draw_indicator">True</property>
+                                      </object>
+                                      <packing>
+                                        <property name="x_options">GTK_FILL</property>
+                                        <property name="y_options">GTK_FILL</property>
+                                      </packing>
+                                    </child>
+                                    <child>
+                                      <object class="GtkCheckButton" id="tab_cbutton">
+                                        <property name="label" translatable="yes">Tab</property>
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">True</property>
+                                        <property name="receives_default">False</property>
+                                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                                        <property name="draw_indicator">True</property>
+                                      </object>
+                                      <packing>
+                                        <property name="left_attach">1</property>
+                                        <property name="right_attach">2</property>
+                                        <property name="x_options">GTK_FILL</property>
+                                        <property name="y_options">GTK_FILL</property>
+                                      </packing>
+                                    </child>
+                                    <child>
+                                      <object class="GtkCheckButton" id="comma_cbutton">
+                                        <property name="label" translatable="yes">Comma (,)</property>
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">True</property>
+                                        <property name="receives_default">False</property>
+                                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                                        <property name="active">True</property>
+                                        <property name="draw_indicator">True</property>
+                                      </object>
+                                      <packing>
+                                        <property name="left_attach">2</property>
+                                        <property name="right_attach">3</property>
+                                        <property name="x_options">GTK_FILL</property>
+                                        <property name="y_options">GTK_FILL</property>
+                                      </packing>
+                                    </child>
+                                    <child>
+                                      <object class="GtkCheckButton" id="colon_cbutton">
+                                        <property name="label" translatable="yes">Colon (:)</property>
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">True</property>
+                                        <property name="receives_default">False</property>
+                                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                                        <property name="draw_indicator">True</property>
+                                      </object>
+                                      <packing>
+                                        <property name="top_attach">1</property>
+                                        <property name="bottom_attach">2</property>
+                                        <property name="x_options">GTK_FILL</property>
+                                        <property name="y_options">GTK_FILL</property>
+                                      </packing>
+                                    </child>
+                                    <child>
+                                      <object class="GtkCheckButton" id="semicolon_cbutton">
+                                        <property name="label" translatable="yes">Semicolon (;)</property>
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">True</property>
+                                        <property name="receives_default">False</property>
+                                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                                        <property name="draw_indicator">True</property>
+                                      </object>
+                                      <packing>
+                                        <property name="left_attach">1</property>
+                                        <property name="right_attach">2</property>
+                                        <property name="top_attach">1</property>
+                                        <property name="bottom_attach">2</property>
+                                        <property name="x_options">GTK_FILL</property>
+                                        <property name="y_options">GTK_FILL</property>
+                                      </packing>
+                                    </child>
+                                    <child>
+                                      <object class="GtkCheckButton" id="hyphen_cbutton">
+                                        <property name="label" translatable="yes">Hyphen (-)</property>
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">True</property>
+                                        <property name="receives_default">False</property>
+                                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                                        <property name="draw_indicator">True</property>
+                                      </object>
+                                      <packing>
+                                        <property name="left_attach">2</property>
+                                        <property name="right_attach">3</property>
+                                        <property name="top_attach">1</property>
+                                        <property name="bottom_attach">2</property>
+                                        <property name="x_options">GTK_FILL</property>
+                                        <property name="y_options">GTK_FILL</property>
+                                      </packing>
+                                    </child>
+                                    <child>
+                                      <object class="GtkCheckButton" id="custom_cbutton">
+                                        <property name="label" translatable="yes">Custom</property>
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">True</property>
+                                        <property name="receives_default">False</property>
+                                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                                        <property name="draw_indicator">True</property>
+                                      </object>
+                                      <packing>
+                                        <property name="top_attach">2</property>
+                                        <property name="bottom_attach">3</property>
+                                        <property name="x_options">GTK_FILL</property>
+                                        <property name="y_options">GTK_FILL</property>
+                                      </packing>
+                                    </child>
+                                    <child>
+                                      <object class="GtkEntry" id="custom_entry">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">True</property>
+                                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                                        <property name="invisible_char">●</property>
+                                        <property name="invisible_char_set">True</property>
+                                        <property name="primary_icon_activatable">False</property>
+                                        <property name="secondary_icon_activatable">False</property>
+                                        <property name="primary_icon_sensitive">True</property>
+                                        <property name="secondary_icon_sensitive">True</property>
+                                      </object>
+                                      <packing>
+                                        <property name="left_attach">1</property>
+                                        <property name="right_attach">3</property>
+                                        <property name="top_attach">2</property>
+                                        <property name="bottom_attach">3</property>
+                                        <property name="x_options">GTK_FILL</property>
+                                        <property name="y_options">GTK_FILL</property>
+                                      </packing>
+                                    </child>
                                   </object>
                                   <packing>
                                     <property name="expand">False</property>
                                     <property name="fill">True</property>
-                                    <property name="position">0</property>
+                                    <property name="position">2</property>
                                   </packing>
                                 </child>
                                 <child>
-                                  <object class="GtkRadioButton" id="csv_button">
-                                    <property name="label" translatable="yes">Separated</property>
+                                  <object class="GtkHSeparator" id="hseparator4">
                                     <property name="visible">True</property>
-                                    <property name="can_focus">True</property>
-                                    <property name="receives_default">False</property>
-                                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                                    <property name="active">True</property>
-                                    <property name="draw_indicator">True</property>
+                                    <property name="can_focus">False</property>
                                   </object>
                                   <packing>
                                     <property name="expand">False</property>
                                     <property name="fill">True</property>
-                                    <property name="position">1</property>
+                                    <property name="position">3</property>
                                   </packing>
                                 </child>
                                 <child>
-                                  <object class="GtkRadioButton" id="fixed_button">
-                                    <property name="label" translatable="yes">Fixed-Width</property>
+                                  <object class="GtkCheckButton" id="multi_split_button">
+                                    <property name="label" translatable="yes">Multi-split</property>
                                     <property name="visible">True</property>
                                     <property name="can_focus">True</property>
                                     <property name="receives_default">False</property>
-                                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                                    <property name="tooltip_text" translatable="yes">Normally the importer will assume each line in the input file will correspond to one transaction. Each line can have information for one transaction and one or two splits.
+
+When Multi-split is enabled the importer will assume multiple consecutive lines together hold the information for one transaction. Each line provides information for exactly one split. The first line should also provide the information for the transaction.
+To know which lines belong to the same transaction, the importer will compare the provided transaction information in each line. If that information is empty or the same as the first transaction line the importer will consider this line part of the same transaction.</property>
                                     <property name="draw_indicator">True</property>
-                                    <property name="group">csv_button</property>
                                   </object>
                                   <packing>
-                                    <property name="expand">False</property>
+                                    <property name="expand">True</property>
                                     <property name="fill">True</property>
-                                    <property name="position">2</property>
+                                    <property name="position">4</property>
                                   </packing>
                                 </child>
                               </object>
-                              <packing>
-                                <property name="expand">False</property>
-                                <property name="fill">True</property>
-                                <property name="position">3</property>
-                              </packing>
                             </child>
+                          </object>
+                        </child>
+                        <child type="label">
+                          <object class="GtkLabel" id="label19">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                            <property name="label" translatable="yes"><b>File Format</b></property>
+                            <property name="use_markup">True</property>
+                          </object>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="expand">True</property>
+                        <property name="fill">True</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkFrame" id="frame8">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="label_xalign">0</property>
+                        <property name="shadow_type">none</property>
+                        <child>
+                          <object class="GtkAlignment" id="alignment8">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="left_padding">5</property>
+                            <property name="right_padding">5</property>
                             <child>
-                              <object class="GtkHSeparator" id="hseparator6">
+                              <object class="GtkVBox" id="vbox6">
                                 <property name="visible">True</property>
                                 <property name="can_focus">False</property>
                                 <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                              </object>
-                              <packing>
-                                <property name="expand">False</property>
-                                <property name="fill">False</property>
-                                <property name="padding">5</property>
-                                <property name="position">4</property>
-                              </packing>
-                            </child>
-                            <child>
-                              <object class="GtkHBox" id="hbox7">
-                                <property name="visible">True</property>
-                                <property name="can_focus">False</property>
-                                <property name="spacing">12</property>
                                 <child>
-                                  <object class="GtkFrame" id="frame2">
+                                  <object class="GtkTable" id="table3">
                                     <property name="visible">True</property>
                                     <property name="can_focus">False</property>
-                                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                                    <property name="label_xalign">0</property>
-                                    <property name="shadow_type">none</property>
+                                    <property name="n_rows">5</property>
+                                    <property name="n_columns">2</property>
+                                    <property name="column_spacing">5</property>
+                                    <property name="row_spacing">5</property>
                                     <child>
                                       <object class="GtkAlignment" id="date_format_container">
                                         <property name="visible">True</property>
                                         <property name="can_focus">False</property>
                                         <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                                        <property name="bottom_padding">5</property>
+                                        <property name="xalign">0</property>
                                       </object>
+                                      <packing>
+                                        <property name="left_attach">1</property>
+                                        <property name="right_attach">2</property>
+                                        <property name="top_attach">1</property>
+                                        <property name="bottom_attach">2</property>
+                                        <property name="x_options">GTK_FILL</property>
+                                      </packing>
                                     </child>
-                                    <child type="label">
-                                      <object class="GtkLabel" id="label4">
+                                    <child>
+                                      <object class="GtkLabel" id="label20">
                                         <property name="visible">True</property>
                                         <property name="can_focus">False</property>
-                                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                                        <property name="xalign">0</property>
                                         <property name="label" translatable="yes">Date Format</property>
-                                        <property name="use_markup">True</property>
                                       </object>
+                                      <packing>
+                                        <property name="top_attach">1</property>
+                                        <property name="bottom_attach">2</property>
+                                        <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+                                      </packing>
                                     </child>
-                                  </object>
-                                  <packing>
-                                    <property name="expand">False</property>
-                                    <property name="fill">False</property>
-                                    <property name="position">0</property>
-                                  </packing>
-                                </child>
-                                <child>
-                                  <object class="GtkFrame" id="frame3">
-                                    <property name="visible">True</property>
-                                    <property name="can_focus">False</property>
-                                    <property name="label_xalign">0</property>
-                                    <property name="shadow_type">none</property>
                                     <child>
                                       <object class="GtkAlignment" id="currency_format_container">
                                         <property name="visible">True</property>
                                         <property name="can_focus">False</property>
-                                        <property name="bottom_padding">5</property>
+                                        <property name="xalign">0</property>
                                       </object>
+                                      <packing>
+                                        <property name="left_attach">1</property>
+                                        <property name="right_attach">2</property>
+                                        <property name="top_attach">2</property>
+                                        <property name="bottom_attach">3</property>
+                                        <property name="x_options">GTK_FILL</property>
+                                      </packing>
                                     </child>
-                                    <child type="label">
-                                      <object class="GtkLabel" id="label2">
+                                    <child>
+                                      <object class="GtkLabel" id="label21">
                                         <property name="visible">True</property>
                                         <property name="can_focus">False</property>
-                                        <property name="label" translatable="yes">Currency format</property>
-                                        <property name="use_markup">True</property>
+                                        <property name="xalign">0</property>
+                                        <property name="label" translatable="yes">Currency Format</property>
                                       </object>
+                                      <packing>
+                                        <property name="top_attach">2</property>
+                                        <property name="bottom_attach">3</property>
+                                        <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+                                      </packing>
                                     </child>
-                                  </object>
-                                  <packing>
-                                    <property name="expand">True</property>
-                                    <property name="fill">True</property>
-                                    <property name="position">1</property>
-                                  </packing>
-                                </child>
-                              </object>
-                              <packing>
-                                <property name="expand">False</property>
-                                <property name="fill">False</property>
-                                <property name="position">5</property>
-                              </packing>
-                            </child>
-                          </object>
-                          <packing>
-                            <property name="expand">False</property>
-                            <property name="fill">False</property>
-                            <property name="position">0</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <object class="GtkVBox" id="vbox3">
-                            <property name="visible">True</property>
-                            <property name="can_focus">False</property>
-                            <child>
-                              <object class="GtkHBox" id="hbox2">
-                                <property name="visible">True</property>
-                                <property name="can_focus">False</property>
-                                <child>
-                                  <object class="GtkTable" id="enctable">
-                                    <property name="visible">True</property>
-                                    <property name="can_focus">False</property>
-                                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
                                     <child>
-                                      <object class="GtkLabel" id="label3">
+                                      <object class="GtkLabel" id="label16">
                                         <property name="visible">True</property>
                                         <property name="can_focus">False</property>
-                                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                                        <property name="label" translatable="yes">Encoding: </property>
+                                        <property name="xalign">0</property>
+                                        <property name="label" translatable="yes">Encoding</property>
                                       </object>
                                       <packing>
+                                        <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+                                      </packing>
+                                    </child>
+                                    <child>
+                                      <object class="GtkAlignment" id="encoding_container">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">False</property>
+                                        <property name="xalign">0</property>
+                                      </object>
+                                      <packing>
+                                        <property name="left_attach">1</property>
+                                        <property name="right_attach">2</property>
                                         <property name="x_options">GTK_FILL</property>
-                                        <property name="y_options">GTK_FILL</property>
                                       </packing>
                                     </child>
-                                  </object>
-                                  <packing>
-                                    <property name="expand">True</property>
-                                    <property name="fill">True</property>
-                                    <property name="position">0</property>
-                                  </packing>
-                                </child>
-                              </object>
-                              <packing>
-                                <property name="expand">False</property>
-                                <property name="fill">False</property>
-                                <property name="position">0</property>
-                              </packing>
-                            </child>
-                            <child>
-                              <object class="GtkHSeparator" id="hseparator1">
-                                <property name="visible">True</property>
-                                <property name="can_focus">False</property>
-                              </object>
-                              <packing>
-                                <property name="expand">False</property>
-                                <property name="fill">True</property>
-                                <property name="padding">5</property>
-                                <property name="position">1</property>
-                              </packing>
-                            </child>
-                            <child>
-                              <object class="GtkFrame" id="frame1">
-                                <property name="visible">True</property>
-                                <property name="can_focus">False</property>
-                                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                                <property name="label_xalign">0</property>
-                                <property name="shadow_type">none</property>
-                                <child>
-                                  <object class="GtkAlignment" id="alignment1">
-                                    <property name="visible">True</property>
-                                    <property name="can_focus">False</property>
-                                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                                    <property name="bottom_padding">5</property>
-                                    <property name="left_padding">12</property>
                                     <child>
-                                      <object class="GtkTable" id="table1">
+                                      <object class="GtkLabel" id="label17">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">False</property>
+                                        <property name="xalign">0</property>
+                                        <property name="label" translatable="yes">Leading Lines to Skip</property>
+                                      </object>
+                                      <packing>
+                                        <property name="top_attach">3</property>
+                                        <property name="bottom_attach">4</property>
+                                        <property name="x_options">GTK_FILL</property>
+                                      </packing>
+                                    </child>
+                                    <child>
+                                      <object class="GtkLabel" id="label18">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">False</property>
+                                        <property name="xalign">0</property>
+                                        <property name="label" translatable="yes">Trailing Lines to Skip</property>
+                                      </object>
+                                      <packing>
+                                        <property name="top_attach">4</property>
+                                        <property name="bottom_attach">5</property>
+                                        <property name="x_options">GTK_FILL</property>
+                                      </packing>
+                                    </child>
+                                    <child>
+                                      <object class="GtkHBox" id="hbox2">
                                         <property name="visible">True</property>
                                         <property name="can_focus">False</property>
-                                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                                        <property name="n_rows">3</property>
-                                        <property name="n_columns">3</property>
-                                        <property name="column_spacing">3</property>
-                                        <property name="row_spacing">3</property>
-                                        <property name="homogeneous">True</property>
-                                        <child>
-                                          <object class="GtkCheckButton" id="space_cbutton">
-                                            <property name="label" translatable="yes">Space</property>
-                                            <property name="visible">True</property>
-                                            <property name="can_focus">True</property>
-                                            <property name="receives_default">False</property>
-                                            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                                            <property name="draw_indicator">True</property>
-                                          </object>
-                                          <packing>
-                                            <property name="x_options">GTK_FILL</property>
-                                            <property name="y_options">GTK_FILL</property>
-                                          </packing>
-                                        </child>
-                                        <child>
-                                          <object class="GtkCheckButton" id="tab_cbutton">
-                                            <property name="label" translatable="yes">Tab</property>
-                                            <property name="visible">True</property>
-                                            <property name="can_focus">True</property>
-                                            <property name="receives_default">False</property>
-                                            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                                            <property name="draw_indicator">True</property>
-                                          </object>
-                                          <packing>
-                                            <property name="left_attach">1</property>
-                                            <property name="right_attach">2</property>
-                                            <property name="x_options">GTK_FILL</property>
-                                            <property name="y_options">GTK_FILL</property>
-                                          </packing>
-                                        </child>
-                                        <child>
-                                          <object class="GtkCheckButton" id="comma_cbutton">
-                                            <property name="label" translatable="yes">Comma (,)</property>
-                                            <property name="visible">True</property>
-                                            <property name="can_focus">True</property>
-                                            <property name="receives_default">False</property>
-                                            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                                            <property name="active">True</property>
-                                            <property name="draw_indicator">True</property>
-                                          </object>
-                                          <packing>
-                                            <property name="left_attach">2</property>
-                                            <property name="right_attach">3</property>
-                                            <property name="x_options">GTK_FILL</property>
-                                            <property name="y_options">GTK_FILL</property>
-                                          </packing>
-                                        </child>
-                                        <child>
-                                          <object class="GtkCheckButton" id="colon_cbutton">
-                                            <property name="label" translatable="yes">Colon (:)</property>
-                                            <property name="visible">True</property>
-                                            <property name="can_focus">True</property>
-                                            <property name="receives_default">False</property>
-                                            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                                            <property name="draw_indicator">True</property>
-                                          </object>
-                                          <packing>
-                                            <property name="top_attach">1</property>
-                                            <property name="bottom_attach">2</property>
-                                            <property name="x_options">GTK_FILL</property>
-                                            <property name="y_options">GTK_FILL</property>
-                                          </packing>
-                                        </child>
-                                        <child>
-                                          <object class="GtkCheckButton" id="semicolon_cbutton">
-                                            <property name="label" translatable="yes">Semicolon (;)</property>
-                                            <property name="visible">True</property>
-                                            <property name="can_focus">True</property>
-                                            <property name="receives_default">False</property>
-                                            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                                            <property name="draw_indicator">True</property>
-                                          </object>
-                                          <packing>
-                                            <property name="left_attach">1</property>
-                                            <property name="right_attach">2</property>
-                                            <property name="top_attach">1</property>
-                                            <property name="bottom_attach">2</property>
-                                            <property name="x_options">GTK_FILL</property>
-                                            <property name="y_options">GTK_FILL</property>
-                                          </packing>
-                                        </child>
                                         <child>
-                                          <object class="GtkCheckButton" id="hyphen_cbutton">
-                                            <property name="label" translatable="yes">Hyphen (-)</property>
+                                          <object class="GtkSpinButton" id="start_row">
                                             <property name="visible">True</property>
                                             <property name="can_focus">True</property>
-                                            <property name="receives_default">False</property>
-                                            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                                            <property name="draw_indicator">True</property>
+                                            <property name="invisible_char">●</property>
+                                            <property name="invisible_char_set">True</property>
+                                            <property name="primary_icon_activatable">False</property>
+                                            <property name="secondary_icon_activatable">False</property>
+                                            <property name="primary_icon_sensitive">True</property>
+                                            <property name="secondary_icon_sensitive">True</property>
+                                            <property name="adjustment">start_row_adj</property>
+                                            <property name="numeric">True</property>
+                                            <signal name="value-changed" handler="csv_import_trans_srow_cb" swapped="no"/>
                                           </object>
                                           <packing>
-                                            <property name="left_attach">2</property>
-                                            <property name="right_attach">3</property>
-                                            <property name="top_attach">1</property>
-                                            <property name="bottom_attach">2</property>
-                                            <property name="x_options">GTK_FILL</property>
-                                            <property name="y_options">GTK_FILL</property>
+                                            <property name="expand">False</property>
+                                            <property name="fill">False</property>
+                                            <property name="position">0</property>
                                           </packing>
                                         </child>
                                         <child>
-                                          <object class="GtkCheckButton" id="custom_cbutton">
-                                            <property name="label" translatable="yes">Custom</property>
-                                            <property name="visible">True</property>
-                                            <property name="can_focus">True</property>
-                                            <property name="receives_default">False</property>
-                                            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                                            <property name="draw_indicator">True</property>
-                                          </object>
-                                          <packing>
-                                            <property name="top_attach">2</property>
-                                            <property name="bottom_attach">3</property>
-                                            <property name="x_options">GTK_FILL</property>
-                                            <property name="y_options">GTK_FILL</property>
-                                          </packing>
+                                          <placeholder/>
                                         </child>
+                                      </object>
+                                      <packing>
+                                        <property name="left_attach">1</property>
+                                        <property name="right_attach">2</property>
+                                        <property name="top_attach">3</property>
+                                        <property name="bottom_attach">4</property>
+                                      </packing>
+                                    </child>
+                                    <child>
+                                      <object class="GtkHBox" id="hbox3">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">False</property>
                                         <child>
-                                          <object class="GtkEntry" id="custom_entry">
+                                          <object class="GtkSpinButton" id="end_row">
                                             <property name="visible">True</property>
                                             <property name="can_focus">True</property>
-                                            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
                                             <property name="invisible_char">●</property>
                                             <property name="invisible_char_set">True</property>
                                             <property name="primary_icon_activatable">False</property>
                                             <property name="secondary_icon_activatable">False</property>
                                             <property name="primary_icon_sensitive">True</property>
                                             <property name="secondary_icon_sensitive">True</property>
+                                            <property name="adjustment">end_row_adj</property>
+                                            <property name="numeric">True</property>
+                                            <signal name="value-changed" handler="csv_import_trans_erow_cb" swapped="no"/>
                                           </object>
                                           <packing>
-                                            <property name="left_attach">1</property>
-                                            <property name="right_attach">3</property>
-                                            <property name="top_attach">2</property>
-                                            <property name="bottom_attach">3</property>
-                                            <property name="x_options">GTK_FILL</property>
-                                            <property name="y_options">GTK_FILL</property>
+                                            <property name="expand">False</property>
+                                            <property name="fill">False</property>
+                                            <property name="position">0</property>
                                           </packing>
                                         </child>
+                                        <child>
+                                          <placeholder/>
+                                        </child>
                                       </object>
+                                      <packing>
+                                        <property name="left_attach">1</property>
+                                        <property name="right_attach">2</property>
+                                        <property name="top_attach">4</property>
+                                        <property name="bottom_attach">5</property>
+                                      </packing>
                                     </child>
                                   </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">True</property>
+                                    <property name="position">0</property>
+                                  </packing>
                                 </child>
-                                <child type="label">
-                                  <object class="GtkLabel" id="label5">
+                                <child>
+                                  <object class="GtkCheckButton" id="skip_rows">
+                                    <property name="label" translatable="yes">Skip alternate lines</property>
                                     <property name="visible">True</property>
-                                    <property name="can_focus">False</property>
-                                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                                    <property name="label" translatable="yes">Separators</property>
-                                    <property name="use_markup">True</property>
+                                    <property name="can_focus">True</property>
+                                    <property name="receives_default">False</property>
+                                    <property name="tooltip_text" translatable="yes">Starting from the first line that is actually imported every second line will be skipped. This option will take the leading lines to skip into account as well.
+For example
+* if 'Leading Lines to Skip' is set to 3, the first line to import will be line 4. Lines 5, 7, 9,... will be skipped.
+* if 'Leading Lines to Skip' is set to 4, the first line to import will be line 5. Lines 6, 8, 10,... will be skipped.</property>
+                                    <property name="draw_indicator">True</property>
+                                    <signal name="toggled" handler="csv_import_trans_skiprows_cb" swapped="no"/>
                                   </object>
+                                  <packing>
+                                    <property name="expand">True</property>
+                                    <property name="fill">True</property>
+                                    <property name="position">1</property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <placeholder/>
                                 </child>
                               </object>
-                              <packing>
-                                <property name="expand">False</property>
-                                <property name="fill">False</property>
-                                <property name="position">2</property>
-                              </packing>
                             </child>
                           </object>
-                          <packing>
-                            <property name="expand">False</property>
-                            <property name="fill">False</property>
-                            <property name="position">1</property>
-                          </packing>
+                        </child>
+                        <child type="label">
+                          <object class="GtkLabel" id="label13">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="use_markup">True</property>
+                          </object>
                         </child>
                       </object>
                       <packing>
                         <property name="expand">False</property>
                         <property name="fill">False</property>
-                        <property name="position">0</property>
+                        <property name="position">1</property>
                       </packing>
                     </child>
                   </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="padding">5</property>
+                    <property name="position">1</property>
+                  </packing>
                 </child>
               </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
             </child>
-            <child type="label">
-              <object class="GtkLabel" id="label6">
-                <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <property name="use_markup">True</property>
-              </object>
+            <child>
+              <placeholder/>
             </child>
           </object>
           <packing>
             <property name="expand">False</property>
             <property name="fill">False</property>
-            <property name="position">1</property>
+            <property name="position">0</property>
           </packing>
         </child>
         <child>
-          <object class="GtkHBox" id="hbox3">
+          <object class="GtkHBox" id="hbox13">
             <property name="visible">True</property>
             <property name="can_focus">False</property>
             <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
@@ -703,21 +712,22 @@ Select location and file name for the Import, then click 'OK'...
           <packing>
             <property name="expand">False</property>
             <property name="fill">False</property>
-            <property name="position">2</property>
+            <property name="padding">5</property>
+            <property name="position">1</property>
           </packing>
         </child>
         <child>
-          <object class="GtkScrolledWindow" id="scrolledwindow1">
+          <object class="GtkScrolledWindow" id="scrolledwindow2">
             <property name="visible">True</property>
             <property name="can_focus">True</property>
             <property name="hscrollbar_policy">automatic</property>
             <property name="vscrollbar_policy">automatic</property>
             <child>
-              <object class="GtkViewport" id="viewport1">
+              <object class="GtkViewport" id="viewport2">
                 <property name="visible">True</property>
                 <property name="can_focus">False</property>
                 <child>
-                  <object class="GtkVBox" id="vbox2">
+                  <object class="GtkVBox" id="vbox8">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
                     <child>
@@ -755,15 +765,16 @@ Select location and file name for the Import, then click 'OK'...
           <packing>
             <property name="expand">True</property>
             <property name="fill">True</property>
-            <property name="position">4</property>
+            <property name="position">2</property>
           </packing>
         </child>
         <child>
-          <object class="GtkHBox" id="hbox5">
+          <object class="GtkHBox" id="hbox14">
             <property name="visible">True</property>
             <property name="can_focus">False</property>
             <child>
               <object class="GtkCheckButton" id="check_butt">
+                <property name="label" translatable="yes">Skip Errors</property>
                 <property name="can_focus">True</property>
                 <property name="receives_default">False</property>
                 <property name="xalign">1</property>
@@ -780,26 +791,18 @@ Select location and file name for the Import, then click 'OK'...
               </packing>
             </child>
             <child>
-              <object class="GtkLabel" id="check_label">
-                <property name="can_focus">False</property>
-                <property name="xpad">5</property>
-                <property name="label" translatable="yes">Skip Errors</property>
-              </object>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-                <property name="pack_type">end</property>
-                <property name="position">1</property>
-              </packing>
+              <placeholder/>
             </child>
           </object>
           <packing>
             <property name="expand">False</property>
             <property name="fill">False</property>
-            <property name="pack_type">end</property>
-            <property name="position">5</property>
+            <property name="position">3</property>
           </packing>
         </child>
+        <child>
+          <placeholder/>
+        </child>
       </object>
       <packing>
         <property name="title" translatable="yes">Preview Settings</property>
@@ -1026,5 +1029,41 @@ More information can be displayed by using the help button.</property>
         <property name="complete">True</property>
       </packing>
     </child>
+    <child>
+      <placeholder/>
+    </child>
+    <child>
+      <placeholder/>
+    </child>
+    <child>
+      <placeholder/>
+    </child>
+    <child>
+      <placeholder/>
+    </child>
+    <child>
+      <placeholder/>
+    </child>
+    <child>
+      <placeholder/>
+    </child>
+    <child>
+      <placeholder/>
+    </child>
+    <child>
+      <placeholder/>
+    </child>
+    <child>
+      <placeholder/>
+    </child>
+    <child>
+      <placeholder/>
+    </child>
+    <child>
+      <placeholder/>
+    </child>
+    <child>
+      <placeholder/>
+    </child>
   </object>
 </interface>
diff --git a/src/import-export/csv-imp/gnc-csv-trans-settings.c b/src/import-export/csv-imp/gnc-csv-trans-settings.c
index 2bfdbcb..7f70efc 100644
--- a/src/import-export/csv-imp/gnc-csv-trans-settings.c
+++ b/src/import-export/csv-imp/gnc-csv-trans-settings.c
@@ -36,6 +36,12 @@
 #define CSV_NAME         "Name"
 #define CSV_FORMAT       "CsvFormat"
 #define CSV_ALT_ROWS     "AltRows"
+#define CSV_SKIP_START   "SkipStartRows"
+#define CSV_SKIP_END     "SkipEndRows"
+
+/* The following two key names are only used by gnucash 2.6
+ * They have been superseded by CSV_SKIP_START and CSV_SKIP_END.
+ */
 #define CSV_START_ROW    "StartRow"
 #define CSV_END_ROWS     "EndRows"
 
@@ -105,14 +111,14 @@ gnc_csv_trans_find_settings (GtkTreeModel *settings_store)
     GKeyFile   *keyfile;
     gchar     **groups = NULL;
     gint        i;
-    gsize       grouplenght;
+    gsize       grouplength;
     GError     *key_error = NULL;
 
     // Get the Key file
     keyfile = gnc_state_get_current ();
 
     // Find all Groups
-    groups = g_key_file_get_groups (keyfile, &grouplenght);
+    groups = g_key_file_get_groups (keyfile, &grouplength);
 
     // Clear the list store
     gtk_list_store_clear (GTK_LIST_STORE(settings_store));
@@ -122,7 +128,7 @@ gnc_csv_trans_find_settings (GtkTreeModel *settings_store)
     gtk_list_store_set (GTK_LIST_STORE(settings_store), &iter, SET_GROUP, NULL, SET_NAME, _("No Settings"), -1);
 
     // Search all Groups for ones starting with prefix
-    for (i=0; i < grouplenght; i++)
+    for (i=0; i < grouplength; i++)
     {
         if (g_str_has_prefix (groups[i], CSV_GROUP_PREFIX))
         {
@@ -147,18 +153,26 @@ gnc_csv_trans_find_settings (GtkTreeModel *settings_store)
 
 
 /**************************************************
- * load_error
+ * handle_load_error
  *
- * record the error in the log file
+ * record possible errors in the log file
+ * ignore key-not-found errors though. We'll just
+ * use a default value and go on.
  **************************************************/
 static gboolean
-load_error (GError **key_error, gchar *group)
+handle_load_error (GError **key_error, gchar *group)
 {
-    GError *kerror;
-    kerror = g_error_copy (*key_error);
-    g_warning ("Error reading group '%s' : %s", group, kerror->message);
+    if (!*key_error)
+        return FALSE;
+
+    if ((*key_error)->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND)
+    {
+        g_clear_error (key_error);
+        return FALSE;
+    }
+
+    g_warning ("Error reading group '%s' : %s", group, (*key_error)->message);
     g_clear_error (key_error);
-    g_error_free (kerror);
     return TRUE;
 }
 
@@ -181,25 +195,49 @@ gnc_csv_trans_load_settings (CsvSettings *settings_data, gchar *group)
     // Get the Key file
     keyfile = gnc_state_get_current ();
 
-    key_int = g_key_file_get_integer (keyfile, group, CSV_START_ROW, &key_error);
-    settings_data->header_rows = (key_error) ? 1 : key_int;
+    key_int = g_key_file_get_integer (keyfile, group, CSV_SKIP_START, &key_error);
+    settings_data->header_rows = (key_error) ? 0 : key_int;
     if (key_error)
-       error = load_error (&key_error, group);
+    {
+
+       /* If the key was not found (in contrast to failing to interpret its value)
+        * perhaps the file still uses the 2.6 key format. Let's check */
+       gboolean tmp_err = handle_load_error (&key_error, group);
+       if (!tmp_err)
+       {
+           key_int = g_key_file_get_integer (keyfile, group, CSV_START_ROW, &key_error);
+           settings_data->header_rows = (key_error) ? 0 : key_int - 1; // Old key was 1-based, new key is 0-based !
+           error |= handle_load_error (&key_error, group);
+       }
+       else
+           error |= tmp_err;
+
+    }
 
-    key_int = g_key_file_get_integer (keyfile, group, CSV_END_ROWS, &key_error);
+    key_int = g_key_file_get_integer (keyfile, group, CSV_SKIP_END, &key_error);
     settings_data->footer_rows = (key_error) ? 0 : key_int;
     if (key_error)
-       error = load_error (&key_error, group);
+    {
+       /* If the key was not found (in contrast to failing to interpret its value)
+        * perhaps the file still uses the 2.6 key format. Let's check */
+       gboolean tmp_err = handle_load_error (&key_error, group);
+       if (!tmp_err)
+       {
+           key_int = g_key_file_get_integer (keyfile, group, CSV_END_ROWS, &key_error);
+           settings_data->footer_rows = (key_error) ? 0 : key_int; // Old key and new key are both 0-based !
+           error |= handle_load_error (&key_error, group);
+       }
+       else
+           error |= tmp_err;
+    }
 
     key_boolean = g_key_file_get_boolean (keyfile, group, CSV_ALT_ROWS, &key_error);
     settings_data->skip_alt_rows = (key_error) ? FALSE : key_boolean;
-    if (key_error)
-       error = load_error (&key_error, group);
+    error |= handle_load_error (&key_error, group);
 
     key_boolean = g_key_file_get_boolean (keyfile, group, CSV_FORMAT, &key_error);
     settings_data->csv_format = (key_error) ? TRUE : key_boolean;
-    if (key_error)
-       error = load_error (&key_error, group);
+    error |= handle_load_error (&key_error, group);
 
     for (i = 0; i < SEP_NUM_OF_TYPES; i++)
     {
@@ -207,42 +245,34 @@ gnc_csv_trans_load_settings (CsvSettings *settings_data, gchar *group)
         sep = g_strdup_printf ("%s%d", CSV_SEP, i);
         key_boolean = g_key_file_get_boolean (keyfile, group, sep, &key_error);
         settings_data->separator[i] = (key_error) ? FALSE : key_boolean;
-        if (key_error)
-           error = load_error (&key_error, group);
+        error |= handle_load_error (&key_error, group);
         g_free (sep);
     }
 
     key_boolean = g_key_file_get_boolean (keyfile, group, CSV_CUSTOM, &key_error);
     settings_data->custom = (key_error) ? FALSE : key_boolean;
-    if (key_error)
-       error = load_error (&key_error, group);
+    error |= handle_load_error (&key_error, group);
 
     settings_data->custom_entry = g_key_file_get_string (keyfile, group, CSV_CUSTOM_ENTRY, &key_error);
-    if (key_error)
-       error = load_error (&key_error, group);
+    error |= handle_load_error (&key_error, group);
 
     key_int = g_key_file_get_integer (keyfile, group, CSV_DATE, &key_error);
     settings_data->date_active = (key_error) ? 0 : key_int;
-    if (key_error)
-       error = load_error (&key_error, group);
+    error |= handle_load_error (&key_error, group);
 
     key_int = g_key_file_get_integer (keyfile, group, CSV_CURRENCY, &key_error);
     settings_data->currency_active = (key_error) ? 0 : key_int;
-    if (key_error)
-       error = load_error (&key_error, group);
+    error |= handle_load_error (&key_error, group);
 
     key_char = g_key_file_get_string (keyfile, group, CSV_ENCODING, &key_error);
     settings_data->encoding = g_strdup((key_error) ? "UTF-8" : key_char);
-    if (key_error)
-       error = load_error (&key_error, group);
+    error |= handle_load_error (&key_error, group);
 
     settings_data->column_types = g_key_file_get_string (keyfile, group, CSV_COL_TYPES, &key_error);
-    if (key_error)
-       error = load_error (&key_error, group);
+    error |= handle_load_error (&key_error, group);
 
     settings_data->column_widths = g_key_file_get_string (keyfile, group, CSV_COL_WIDTHS, &key_error);
-    if (key_error)
-       error = load_error (&key_error, group);
+    error |= handle_load_error (&key_error, group);
 
     g_free (key_char);
     return error;
@@ -304,8 +334,8 @@ gnc_csv_trans_save_settings (CsvSettings *settings_data, gchar *settings_name)
     // Start Saving the settings
     g_key_file_set_string (keyfile, group, CSV_NAME, settings_name);
 
-    g_key_file_set_integer (keyfile, group, CSV_START_ROW, settings_data->header_rows);
-    g_key_file_set_integer (keyfile, group, CSV_END_ROWS, settings_data->footer_rows);
+    g_key_file_set_integer (keyfile, group, CSV_SKIP_START, settings_data->header_rows);
+    g_key_file_set_integer (keyfile, group, CSV_SKIP_END, settings_data->footer_rows);
     g_key_file_set_boolean (keyfile, group, CSV_ALT_ROWS, settings_data->skip_alt_rows);
     g_key_file_set_boolean (keyfile, group, CSV_FORMAT, settings_data->csv_format);
 
diff --git a/src/import-export/csv-imp/gnc-tx-import.cpp b/src/import-export/csv-imp/gnc-tx-import.cpp
index 7bf507a..bdcd448 100644
--- a/src/import-export/csv-imp/gnc-tx-import.cpp
+++ b/src/import-export/csv-imp/gnc-tx-import.cpp
@@ -68,9 +68,9 @@ GncTxImport::GncTxImport(GncImpFileFormat format)
      * initialized, only the data that needs to be freed is freed. */
     date_format = -1;
     currency_format = 0;
-    start_row = 0;
-    end_row = 1000;
-    skip_rows = FALSE;
+    skip_start_lines = 0;
+    skip_end_lines = 0;
+    skip_alt_lines = FALSE;
     parse_errors = false;
 
     file_fmt = format;
@@ -423,13 +423,10 @@ void GncTxImport::create_transactions (Account* account,
 
     /* compute start and end iterators based on user-set restrictions */
     auto parsed_lines_it = parsed_lines.begin();
-    std::advance(parsed_lines_it, start_row);
+    std::advance(parsed_lines_it, skip_start_lines);
 
     auto parsed_lines_max = parsed_lines.begin();
-    if (end_row > parsed_lines.size())
-        parsed_lines_max = parsed_lines.end();
-    else
-        std::advance(parsed_lines_max, end_row);
+    std::advance(parsed_lines_max, parsed_lines.size() - skip_end_lines);
 
     base_account = account;
     auto odd_line = false;
@@ -437,7 +434,7 @@ void GncTxImport::create_transactions (Account* account,
 
     /* Iterate over all parsed lines */
     for (parsed_lines_it, odd_line;
-            parsed_lines_it != parsed_lines_max;
+            parsed_lines_it < parsed_lines_max;
             ++parsed_lines_it, odd_line = !odd_line)
     {
         auto parsed_line = *parsed_lines_it;
@@ -449,7 +446,7 @@ void GncTxImport::create_transactions (Account* account,
               skip_rows is enabled AND
               current line is an odd line */
         if ((redo_errors && std::get<1>(parsed_line).empty()) ||
-           (!redo_errors && skip_rows && odd_line))
+           (!redo_errors && skip_alt_lines && odd_line))
             continue;
 
         try
diff --git a/src/import-export/csv-imp/gnc-tx-import.hpp b/src/import-export/csv-imp/gnc-tx-import.hpp
index 04ad77a..01dd2fb 100644
--- a/src/import-export/csv-imp/gnc-tx-import.hpp
+++ b/src/import-export/csv-imp/gnc-tx-import.hpp
@@ -113,9 +113,9 @@ public:
     std::multimap <time64, std::shared_ptr<DraftTransaction>> transactions;  /**< map of transaction objects created
                                                      from parsed_lines and column_types, ordered by date */
     int date_format;            /**< The format of the text in the date columns from date_format_internal. */
-    guint start_row;            /**< The start row to generate transactions from. */
-    guint end_row;              /**< The end row to generate transactions from. */
-    gboolean skip_rows;         /**< Skip Alternate Rows from start row. */
+    guint skip_start_lines;     /**< Number of lines to skip at the beginning of the parse data. */
+    guint skip_end_lines;       /**< Number of lines to skip at the end of the parse data. */
+    gboolean skip_alt_lines;         /**< Skip Alternate Rows from start row. */
     int currency_format;        /**< The currency format, 0 for locale, 1 for comma dec and 2 for period */
     bool parse_errors;          /**< Indicates whether the last parse_to_trans run had any errors */
 

commit 056d20c1f73b0db7e7553929e28914a074cf5ed8
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Tue Nov 29 20:33:04 2016 +0100

    Fix memory leaks in the draft transactions object

diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index 82a0127..8148923 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -2463,7 +2463,11 @@ csv_import_trans_assistant_match_page_prepare (GtkAssistant *assistant,
             for (auto trans_it : info->parse_data->transactions)
             {
                 auto draft_trans = trans_it.second;
-                gnc_gen_trans_list_add_trans (info->gnc_csv_importer_gui, draft_trans->trans);
+                if (draft_trans->trans)
+                {
+                    gnc_gen_trans_list_add_trans (info->gnc_csv_importer_gui, draft_trans->trans);
+                    draft_trans->trans = nullptr;
+                }
             }
         }
     }
diff --git a/src/import-export/csv-imp/gnc-tx-import.cpp b/src/import-export/csv-imp/gnc-tx-import.cpp
index 651a753..7bf507a 100644
--- a/src/import-export/csv-imp/gnc-tx-import.cpp
+++ b/src/import-export/csv-imp/gnc-tx-import.cpp
@@ -240,9 +240,9 @@ static void trans_properties_verify_essentials (parse_line_t& parsed_line)
  * Note: this function assumes all properties have been verified
  *       to be valid and the required properties are available.
  * @param parsed_line The current line being parsed
- * @return On success, a DraftTransaction; on failure a nullptr
+ * @return On success, a shared pointer to a DraftTransaction object; on failure a nullptr
  */
-static DraftTransaction* trans_properties_to_trans (parse_line_t& parsed_line)
+static std::shared_ptr<DraftTransaction> trans_properties_to_trans (parse_line_t& parsed_line)
 {
     std::string error_message;
     std::shared_ptr<GncPreTrans> trans_props;
@@ -258,10 +258,7 @@ static DraftTransaction* trans_properties_to_trans (parse_line_t& parsed_line)
     if (!trans)
         return nullptr;
 
-    DraftTransaction* draft_trans = g_new (DraftTransaction, 1);
-    draft_trans->balance_set = false;
-    draft_trans->balance = gnc_numeric_zero();
-
+    auto draft_trans = std::make_shared<DraftTransaction>(trans);
     auto balance = split_props->create_split(trans);
     if (balance)
     {
@@ -390,7 +387,7 @@ void GncTxImport::create_transaction (parse_line_t& parsed_line)
         if (draft_trans)
         {
             auto trans_date = xaccTransGetDate (draft_trans->trans);
-            transactions.insert (std::pair<time64, DraftTransaction*>(trans_date,draft_trans));
+            transactions.insert (std::pair<time64, std::shared_ptr<DraftTransaction>>(trans_date,std::move(draft_trans)));
         }
     }
     catch (const std::invalid_argument& e)
@@ -420,11 +417,8 @@ void GncTxImport::create_transactions (Account* account,
         for (auto orig_line : parsed_lines)
             std::get<1>(orig_line).clear();
 
-        /* FIXME handle memory leak here!
-         * Existing transactions in the map should probably removed before emptying the map
-         */
-        if (!transactions.empty())
-            transactions.clear();
+        /* Drop all existing draft transactions on a full run */
+        transactions.clear();
     }
 
     /* compute start and end iterators based on user-set restrictions */
diff --git a/src/import-export/csv-imp/gnc-tx-import.hpp b/src/import-export/csv-imp/gnc-tx-import.hpp
index af0e195..04ad77a 100644
--- a/src/import-export/csv-imp/gnc-tx-import.hpp
+++ b/src/import-export/csv-imp/gnc-tx-import.hpp
@@ -49,6 +49,8 @@ extern "C" {
  *  the user had selected a balance column. */
 struct DraftTransaction
 {
+    DraftTransaction (Transaction* tx) : trans(tx), balance(gnc_numeric_zero()), balance_set(false) {}
+    ~DraftTransaction () { if (trans) { xaccTransDestroy (trans); trans = nullptr; } }
     Transaction* trans;
     gnc_numeric balance;  /**< The expected balance after this transaction takes place */
     bool balance_set;     /**< true if balance has been set from user data, false otherwise */
@@ -108,7 +110,7 @@ public:
                                                      Per line also holds possible error messages and objects with extracted transaction
                                                      and split properties. */
     std::vector<GncTransPropType> column_types; /**< Vector of values from the GncCsvColumnType enumeration */
-    std::multimap <time64, <DraftTransaction*> transactions;  /**< map of transaction objects created
+    std::multimap <time64, std::shared_ptr<DraftTransaction>> transactions;  /**< map of transaction objects created
                                                      from parsed_lines and column_types, ordered by date */
     int date_format;            /**< The format of the text in the date columns from date_format_internal. */
     guint start_row;            /**< The start row to generate transactions from. */

commit 854ee319894f941d2f89d43cf8b733b98c69d4b4
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sun Dec 11 17:10:53 2016 +0100

    Cleanup commit
    
    - improve comments
    - improve variable and function names (to be more concise and to the point
    - use 'Transfer Account' instead of 'Other Account' as that's the term used in the rest of gnucash

diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index 894c35d..82a0127 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -284,7 +284,7 @@ csv_import_trans_load_settings (CsvImportTrans *info)
             if (info->settings_data->column_widths, NULL)
                 fwtok->cols_from_string (std::string(info->settings_data->column_widths));
 
-            info->parse_data->parse (false);
+            info->parse_data->tokenize (false);
             gnc_csv_preview_update_assist (info);
         }
     }
@@ -323,7 +323,7 @@ csv_import_trans_load_settings (CsvImportTrans *info)
             int saved_col_type = atoi (columns[i]);
 
             if (saved_col_type >= static_cast<int>(GncTransPropType::NONE) &&
-                saved_col_type <= static_cast<int>(GncTransPropType::OMEMO))
+                saved_col_type <= static_cast<int>(GncTransPropType::TMEMO))
             {
                 col_type = static_cast<GncTransPropType>(saved_col_type);
                 info->parse_data->column_types.at(i) = col_type;
@@ -693,7 +693,7 @@ csv_import_trans_file_chooser_confirm_cb (GtkWidget *button, CsvImportTrans *inf
     {
         parse_data->file_format (GncImpFileFormat::CSV);
         parse_data->load_file (info->file_name);
-        parse_data->parse (true);
+        parse_data->tokenize (true);
     }
     catch (std::ifstream::failure& e)
     {
@@ -939,7 +939,7 @@ void sep_button_clicked (GtkWidget* widget, CsvImportTrans* info)
      * configurations intact. */
     try
     {
-        info->parse_data->parse (false);
+        info->parse_data->tokenize (false);
     }
     catch (std::range_error &e)
     {
@@ -999,7 +999,7 @@ static void separated_or_fixed_selected (GtkToggleButton* csv_button, CsvImportT
         info->parse_data->file_format (GncImpFileFormat::FIXED_WIDTH);
 
         /* Reparse the data. */
-        info->parse_data->parse (false);
+        info->parse_data->tokenize (false);
 
         /* Show the new data. */
         gnc_csv_preview_update_assist (info);
@@ -1050,7 +1050,7 @@ static void encoding_selected (GOCharmapSel* selector, const char* encoding,
         try
         {
             info->parse_data->convert_encoding (encoding);
-            info->parse_data->parse (false);
+            info->parse_data->tokenize (false);
         }
         catch (...)
         {
@@ -1209,7 +1209,7 @@ fixed_context_menu_handler (GnumericPopupMenuElement const *element,
 
     try
     {
-        info->parse_data->parse (false);
+        info->parse_data->tokenize (false);
     }
     catch(std::range_error& e)
     {
@@ -1394,7 +1394,7 @@ split_column (CsvImportTrans* info, int col, int dx)
     fwtok->col_split (col, rel_pos);
     try
     {
-        info->parse_data->parse (false);
+        info->parse_data->tokenize (false);
     }
     catch (std::range_error& e)
     {
@@ -1520,11 +1520,11 @@ bool preview_settings_valid (CsvImportTrans* info)
             weight = weight + 1;
             break;
 
-        case GncTransPropType::OACCOUNT:
+        case GncTransPropType::TACCOUNT:
             oweight = oweight + 100;
             break;
 
-        case GncTransPropType::OMEMO:
+        case GncTransPropType::TMEMO:
             oweight = oweight + 1;
             break;
         default:
@@ -1634,7 +1634,7 @@ bool get_list_of_accounts (CsvImportTrans* info, GtkTreeModel *store)
             gtk_tree_model_get (ctstore, &iter1, 2 * i + 1, &col_type, -1);
 
             /* We're only interested in columns of type ACCOUNT and OACCOUNT. */
-            if ((col_type == GncTransPropType::ACCOUNT) || (col_type == GncTransPropType::OACCOUNT))
+            if ((col_type == GncTransPropType::ACCOUNT) || (col_type == GncTransPropType::TACCOUNT))
             {
                 /* Get an iterator for the row in the data store. */
                 if (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL(datastore), &iter2, NULL, j))
@@ -1723,8 +1723,8 @@ static void gnc_csv_preview_update_assist (CsvImportTrans* info)
     g_free (bodytypes);
 
     /* Fill the data liststore with data from the file. */
-    info->num_of_rows = info->parse_data->orig_lines.size();
-    for (auto parse_line : info->parse_data->orig_lines)
+    info->num_of_rows = info->parse_data->parsed_lines.size();
+    for (auto parse_line : info->parse_data->parsed_lines)
     {
         // When previewing errors skip all lines that don't have errors
         if (info->previewing_errors && std::get<1>(parse_line).empty())
@@ -2462,8 +2462,8 @@ csv_import_trans_assistant_match_page_prepare (GtkAssistant *assistant,
             /* Copy all of the transactions to the importer GUI. */
             for (auto trans_it : info->parse_data->transactions)
             {
-                auto trans_line = trans_it.second;
-                gnc_gen_trans_list_add_trans (info->gnc_csv_importer_gui, trans_line->trans);
+                auto draft_trans = trans_it.second;
+                gnc_gen_trans_list_add_trans (info->gnc_csv_importer_gui, draft_trans->trans);
             }
         }
     }
@@ -2563,7 +2563,7 @@ csv_import_trans_forward_page_func (gint current_page, gpointer user_data)
 
                 // Check to see if we have an account / other account columns
                 if (info->parse_data->check_for_column_type (GncTransPropType::ACCOUNT) ||
-                        info->parse_data->check_for_column_type (GncTransPropType::OACCOUNT))
+                        info->parse_data->check_for_column_type (GncTransPropType::TACCOUNT))
                     next_page = 4;
                 else
                     next_page = 5;
@@ -2593,7 +2593,7 @@ csv_import_trans_forward_page_func (gint current_page, gpointer user_data)
             {
                 /* Create transactions from the parsed data, first time with false
                    Subsequent times with true */
-                    info->parse_data->parse_to_trans (info->account, info->match_parse_run);
+                    info->parse_data->create_transactions (info->account, info->match_parse_run);
 
                 /* if there are errors, we jump back to preview to correct */
                 if (info->parse_data->parse_errors && !info->skip_errors)
diff --git a/src/import-export/csv-imp/gnc-trans-props.cpp b/src/import-export/csv-imp/gnc-trans-props.cpp
index e85f699..5699b92 100644
--- a/src/import-export/csv-imp/gnc-trans-props.cpp
+++ b/src/import-export/csv-imp/gnc-trans-props.cpp
@@ -55,8 +55,8 @@ std::map<GncTransPropType, const char*> gnc_csv_col_type_strs = {
         { GncTransPropType::WITHDRAWAL, N_("Withdrawal") },
         { GncTransPropType::BALANCE, N_("Balance") },
         { GncTransPropType::MEMO, N_("Memo") },
-        { GncTransPropType::OACCOUNT, N_("Other Account") },
-        { GncTransPropType::OMEMO, N_("Other Memo") }
+        { GncTransPropType::TACCOUNT, N_("Transfer Account") },
+        { GncTransPropType::TMEMO, N_("Transfer Memo") }
 };
 
 /* Regular expressions used to parse dates per date format */
@@ -171,7 +171,7 @@ time64 parse_date (const std::string &date_str, int format)
  * @param currency_format The currency format to use.
  * @return a gnc_numeric on success, boost::none on failure
  */
-static boost::optional<gnc_numeric> convert_amount_col_str (const std::string &str, int currency_format)
+static boost::optional<gnc_numeric> parse_amount (const std::string &str, int currency_format)
 {
     /* If a cell is empty or just spaces return invalid amount */
     if(!boost::regex_search(str, boost::regex("[0-9]")))
@@ -206,24 +206,24 @@ static boost::optional<gnc_numeric> convert_amount_col_str (const std::string &s
 }
 
 
-void GncPreTrans::set_property (GncTransPropType prop_type, const std::string& prop_value_str, int date_format)
+void GncPreTrans::set_property (GncTransPropType prop_type, const std::string& value, int date_format)
 {
     switch (prop_type)
     {
         case GncTransPropType::DATE:
-            m_date = parse_date (prop_value_str.c_str(), date_format); // Throws if parsing fails
+            m_date = parse_date (value.c_str(), date_format); // Throws if parsing fails
             break;
 
         case GncTransPropType::DESCRIPTION:
-            if (!prop_value_str.empty())
-                m_desc = prop_value_str;
+            if (!value.empty())
+                m_desc = value;
             else
                 m_desc = boost::none;
             break;
 
         case GncTransPropType::NOTES:
-            if (!prop_value_str.empty())
-                m_notes = prop_value_str;
+            if (!value.empty())
+                m_notes = value;
             else
                 m_notes = boost::none;
             break;
@@ -267,56 +267,56 @@ Transaction* GncPreTrans::create_trans (QofBook* book, gnc_commodity* currency)
 }
 
 
-void GncPreSplit::set_property (GncTransPropType prop_type, const std::string& prop_value_str, int currency_format)
+void GncPreSplit::set_property (GncTransPropType prop_type, const std::string& value, int currency_format)
 {
     Account *acct = nullptr;
     switch (prop_type)
     {
         case GncTransPropType::ACCOUNT:
-            acct = gnc_csv_account_map_search (prop_value_str.c_str());
+            acct = gnc_csv_account_map_search (value.c_str());
             if (acct)
                 m_account = acct;
             else
                 throw std::invalid_argument ("String can't be mapped back to an account.");
             break;
 
-        case GncTransPropType::OACCOUNT:
-            acct = gnc_csv_account_map_search (prop_value_str.c_str());
+        case GncTransPropType::TACCOUNT:
+            acct = gnc_csv_account_map_search (value.c_str());
             if (acct)
-                m_oaccount = acct;
+                m_taccount = acct;
             else
                 throw std::invalid_argument ("String can't be mapped back to an account.");
             break;
 
         case GncTransPropType::MEMO:
-            if (!prop_value_str.empty())
-                m_memo = prop_value_str;
+            if (!value.empty())
+                m_memo = value;
             else
                 m_memo = boost::none;
             break;
 
-        case GncTransPropType::OMEMO:
-            if (!prop_value_str.empty())
-                m_omemo = prop_value_str;
+        case GncTransPropType::TMEMO:
+            if (!value.empty())
+                m_tmemo = value;
             else
-                m_omemo = boost::none;
+                m_tmemo = boost::none;
             break;
 
         case GncTransPropType::NUM:
-            if (!prop_value_str.empty())
-                m_num = prop_value_str;
+            if (!value.empty())
+                m_num = value;
             else
                 m_num = boost::none;
             break;
 
         case GncTransPropType::BALANCE:
-            m_balance = convert_amount_col_str (prop_value_str, currency_format); // Will throw if parsing fails
+            m_balance = parse_amount (value, currency_format); // Will throw if parsing fails
             break;
         case GncTransPropType::DEPOSIT:
-            m_deposit = convert_amount_col_str (prop_value_str, currency_format); // Will throw if parsing fails
+            m_deposit = parse_amount (value, currency_format); // Will throw if parsing fails
             break;
         case GncTransPropType::WITHDRAWAL:
-            m_withdrawal = convert_amount_col_str (prop_value_str, currency_format); // Will throw if parsing fails
+            m_withdrawal = parse_amount (value, currency_format); // Will throw if parsing fails
             break;
 
         default:
@@ -366,9 +366,9 @@ boost::optional<gnc_numeric> GncPreSplit::create_split (Transaction* trans)
     auto book = xaccTransGetBook (trans);
     std::string num;
     std::string memo;
-    std::string omemo;
+    std::string tmemo;
     Account *account = nullptr;
-    Account *oaccount = nullptr;
+    Account *taccount = nullptr;
     bool amount_set = false;
     gnc_numeric deposit = { 0, 1 };
     gnc_numeric withdrawal = { 0, 1 };
@@ -376,12 +376,12 @@ boost::optional<gnc_numeric> GncPreSplit::create_split (Transaction* trans)
 
     if (m_account)
         account = *m_account;
-    if (m_oaccount)
-        oaccount = *m_oaccount;
+    if (m_taccount)
+        taccount = *m_taccount;
     if (m_memo)
         memo = *m_memo;
-    if (m_omemo)
-        omemo = *m_omemo;
+    if (m_tmemo)
+        tmemo = *m_tmemo;
     if (m_num)
         num = *m_num;
     if (m_deposit)
@@ -402,12 +402,12 @@ boost::optional<gnc_numeric> GncPreSplit::create_split (Transaction* trans)
     /* Add a split with the cumulative amount value. */
     trans_add_split (trans, account, book, amount, num, memo);
 
-    if (oaccount)
+    if (taccount)
         /* Note: the current importer assumes at most 2 splits. This means the second split amount
          * will be the negative of the the first split amount. We also only set the num field once,
          * for the first split.
          */
-        trans_add_split (trans, oaccount, book, gnc_numeric_neg(amount), "", omemo);
+        trans_add_split (trans, taccount, book, gnc_numeric_neg(amount), "", tmemo);
 
 
     created = true;
diff --git a/src/import-export/csv-imp/gnc-trans-props.hpp b/src/import-export/csv-imp/gnc-trans-props.hpp
index 7b1552d..e631458 100644
--- a/src/import-export/csv-imp/gnc-trans-props.hpp
+++ b/src/import-export/csv-imp/gnc-trans-props.hpp
@@ -57,9 +57,9 @@ enum class GncTransPropType {
     WITHDRAWAL,
     BALANCE,
     MEMO,
-    OACCOUNT,
-    OMEMO,
-    SPLIT_PROPS = OMEMO
+    TACCOUNT,
+    TMEMO,
+    SPLIT_PROPS = TMEMO
 };
 
 /** Maps all column types to a string representation.
@@ -73,7 +73,7 @@ time64 parse_date (const std::string &date_str, int format);
 struct GncPreTrans
 {
 public:
-    void set_property (GncTransPropType prop_type, const std::string& prop_value_str, int date_format = 0);
+    void set_property (GncTransPropType prop_type, const std::string& value, int date_format = 0);
     std::string verify_essentials (void);
     Transaction *create_trans (QofBook* book, gnc_commodity* currency);
 
@@ -87,7 +87,7 @@ private:
 struct GncPreSplit
 {
 public:
-    void set_property (GncTransPropType prop_type, const std::string& prop_value_str, int currency_format = 0);
+    void set_property (GncTransPropType prop_type, const std::string& value, int currency_format = 0);
     std::string verify_essentials (void);
     boost::optional<gnc_numeric> create_split(Transaction* trans);
 
@@ -100,8 +100,8 @@ private:
     boost::optional<gnc_numeric> m_withdrawal;
     boost::optional<gnc_numeric> m_balance;
     boost::optional<std::string> m_memo;
-    boost::optional<Account*> m_oaccount;
-    boost::optional<std::string> m_omemo;
+    boost::optional<Account*> m_taccount;
+    boost::optional<std::string> m_tmemo;
 
     // Strictly speaking num is a transaction property
     // However due to the option to swap num and action fields
diff --git a/src/import-export/csv-imp/gnc-tx-import.cpp b/src/import-export/csv-imp/gnc-tx-import.cpp
index 248f245..651a753 100644
--- a/src/import-export/csv-imp/gnc-tx-import.cpp
+++ b/src/import-export/csv-imp/gnc-tx-import.cpp
@@ -57,6 +57,7 @@ G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT;
 //                                      };
 //
 
+
 /** Constructor for GncTxImport.
  * @return Pointer to a new GncCSvParseData
  */
@@ -87,7 +88,7 @@ GncTxImport::~GncTxImport()
  *  previously set file format was different and a
  *  filename was already set.
  *  @param format the new format to set
- *  @exception the reloading of the file may throw std::ifstream::failure
+ *  @exception std::ifstream::failure if file reloading fails
  */
 void GncTxImport::file_format(GncImpFileFormat format)
 {
@@ -112,6 +113,7 @@ void GncTxImport::file_format(GncImpFileFormat format)
     tokenizer->encoding(new_encoding);
     load_file(new_imp_file);
 }
+
 GncImpFileFormat GncTxImport::file_format()
 {
     return file_fmt;
@@ -120,7 +122,6 @@ GncImpFileFormat GncTxImport::file_format()
 /** Converts raw file data using a new encoding. This function must be
  * called after load_file only if load_file guessed
  * the wrong encoding.
- * @param parse_data Data that is being parsed
  * @param encoding Encoding that data should be translated using
  */
 void GncTxImport::convert_encoding (const std::string& encoding)
@@ -131,13 +132,8 @@ void GncTxImport::convert_encoding (const std::string& encoding)
 }
 
 /** Loads a file into a GncTxImport. This is the first function
- * that must be called after creating a new GncTxImport. If this
- * fails because the file couldn't be opened, no more functions can be
- * called on the parse data until this succeeds (or until it fails
- * because of an encoding guess error). If it fails because the
- * encoding could not be guessed, gnc_csv_convert_encoding must be
- * called until it succeeds.
- * @param parse_data Data that is being parsed
+ * that must be called after creating a new GncTxImport. As long as
+ * this function didn't run successfully, the importer can't proceed.
  * @param filename Name of the file that should be opened
  * @exception may throw std::ifstream::failure on any io error
  */
@@ -158,24 +154,25 @@ void GncTxImport::load_file (const std::string& filename)
     }
 }
 
-/** Parses a file into cells. This requires having an encoding that
- * works (see gnc_csv_convert_encoding). options should be
- * set according to how the user wants before calling this
- * function. (Note: this function must be called with guessColTypes as
- * TRUE before it is ever called with it as FALSE.) (Note: if
- * guessColTypes is TRUE, all the column types will be GncTransPropType::NONE
- * right now.)
- * @param guessColTypes TRUE to guess what the types of columns are based on the cell contents
- * @exception throws std::range_error if parsing failed
+/** Splits a file into cells. This requires having an encoding that
+ * works (see GncTxImport::convert_encoding). Tokenizing related options
+ * should be set to the user's selections before calling this
+ * function.
+ * Notes: - this function must be called with guessColTypes set to true once
+ *          before calling it with guessColTypes set to false.
+ *        - if guessColTypes is TRUE, all the column types will be set
+ *          GncTransPropType::NONE right now as real guessing isn't implemented yet
+ * @param guessColTypes true to guess what the types of columns are based on the cell contents
+ * @exception std::range_error if tokenizing failed
  */
-void GncTxImport::parse (bool guessColTypes)
+void GncTxImport::tokenize (bool guessColTypes)
 {
     uint max_cols = 0;
     tokenizer->tokenize();
-    orig_lines.clear();
+    parsed_lines.clear();
     for (auto tokenized_line : tokenizer->get_tokens())
     {
-        orig_lines.push_back (std::make_tuple (tokenized_line, std::string(),
+        parsed_lines.push_back (std::make_tuple (tokenized_line, std::string(),
                 std::make_shared<GncPreTrans>(), std::make_shared<GncPreSplit>()));
         auto length = tokenized_line.size();
         if (length > max_cols)
@@ -183,9 +180,9 @@ void GncTxImport::parse (bool guessColTypes)
     }
 
     /* If it failed, generate an error. */
-    if (orig_lines.size() == 0)
+    if (parsed_lines.size() == 0)
     {
-        throw (std::range_error ("Parsing failed."));
+        throw (std::range_error ("Tokenizing failed."));
         return;
     }
 
@@ -211,16 +208,16 @@ void GncTxImport::parse (bool guessColTypes)
  * - at least one of "Balance", "Deposit", or "Withdrawal"
  * - "Account"
  * Note account isn't checked for here as this has been done before
- * @param parse_line The line we are checking
+ * @param parsed_line The line we are checking
  * @exception std::invalid_argument in an essential property is missing
  */
-static void trans_properties_verify_essentials (parse_line_t& orig_line)
+static void trans_properties_verify_essentials (parse_line_t& parsed_line)
 {
     std::string error_message;
     std::shared_ptr<GncPreTrans> trans_props;
     std::shared_ptr<GncPreSplit> split_props;
 
-    std::tie(std::ignore, error_message, trans_props, split_props) = orig_line;
+    std::tie(std::ignore, error_message, trans_props, split_props) = parsed_line;
 
     auto trans_error = trans_props->verify_essentials();
     auto split_error = split_props->verify_essentials();
@@ -238,19 +235,19 @@ static void trans_properties_verify_essentials (parse_line_t& orig_line)
     if (!error_message.empty())
         throw std::invalid_argument(error_message);
 }
-/** Create a Transaction from a map of transaction properties.
- * Note: this function assumes all properties in the map have been verified
- *       to be valid. No further checks are performed here other than that
- *       the required properties are in the map
- * @param orig_line The current line being parsed
- * @return On success, a GncCsvTransLine; on failure, the trans pointer is NULL
+
+/** Create a transaction and splits from a pair of trans and split property objects.
+ * Note: this function assumes all properties have been verified
+ *       to be valid and the required properties are available.
+ * @param parsed_line The current line being parsed
+ * @return On success, a DraftTransaction; on failure a nullptr
  */
-static GncCsvTransLine* trans_properties_to_trans (parse_line_t& orig_line)
+static DraftTransaction* trans_properties_to_trans (parse_line_t& parsed_line)
 {
     std::string error_message;
     std::shared_ptr<GncPreTrans> trans_props;
     std::shared_ptr<GncPreSplit> split_props;
-    std::tie(std::ignore, error_message, trans_props, split_props) = orig_line;
+    std::tie(std::ignore, error_message, trans_props, split_props) = parsed_line;
     auto account = split_props->get_account();
 
     QofBook* book = gnc_account_get_book (account);
@@ -261,30 +258,29 @@ static GncCsvTransLine* trans_properties_to_trans (parse_line_t& orig_line)
     if (!trans)
         return nullptr;
 
-    GncCsvTransLine* trans_line = g_new (GncCsvTransLine, 1);
-    trans_line->balance_set = false;
-    trans_line->balance = gnc_numeric_zero();
+    DraftTransaction* draft_trans = g_new (DraftTransaction, 1);
+    draft_trans->balance_set = false;
+    draft_trans->balance = gnc_numeric_zero();
 
     auto balance = split_props->create_split(trans);
     if (balance)
     {
-        trans_line->balance_set = true;
-        trans_line->balance = *balance;
+        draft_trans->balance_set = true;
+        draft_trans->balance = *balance;
     }
 
-    return trans_line;
+    return draft_trans;
 }
 
-void GncTxImport::adjust_balances (Account *account)
+void GncTxImport::adjust_balances (void)
 {
-    Split      *split, *osplit;
+    Split      *split, *tsplit;
 
     /* balance_offset is how much the balance currently in the account
      * differs from what it will be after the transactions are
      * imported. This will be sum of all the previous transactions for
      * any given transaction. */
-    auto balance_offset = double_to_gnc_numeric (0.0, xaccAccountGetCommoditySCU (account),
-                                 GNC_HOW_RND_ROUND_HALF_UP);
+    auto balance_offset = gnc_numeric_zero();
     for (auto trans_iter : transactions)
     {
         auto trans_line = trans_iter.second;
@@ -293,25 +289,25 @@ void GncTxImport::adjust_balances (Account *account)
             time64 date = xaccTransGetDate (trans_line->trans);
             /* Find what the balance should be by adding the offset to the actual balance. */
             gnc_numeric existing_balance = gnc_numeric_add (balance_offset,
-                                           xaccAccountGetBalanceAsOfDate (account, date),
-                                           xaccAccountGetCommoditySCU (account),
+                                           xaccAccountGetBalanceAsOfDate (base_account, date),
+                                           xaccAccountGetCommoditySCU (base_account),
                                            GNC_HOW_RND_ROUND_HALF_UP);
 
             /* The amount of the transaction is the difference between the new and existing balance. */
             gnc_numeric amount = gnc_numeric_sub (trans_line->balance,
                                                  existing_balance,
-                                                 xaccAccountGetCommoditySCU (account),
+                                                 xaccAccountGetCommoditySCU (base_account),
                                                  GNC_HOW_RND_ROUND_HALF_UP);
 
             // Find home account split
-            split  = xaccTransFindSplitByAccount (trans_line->trans, account);
+            split  = xaccTransFindSplitByAccount (trans_line->trans, base_account);
             xaccSplitSetAmount (split, amount);
             xaccSplitSetValue (split, amount);
 
             // If we have two splits, change other side
             if (xaccTransCountSplits (trans_line->trans) == 2)
             {
-                osplit = xaccSplitGetOtherSplit (split);
+                tsplit = xaccSplitGetOtherSplit (split);
                 xaccSplitSetAmount (split, amount);
                 xaccSplitSetValue (split, gnc_numeric_neg (amount));
             }
@@ -319,23 +315,23 @@ void GncTxImport::adjust_balances (Account *account)
             /* This new transaction needs to be added to the balance offset. */
             balance_offset = gnc_numeric_add (balance_offset,
                                              amount,
-                                             xaccAccountGetCommoditySCU (account),
+                                             xaccAccountGetCommoditySCU (base_account),
                                              GNC_HOW_RND_ROUND_HALF_UP);
         }
     }
 
 }
 
-void GncTxImport::parse_line_to_trans (parse_line_t& orig_line)
+void GncTxImport::create_transaction (parse_line_t& parsed_line)
 {
     StrVec line;
     std::string error_message;
     std::shared_ptr<GncPreTrans> trans_props;
     std::shared_ptr<GncPreSplit> split_props;
-    std::tie(line, error_message, trans_props, split_props) = orig_line;
+    std::tie(line, error_message, trans_props, split_props) = parsed_line;
     error_message.clear();
 
-    /* Convert this import line into a map of transaction/split properties. */
+    /* Convert all tokens in this line into transaction/split properties. */
     auto col_types_it = column_types.cbegin();
     auto line_it = line.cbegin();
     for (col_types_it, line_it;
@@ -368,8 +364,8 @@ void GncTxImport::parse_line_to_trans (parse_line_t& orig_line)
     auto line_acct = split_props->get_account();
     if (!line_acct)
     {
-        if (home_account)
-            split_props->set_account(home_account);
+        if (base_account)
+            split_props->set_account(base_account);
         else
         {
             // Oops - the user didn't select an Account column *and* we didn't get a default value either!
@@ -384,17 +380,17 @@ void GncTxImport::parse_line_to_trans (parse_line_t& orig_line)
     /* If column parsing was successful, convert trans properties into a trans line. */
     try
     {
-        trans_properties_verify_essentials (orig_line);
+        trans_properties_verify_essentials (parsed_line);
 
         /* If all went well, add this transaction to the list. */
         /* We want to keep the transactions sorted by date in case we have
          * to calculate the transaction's amount based on the user provided balances.
          * The multimap should deal with this for us. */
-        auto trans_line = trans_properties_to_trans (orig_line);
-        if (trans_line)
+        auto draft_trans = trans_properties_to_trans (parsed_line);
+        if (draft_trans)
         {
-            auto trans_date = xaccTransGetDate (trans_line->trans);
-            transactions.insert (std::pair<time64, GncCsvTransLine*>(trans_date,trans_line));
+            auto trans_date = xaccTransGetDate (draft_trans->trans);
+            transactions.insert (std::pair<time64, DraftTransaction*>(trans_date,draft_trans));
         }
     }
     catch (const std::invalid_argument& e)
@@ -406,23 +402,22 @@ void GncTxImport::parse_line_to_trans (parse_line_t& orig_line)
 
 
 /** Creates a list of transactions from parsed data. Transactions that
- * could be created from rows are placed in transactions;
- * rows that fail are placed in error_lines. (Note: there
- * is no way for this function to "fail," i.e. it only returns 0, so
- * it may be changed to a void function in the future.)
- * @param parse_data Data that is being parsed
+ * could be created from rows are placed in transactions; Lines that couldn't
+ * be converted are marked with the failure reason. These can be redone in
+ * a subsequent run with redo_errors set to true.
  * @param account Account with which transactions are created
- * @param redo_errors TRUE to convert only error data, FALSE for all data
- * @return 0 on success, 1 on failure
+ * @param redo_errors true to convert only error data, false to convert all data
  */
-int GncTxImport::parse_to_trans (Account* account,
-                                     bool redo_errors)
+void GncTxImport::create_transactions (Account* account,
+                                       bool redo_errors)
 {
-    /* Free error_lines and transactions if they
-     * already exist. */
-    if (!redo_errors) /* If we're redoing errors, we save freeing until the end. */
+    /* If a full conversion is requested (as opposed to only
+     * attempting to convers the lines which had errors in the previous run)
+     * clear all errors and possibly already created transactions. */
+    if (!redo_errors)
     {
-        for (auto orig_line : orig_lines)
+        /* Clear error messages on full run */
+        for (auto orig_line : parsed_lines)
             std::get<1>(orig_line).clear();
 
         /* FIXME handle memory leak here!
@@ -433,23 +428,25 @@ int GncTxImport::parse_to_trans (Account* account,
     }
 
     /* compute start and end iterators based on user-set restrictions */
-    auto orig_lines_it = orig_lines.begin();
-    std::advance(orig_lines_it, start_row);
+    auto parsed_lines_it = parsed_lines.begin();
+    std::advance(parsed_lines_it, start_row);
 
-    auto orig_lines_max = orig_lines.begin();
-    if (end_row > orig_lines.size())
-        orig_lines_max = orig_lines.end();
+    auto parsed_lines_max = parsed_lines.begin();
+    if (end_row > parsed_lines.size())
+        parsed_lines_max = parsed_lines.end();
     else
-        std::advance(orig_lines_max, end_row);
+        std::advance(parsed_lines_max, end_row);
 
-    home_account = account;
+    base_account = account;
     auto odd_line = false;
     parse_errors = false;
-    for (orig_lines_it, odd_line;
-            orig_lines_it != orig_lines_max;
-            ++orig_lines_it, odd_line = !odd_line)
+
+    /* Iterate over all parsed lines */
+    for (parsed_lines_it, odd_line;
+            parsed_lines_it != parsed_lines_max;
+            ++parsed_lines_it, odd_line = !odd_line)
     {
-        auto orig_line = *orig_lines_it;
+        auto parsed_line = *parsed_lines_it;
 
         /* Skip current line if:
            1. only looking for lines with error AND no error on current line
@@ -457,13 +454,13 @@ int GncTxImport::parse_to_trans (Account* account,
            2. looking for all lines AND
               skip_rows is enabled AND
               current line is an odd line */
-        if ((redo_errors && std::get<1>(orig_line).empty()) ||
+        if ((redo_errors && std::get<1>(parsed_line).empty()) ||
            (!redo_errors && skip_rows && odd_line))
             continue;
 
         try
         {
-            parse_line_to_trans (orig_line);
+            create_transaction (parsed_line);
         }
         catch (const std::invalid_argument&)
         {
@@ -473,9 +470,7 @@ int GncTxImport::parse_to_trans (Account* account,
 
     if (std::find(column_types.begin(),column_types.end(), GncTransPropType::BALANCE) !=
         column_types.end()) // This is only used if we have one home account
-        adjust_balances (home_account);
-
-    return 0;
+        adjust_balances ();
 }
 
 
diff --git a/src/import-export/csv-imp/gnc-tx-import.hpp b/src/import-export/csv-imp/gnc-tx-import.hpp
index f9ab224..af0e195 100644
--- a/src/import-export/csv-imp/gnc-tx-import.hpp
+++ b/src/import-export/csv-imp/gnc-tx-import.hpp
@@ -44,20 +44,13 @@ extern "C" {
 #include "gnc-trans-props.hpp"
 
 
-/* TODO We now sort transactions by date, not line number, so we
- * should probably get rid of this struct and uses of it. */
-
-/** Struct pairing a transaction with a line number. This struct is
- * used to keep the transactions in order. When rows are separated
- * into "valid" and "error" lists (in case some of the rows have cells
- * that are unparseable), we want the user to still be able to
- * "correct" the error list. If we keep the line numbers of valid
- * transactions, we can then put transactions created from the newly
- * corrected rows into the right places. */
-struct GncCsvTransLine
+/** This struct stores a possibly incomplete transaction
+ *  optionally together with its intended balance in case
+ *  the user had selected a balance column. */
+struct DraftTransaction
 {
     Transaction* trans;
-    gnc_numeric balance;  /**< The (supposed) balance after this transaction takes place */
+    gnc_numeric balance;  /**< The expected balance after this transaction takes place */
     bool balance_set;     /**< true if balance has been set from user data, false otherwise */
 };
 
@@ -102,14 +95,21 @@ public:
     void load_file (const std::string& filename);
     void convert_encoding (const std::string& encoding);
 
-    void parse (bool guessColTypes);
-    int parse_to_trans (Account* account, bool redo_errors);
+    void tokenize (bool guessColTypes);
+
+    /** This function will attempt to convert all tokenized lines into
+     *  transactions using the column types the user has set.
+     */
+    void create_transactions (Account* account, bool redo_errors);
     bool check_for_column_type (GncTransPropType type);
 
     std::unique_ptr<GncTokenizer> tokenizer;    /**< Will handle file loading/encoding conversion/splitting into fields */
-    std::vector<parse_line_t> orig_lines;      /**< file_str parsed into a two-dimensional array of strings */
-    std::vector<GncTransPropType> column_types;       /**< Vector of values from the GncCsvColumnType enumeration */
-    std::multimap <time64, GncCsvTransLine*> transactions;        /**< List of GncCsvTransLine*s created using orig_lines and column_types */
+    std::vector<parse_line_t> parsed_lines;     /**< source file parsed into a two-dimensional array of strings.
+                                                     Per line also holds possible error messages and objects with extracted transaction
+                                                     and split properties. */
+    std::vector<GncTransPropType> column_types; /**< Vector of values from the GncCsvColumnType enumeration */
+    std::multimap <time64, <DraftTransaction*> transactions;  /**< map of transaction objects created
+                                                     from parsed_lines and column_types, ordered by date */
     int date_format;            /**< The format of the text in the date columns from date_format_internal. */
     guint start_row;            /**< The start row to generate transactions from. */
     guint end_row;              /**< The end row to generate transactions from. */
@@ -118,11 +118,25 @@ public:
     bool parse_errors;          /**< Indicates whether the last parse_to_trans run had any errors */
 
 private:
-    void parse_line_to_trans (parse_line_t& orig_line);
-    void adjust_balances (Account *account);
+    /** A helper function used by create_transactions. It will attempt
+     *  to convert a single tokenized line into a transaction using
+     *  the column types the user has set.
+     */
+    void create_transaction (parse_line_t& parsed_line);
+
+    /** A helper function used by create_transactions. If the input data has
+     *  a balance column (an no deposit and withdrawal columns)
+     *  it will iterate over all created transactions
+     *  to set the split amount(s) based on the desired balance for that line.
+     */
+    void adjust_balances (void);
 
     GncImpFileFormat file_fmt = GncImpFileFormat::UNKNOWN;
-    Account *home_account = NULL;
+
+    /* The variables below are only used during while creating
+     * transactions. They keep state information during the conversion.
+     */
+    Account *base_account = nullptr;
 };
 
 

commit 90e5e96f8fbc50bf702b90cd524176c53ff5d8c7
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Tue Nov 29 12:32:02 2016 +0100

    Rework the intermediate properties storage
    
    The overly complex templated class hierarchy is replaced
    with two simple classes. One to keep the discovered transaction
    properties and one for the discovered split properties. Make
    both classes responsible for verifying it's state and creating
    the necessary objects.

diff --git a/src/import-export/csv-imp/CMakeLists.txt b/src/import-export/csv-imp/CMakeLists.txt
index 72143a2..4b5fe58 100644
--- a/src/import-export/csv-imp/CMakeLists.txt
+++ b/src/import-export/csv-imp/CMakeLists.txt
@@ -17,6 +17,7 @@ SET(csv_import_SOURCES
   gnc-dummy-tokenizer.cpp
   gnc-fw-tokenizer.cpp
   gnc-tokenizer.cpp
+  gnc-trans-props.cpp
   gnc-tx-import.cpp
   ${CMAKE_SOURCE_DIR}/lib/stf/stf-parse.c
   ${CMAKE_SOURCE_DIR}/lib/goffice/go-charmap-sel.c
@@ -42,6 +43,7 @@ SET(csv_import_noinst_HEADERS
   gnc-dummy-tokenizer.hpp
   gnc-fw-tokenizer.hpp
   gnc-tokenizer.hpp
+  gnc-trans-props.hpp
   gnc-tx-import.hpp
   ${CMAKE_SOURCE_DIR}/lib/stf/stf-parse.h
   ${CMAKE_SOURCE_DIR}/lib/goffice/go-charmap-sel.h
diff --git a/src/import-export/csv-imp/Makefile.am b/src/import-export/csv-imp/Makefile.am
index e3c4775..0014c88 100644
--- a/src/import-export/csv-imp/Makefile.am
+++ b/src/import-export/csv-imp/Makefile.am
@@ -18,6 +18,7 @@ libgncmod_csv_import_la_SOURCES = \
   gnc-fw-tokenizer.cpp \
   gnc-tokenizer.cpp \
   gnc-tx-import.cpp \
+  gnc-trans-props.cpp \
   gnc-csv-trans-settings.c
 
 noinst_HEADERS = \
@@ -35,6 +36,7 @@ noinst_HEADERS = \
   gnc-fw-tokenizer.hpp \
   gnc-tokenizer.hpp \
   gnc-tx-import.hpp \
+  gnc-trans-props.hpp \
   gnc-csv-trans-settings.h
 
 libgncmod_csv_import_la_LDFLAGS = -avoid-version
diff --git a/src/import-export/csv-imp/gnc-trans-props.cpp b/src/import-export/csv-imp/gnc-trans-props.cpp
new file mode 100644
index 0000000..e85f699
--- /dev/null
+++ b/src/import-export/csv-imp/gnc-trans-props.cpp
@@ -0,0 +1,419 @@
+/********************************************************************\
+ * gnc-csv-imp-trans.cpp - import transactions from csv files       *
+ *                                                                  *
+ * 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 2 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, contact:                        *
+ *                                                                  *
+ * Free Software Foundation           Voice:  +1-617-542-5942       *
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
+ *                                                                  *
+\********************************************************************/
+
+extern "C" {
+#include <platform.h>
+#if PLATFORM(WINDOWS)
+#include <windows.h>
+#endif
+
+#include <glib/gi18n.h>
+
+#include "engine-helpers.h"
+#include "gnc-csv-account-map.h"
+#include "gnc-ui-util.h"
+#include "Account.h"
+#include "Transaction.h"
+
+}
+
+#include <string>
+#include <boost/regex.hpp>
+#include <boost/regex/icu.hpp>
+#include "gnc-trans-props.hpp"
+
+G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT;
+
+/* This map contains a set of strings representing the different column types. */
+std::map<GncTransPropType, const char*> gnc_csv_col_type_strs = {
+        { GncTransPropType::NONE, N_("None") },
+        { GncTransPropType::DATE, N_("Date") },
+        { GncTransPropType::NUM, N_("Num") },
+        { GncTransPropType::DESCRIPTION, N_("Description") },
+        { GncTransPropType::NOTES, N_("Notes") },
+        { GncTransPropType::ACCOUNT, N_("Account") },
+        { GncTransPropType::DEPOSIT, N_("Deposit") },
+        { GncTransPropType::WITHDRAWAL, N_("Withdrawal") },
+        { GncTransPropType::BALANCE, N_("Balance") },
+        { GncTransPropType::MEMO, N_("Memo") },
+        { GncTransPropType::OACCOUNT, N_("Other Account") },
+        { GncTransPropType::OMEMO, N_("Other Memo") }
+};
+
+/* Regular expressions used to parse dates per date format */
+const char* date_regex[] = {
+                             "(?:"                                   // either y-m-d
+                                 "(?<YEAR>[0-9]+)[-/.' ]+"
+                                 "(?<MONTH>[0-9]+)[-/.' ]+"
+                                 "(?<DAY>[0-9]+)"
+                             "|"                                     // or CCYYMMDD
+                                 "(?<YEAR>[0-9]{4})"
+                                 "(?<MONTH>[0-9]{2})"
+                                 "(?<DAY>[0-9]{2})"
+                             ")",
+
+                             "(?:"                                   // either d-m-y
+                                 "(?<DAY>[0-9]+)[-/.' ]+"
+                                 "(?<MONTH>[0-9]+)[-/.' ]+"
+                                 "(?<YEAR>[0-9]+)"
+                             "|"                                     // or DDMMCCYY
+                                 "(?<DAY>[0-9]{2})"
+                                 "(?<MONTH>[0-9]{2})"
+                                 "(?<YEAR>[0-9]{4})"
+                             ")",
+
+                             "(?:"                                   // either m-d-y
+                                 "(?<MONTH>[0-9]+)[-/.' ]+"
+                                 "(?<DAY>[0-9]+)[-/.' ]+"
+                                 "(?<YEAR>[0-9]+)"
+                             "|"                                     // or MMDDCCYY
+                                 "(?<MONTH>[0-9]{2})"
+                                 "(?<DAY>[0-9]{2})"
+                                 "(?<YEAR>[0-9]{4})"
+                             ")",
+
+                             "(?:"                                   // either d-m(-y)
+                                 "(?<DAY>[0-9]+)[-/.' ]+"
+                                 "(?<MONTH>[0-9]+)(?:[-/.' ]+"
+                                 "(?<YEAR>[0-9]+))?"
+                             "|"                                     // or DDMM(CCYY)
+                                 "(?<DAY>[0-9]{2})"
+                                 "(?<MONTH>[0-9]{2})"
+                                 "(?<YEAR>[0-9]+)?"
+                             ")",
+
+                             "(?:"                                   // either m-d(-y)
+                                 "(?<MONTH>[0-9]+)[-/.' ]+"
+                                 "(?<DAY>[0-9]+)(?:[-/.' ]+"
+                                 "(?<YEAR>[0-9]+))?"
+                             "|"                                     // or MMDD(CCYY)
+                                 "(?<MONTH>[0-9]{2})"
+                                 "(?<DAY>[0-9]{2})"
+                                 "(?<YEAR>[0-9]+)?"
+                             ")",
+};
+
+/** Parses a string into a date, given a format. This function
+ * requires only knowing the order in which the year, month and day
+ * appear. For example, 01-02-2003 will be parsed the same way as
+ * 01/02/2003.
+ * @param date_str The string containing a date being parsed
+ * @param format An index specifying a format in date_format_user
+ * @exception std::invalid_argument if the string can't be parsed into a date.
+ * @return The parsed value of date_str on success, throws on failure
+ */
+
+time64 parse_date (const std::string &date_str, int format)
+{
+    boost::regex r(date_regex[format]);
+    boost::smatch what;
+    if(!boost::regex_search(date_str, what, r))
+        throw std::invalid_argument ("String doesn't appear to be formatted as a date.");  // regex didn't find a match
+
+    // Attention: different behavior from 2.6.x series !
+    // If date format without year was selected, the match
+    // should NOT have found a year.
+    if ((format >= 3) && (what.length("YEAR") != 0))
+        throw std::invalid_argument ("String appears to contain a year while the selected format forbids this.");
+
+    auto day = std::stoi (what.str("DAY"));
+    auto month = std::stoi (what.str("MONTH"));
+
+    int year;
+    if (format < 3)
+    {
+        /* The input dates have a year, so use that one */
+        year = std::stoi (what.str("YEAR"));
+
+        /* Handle two-digit years. */
+        if (year < 100)
+        {
+            /* We allow two-digit years in the range 1969 - 2068. */
+            if (year < 69)
+                year += 2000;
+            else
+                year += 1900;
+        }
+    }
+    else
+    {
+        /* The input dates don't have a year, so work with today's year.
+         */
+        gnc_timespec2dmy(timespec_now(), nullptr, nullptr, &year);
+    }
+
+    auto ts = gnc_dmy2timespec_neutral(day, month, year);
+    return ts.tv_sec;
+}
+
+
+/** Convert str into a gnc_numeric using the user-specified (import) currency format.
+ * @param str The string to be parsed
+ * @param currency_format The currency format to use.
+ * @return a gnc_numeric on success, boost::none on failure
+ */
+static boost::optional<gnc_numeric> convert_amount_col_str (const std::string &str, int currency_format)
+{
+    /* If a cell is empty or just spaces return invalid amount */
+    if(!boost::regex_search(str, boost::regex("[0-9]")))
+        throw std::invalid_argument ("String doesn't appear to contain a valid number.");
+
+    auto expr = boost::make_u32regex("[[:Sc:]]");
+    std::string str_no_symbols = boost::u32regex_replace(str, expr, "");
+
+    /* Convert based on user chosen currency format */
+    gnc_numeric val;
+    char *endptr;
+    switch (currency_format)
+    {
+    case 0:
+        /* Currency locale */
+        if (!(xaccParseAmount (str_no_symbols.c_str(), TRUE, &val, &endptr)))
+            throw std::invalid_argument ("String can't be parsed into a number using the selected currency format.");
+        break;
+    case 1:
+        /* Currency decimal period */
+        if (!(xaccParseAmountExtended (str_no_symbols.c_str(), TRUE, '-', '.', ',', "\003\003", "$+", &val, &endptr)))
+            throw std::invalid_argument ("String can't be parsed into a number using the selected currency format.");
+        break;
+    case 2:
+        /* Currency decimal comma */
+        if (!(xaccParseAmountExtended (str_no_symbols.c_str(), TRUE, '-', ',', '.', "\003\003", "$+", &val, &endptr)))
+            throw std::invalid_argument ("String can't be parsed into a number using the selected currency format.");
+        break;
+    }
+
+    return val;
+}
+
+
+void GncPreTrans::set_property (GncTransPropType prop_type, const std::string& prop_value_str, int date_format)
+{
+    switch (prop_type)
+    {
+        case GncTransPropType::DATE:
+            m_date = parse_date (prop_value_str.c_str(), date_format); // Throws if parsing fails
+            break;
+
+        case GncTransPropType::DESCRIPTION:
+            if (!prop_value_str.empty())
+                m_desc = prop_value_str;
+            else
+                m_desc = boost::none;
+            break;
+
+        case GncTransPropType::NOTES:
+            if (!prop_value_str.empty())
+                m_notes = prop_value_str;
+            else
+                m_notes = boost::none;
+            break;
+
+        default:
+            /* Issue a warning for all other prop_types. */
+            PWARN ("%d is an invalid property for a transaction", static_cast<int>(prop_type));
+            break;
+    }
+
+}
+
+std::string GncPreTrans::verify_essentials (void)
+{
+    /* Make sure this transaction has the minimum required set of properties defined */
+    if (!m_date)
+        return N_("No date column.");
+    else
+        return std::string();
+}
+
+Transaction* GncPreTrans::create_trans (QofBook* book, gnc_commodity* currency)
+{
+    if (created)
+        return nullptr;
+
+    auto trans = xaccMallocTransaction (book);
+    xaccTransBeginEdit (trans);
+    xaccTransSetCurrency (trans, currency);
+
+    xaccTransSetDatePostedSecsNormalized (trans, *m_date);
+
+    if (m_desc)
+        xaccTransSetDescription (trans, m_desc->c_str());
+
+    if (m_notes)
+        xaccTransSetNotes (trans, m_notes->c_str());
+
+    created = true;
+    return trans;
+}
+
+
+void GncPreSplit::set_property (GncTransPropType prop_type, const std::string& prop_value_str, int currency_format)
+{
+    Account *acct = nullptr;
+    switch (prop_type)
+    {
+        case GncTransPropType::ACCOUNT:
+            acct = gnc_csv_account_map_search (prop_value_str.c_str());
+            if (acct)
+                m_account = acct;
+            else
+                throw std::invalid_argument ("String can't be mapped back to an account.");
+            break;
+
+        case GncTransPropType::OACCOUNT:
+            acct = gnc_csv_account_map_search (prop_value_str.c_str());
+            if (acct)
+                m_oaccount = acct;
+            else
+                throw std::invalid_argument ("String can't be mapped back to an account.");
+            break;
+
+        case GncTransPropType::MEMO:
+            if (!prop_value_str.empty())
+                m_memo = prop_value_str;
+            else
+                m_memo = boost::none;
+            break;
+
+        case GncTransPropType::OMEMO:
+            if (!prop_value_str.empty())
+                m_omemo = prop_value_str;
+            else
+                m_omemo = boost::none;
+            break;
+
+        case GncTransPropType::NUM:
+            if (!prop_value_str.empty())
+                m_num = prop_value_str;
+            else
+                m_num = boost::none;
+            break;
+
+        case GncTransPropType::BALANCE:
+            m_balance = convert_amount_col_str (prop_value_str, currency_format); // Will throw if parsing fails
+            break;
+        case GncTransPropType::DEPOSIT:
+            m_deposit = convert_amount_col_str (prop_value_str, currency_format); // Will throw if parsing fails
+            break;
+        case GncTransPropType::WITHDRAWAL:
+            m_withdrawal = convert_amount_col_str (prop_value_str, currency_format); // Will throw if parsing fails
+            break;
+
+        default:
+            /* Issue a warning for all other prop_types. */
+            PWARN ("%d is an invalid property for a split", static_cast<int>(prop_type));
+            break;
+    }
+
+}
+
+std::string GncPreSplit::verify_essentials (void)
+{
+    /* Make sure this split has the minimum required set of properties defined. */
+    if (!m_deposit && !m_withdrawal && !m_balance)
+        return N_("No balance, deposit, or withdrawal column.");
+    else
+        return std::string();
+}
+
+/** Adds a split to a transaction.
+ * @param trans The transaction to add a split to
+ * @param account The account used for the split
+ * @param book The book where the split should be stored
+ * @param amount The amount of the split
+ */
+static void trans_add_split (Transaction* trans, Account* account, QofBook* book,
+                            gnc_numeric amount, const std::string& num, const std::string& memo)
+{
+    auto split = xaccMallocSplit (book);
+    xaccSplitSetAccount (split, account);
+    xaccSplitSetParent (split, trans);
+    xaccSplitSetAmount (split, amount);
+    xaccSplitSetValue (split, amount);
+    if (!memo.empty())
+        xaccSplitSetMemo (split, memo.c_str());
+    /* set tran-num and/or split-action per book option
+     * note this function does nothing if num is NULL also */
+    if (!num.empty())
+        gnc_set_num_action (trans, split, num.c_str(), NULL);
+}
+
+boost::optional<gnc_numeric> GncPreSplit::create_split (Transaction* trans)
+{
+    if (created)
+        return boost::none;
+
+    auto book = xaccTransGetBook (trans);
+    std::string num;
+    std::string memo;
+    std::string omemo;
+    Account *account = nullptr;
+    Account *oaccount = nullptr;
+    bool amount_set = false;
+    gnc_numeric deposit = { 0, 1 };
+    gnc_numeric withdrawal = { 0, 1 };
+    gnc_numeric amount = { 0, 1 };
+
+    if (m_account)
+        account = *m_account;
+    if (m_oaccount)
+        oaccount = *m_oaccount;
+    if (m_memo)
+        memo = *m_memo;
+    if (m_omemo)
+        omemo = *m_omemo;
+    if (m_num)
+        num = *m_num;
+    if (m_deposit)
+    {
+        deposit = *m_deposit;
+        amount_set = true;
+    }
+    if (m_withdrawal)
+    {
+        withdrawal = *m_withdrawal;
+        amount_set = true;
+    }
+    if (amount_set)
+        amount = gnc_numeric_add (deposit, withdrawal,
+                xaccAccountGetCommoditySCU (account),
+                GNC_HOW_RND_ROUND_HALF_UP);
+
+    /* Add a split with the cumulative amount value. */
+    trans_add_split (trans, account, book, amount, num, memo);
+
+    if (oaccount)
+        /* Note: the current importer assumes at most 2 splits. This means the second split amount
+         * will be the negative of the the first split amount. We also only set the num field once,
+         * for the first split.
+         */
+        trans_add_split (trans, oaccount, book, gnc_numeric_neg(amount), "", omemo);
+
+
+    created = true;
+
+    if (amount_set)
+        return boost::none;
+    else
+        return m_balance;
+}
diff --git a/src/import-export/csv-imp/gnc-trans-props.hpp b/src/import-export/csv-imp/gnc-trans-props.hpp
new file mode 100644
index 0000000..7b1552d
--- /dev/null
+++ b/src/import-export/csv-imp/gnc-trans-props.hpp
@@ -0,0 +1,115 @@
+/********************************************************************\
+ * gnc-trans-props.hpp - encapsulate transaction properties for use *
+ *                       in the csv importer                        *
+ *                                                                  *
+ * 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 2 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, contact:                        *
+ *                                                                  *
+ * Free Software Foundation           Voice:  +1-617-542-5942       *
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
+ *                                                                  *
+\********************************************************************/
+
+#ifndef GNC_TRANS_PROPS_HPP
+#define GNC_TRANS_PROPS_HPP
+
+extern "C" {
+#include <platform.h>
+#if PLATFORM(WINDOWS)
+#include <windows.h>
+#endif
+
+#include <glib/gi18n.h>
+}
+
+#include <string>
+#include <boost/optional.hpp>
+
+/** Enumeration for column types. These are the different types of
+ * columns that can exist in a CSV/Fixed-Width file. There should be
+ * no two columns with the same type except for the GncTransPropType::NONE
+ * type. */
+enum class GncTransPropType {
+    NONE,
+    DATE,
+    DESCRIPTION,
+    NOTES,
+    TRANS_PROPS = NOTES,
+
+    // num is strictly speaking a trans prop and not a split prop
+    // however due to the num/action swap user option, it can only be
+    // set while creating splits...
+    NUM,
+
+    ACCOUNT,
+    DEPOSIT,
+    WITHDRAWAL,
+    BALANCE,
+    MEMO,
+    OACCOUNT,
+    OMEMO,
+    SPLIT_PROPS = OMEMO
+};
+
+/** Maps all column types to a string representation.
+ *  The actual definition is in gnc-csv-imp-trans.cpp.
+ *  Attention: that definition should be adjusted for any
+ *  changes to enum class GncTransPropType ! */
+extern std::map<GncTransPropType, const char*> gnc_csv_col_type_strs;
+
+time64 parse_date (const std::string &date_str, int format);
+
+struct GncPreTrans
+{
+public:
+    void set_property (GncTransPropType prop_type, const std::string& prop_value_str, int date_format = 0);
+    std::string verify_essentials (void);
+    Transaction *create_trans (QofBook* book, gnc_commodity* currency);
+
+private:
+    boost::optional<time64> m_date;
+    boost::optional<std::string> m_desc;
+    boost::optional<std::string> m_notes;
+    bool created = false;
+};
+
+struct GncPreSplit
+{
+public:
+    void set_property (GncTransPropType prop_type, const std::string& prop_value_str, int currency_format = 0);
+    std::string verify_essentials (void);
+    boost::optional<gnc_numeric> create_split(Transaction* trans);
+
+    Account* get_account () { if (m_account) return *m_account; else return nullptr; }
+    void set_account (Account* acct) { if (acct) m_account = acct; else m_account = boost::none; }
+
+private:
+    boost::optional<Account*> m_account;
+    boost::optional<gnc_numeric> m_deposit;
+    boost::optional<gnc_numeric> m_withdrawal;
+    boost::optional<gnc_numeric> m_balance;
+    boost::optional<std::string> m_memo;
+    boost::optional<Account*> m_oaccount;
+    boost::optional<std::string> m_omemo;
+
+    // Strictly speaking num is a transaction property
+    // However due to the option to swap num and action fields
+    // This can only be set when splits are created
+    boost::optional<std::string> m_num;
+    bool created = false;
+};
+
+
+
+#endif
diff --git a/src/import-export/csv-imp/gnc-tx-import.cpp b/src/import-export/csv-imp/gnc-tx-import.cpp
index de0428e..248f245 100644
--- a/src/import-export/csv-imp/gnc-tx-import.cpp
+++ b/src/import-export/csv-imp/gnc-tx-import.cpp
@@ -30,26 +30,13 @@ extern "C" {
 #endif
 
 #include <glib/gi18n.h>
-
-#include "gnc-csv-account-map.h"
-#include "gnc-ui-util.h"
-#include "engine-helpers.h"
-
-#include <string.h>
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#include <math.h>
 }
 
-#include <algorithm>
 #include <boost/regex.hpp>
 #include <boost/regex/icu.hpp>
 
 #include "gnc-tx-import.hpp"
+#include "gnc-trans-props.hpp"
 #include "gnc-csv-tokenizer.hpp"
 #include "gnc-fw-tokenizer.hpp"
 
@@ -63,131 +50,12 @@ G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT;
 //                                   N_("m-d")
 //                                  };
 //
-/* Regular expressions used to parse dates per date format */
-const char* date_regex[] = {
-                             "(?:"                                   // either y-m-d
-                                 "(?<YEAR>[0-9]+)[-/.' ]+"
-                                 "(?<MONTH>[0-9]+)[-/.' ]+"
-                                 "(?<DAY>[0-9]+)"
-                             "|"                                     // or CCYYMMDD
-                                 "(?<YEAR>[0-9]{4})"
-                                 "(?<MONTH>[0-9]{2})"
-                                 "(?<DAY>[0-9]{2})"
-                             ")",
-
-                             "(?:"                                   // either d-m-y
-                                 "(?<DAY>[0-9]+)[-/.' ]+"
-                                 "(?<MONTH>[0-9]+)[-/.' ]+"
-                                 "(?<YEAR>[0-9]+)"
-                             "|"                                     // or DDMMCCYY
-                                 "(?<DAY>[0-9]{2})"
-                                 "(?<MONTH>[0-9]{2})"
-                                 "(?<YEAR>[0-9]{4})"
-                             ")",
-
-                             "(?:"                                   // either m-d-y
-                                 "(?<MONTH>[0-9]+)[-/.' ]+"
-                                 "(?<DAY>[0-9]+)[-/.' ]+"
-                                 "(?<YEAR>[0-9]+)"
-                             "|"                                     // or MMDDCCYY
-                                 "(?<MONTH>[0-9]{2})"
-                                 "(?<DAY>[0-9]{2})"
-                                 "(?<YEAR>[0-9]{4})"
-                             ")",
-
-                             "(?:"                                   // either d-m(-y)
-                                 "(?<DAY>[0-9]+)[-/.' ]+"
-                                 "(?<MONTH>[0-9]+)(?:[-/.' ]+"
-                                 "(?<YEAR>[0-9]+))?"
-                             "|"                                     // or DDMM(CCYY)
-                                 "(?<DAY>[0-9]{2})"
-                                 "(?<MONTH>[0-9]{2})"
-                                 "(?<YEAR>[0-9]+)?"
-                             ")",
-
-                             "(?:"                                   // either m-d(-y)
-                                 "(?<MONTH>[0-9]+)[-/.' ]+"
-                                 "(?<DAY>[0-9]+)(?:[-/.' ]+"
-                                 "(?<YEAR>[0-9]+))?"
-                             "|"                                     // or MMDD(CCYY)
-                                 "(?<MONTH>[0-9]{2})"
-                                 "(?<DAY>[0-9]{2})"
-                                 "(?<YEAR>[0-9]+)?"
-                             ")",
-};
 //const int num_currency_formats = 3;
 //const gchar* currency_format_user[] = {N_("Locale"),
 //                                       N_("Period: 123,456.78"),
 //                                       N_("Comma: 123.456,78")
 //                                      };
 //
-/* This map contains a set of strings representing the different column types. */
-std::map<GncTransPropType, const char*> gnc_csv_col_type_strs = {
-        { GncTransPropType::NONE, N_("None") },
-        { GncTransPropType::DATE, N_("Date") },
-        { GncTransPropType::NUM, N_("Num") },
-        { GncTransPropType::DESCRIPTION, N_("Description") },
-        { GncTransPropType::NOTES, N_("Notes") },
-        { GncTransPropType::ACCOUNT, N_("Account") },
-        { GncTransPropType::DEPOSIT, N_("Deposit") },
-        { GncTransPropType::WITHDRAWAL, N_("Withdrawal") },
-        { GncTransPropType::BALANCE, N_("Balance") },
-        { GncTransPropType::MEMO, N_("Memo") },
-        { GncTransPropType::OACCOUNT, N_("Other Account") },
-        { GncTransPropType::OMEMO, N_("Other Memo") }
-};
-
-/** Parses a string into a date, given a format. This function
- * requires only knowing the order in which the year, month and day
- * appear. For example, 01-02-2003 will be parsed the same way as
- * 01/02/2003.
- * @param date_str The string containing a date being parsed
- * @param format An index specifying a format in date_format_user
- * @exception std::invalid_argument if the string can't be parsed into a date.
- * @return The parsed value of date_str on success, throws on failure
- */
-time64 parse_date (const std::string &date_str, int format)
-{
-    boost::regex r(date_regex[format]);
-    boost::smatch what;
-    if(!boost::regex_search(date_str, what, r))
-        throw std::invalid_argument ("String doesn't appear to be formatted as a date.");  // regex didn't find a match
-
-    // Attention: different behavior from 2.6.x series !
-    // If date format without year was selected, the match
-    // should NOT have found a year.
-    if ((format >= 3) && (what.length("YEAR") != 0))
-        throw std::invalid_argument ("String appears to contain a year while the selected format forbids this.");
-
-    auto day = std::stoi (what.str("DAY"));
-    auto month = std::stoi (what.str("MONTH"));
-
-    int year;
-    if (format < 3)
-    {
-        /* The input dates have a year, so use that one */
-        year = std::stoi (what.str("YEAR"));
-
-        /* Handle two-digit years. */
-        if (year < 100)
-        {
-            /* We allow two-digit years in the range 1969 - 2068. */
-            if (year < 69)
-                year += 2000;
-            else
-                year += 1900;
-        }
-    }
-    else
-    {
-        /* The input dates don't have a year, so work with today's year.
-         */
-        gnc_timespec2dmy(timespec_now(), nullptr, nullptr, &year);
-    }
-
-    auto ts = gnc_dmy2timespec_neutral(day, month, year);
-    return ts.tv_sec;
-}
 
 /** Constructor for GncTxImport.
  * @return Pointer to a new GncCSvParseData
@@ -337,368 +205,73 @@ void GncTxImport::parse (bool guessColTypes)
 }
 
 
-/** Convert str into a time64 using the user-specified (import) date format.
- * @param str The string to be parsed
- * @param date_format The date format to use.
- * @return a pointer to a time64 on success, nullptr on failure
- */
-static time64* convert_date_col_str (const std::string &str, int date_format)
-{
-    try
-    {
-        auto parsed_date = parse_date (str.c_str(), date_format);
-        auto mydate = new time64;
-        *mydate = parsed_date;
-        return mydate;
-    }
-    catch (std::invalid_argument)
-    {
-        return nullptr;
-    }
-}
-
-
-/** Convert str into a gnc_numeric using the user-specified (import) currency format.
- * @param str The string to be parsed
- * @param currency_format The currency format to use.
- * @return a pointer to a gnc_numeric on success, nullptr on failure
- */
-static gnc_numeric* convert_amount_col_str (const std::string &str, int currency_format)
-{
-    /* If a cell is empty or just spaces return 0 as amount */
-    if(!boost::regex_search(str, boost::regex("[0-9]")))
-        return nullptr;
-
-    auto expr = boost::make_u32regex("[[:Sc:]]");
-    std::string str_no_symbols = boost::u32regex_replace(str, expr, "");
-
-    /* Convert based on user chosen currency format */
-    gnc_numeric val;
-    char *endptr;
-    switch (currency_format)
-    {
-    case 0:
-        /* Currency locale */
-        if (!(xaccParseAmount (str_no_symbols.c_str(), TRUE, &val, &endptr)))
-            return nullptr;
-        break;
-    case 1:
-        /* Currency decimal period */
-        if (!(xaccParseAmountExtended (str_no_symbols.c_str(), TRUE, '-', '.', ',', "\003\003", "$+", &val, &endptr)))
-            return nullptr;
-        break;
-    case 2:
-        /* Currency decimal comma */
-        if (!(xaccParseAmountExtended (str_no_symbols.c_str(), TRUE, '-', ',', '.', "\003\003", "$+", &val, &endptr)))
-            return nullptr;
-        break;
-    }
-
-    auto amount = new gnc_numeric;
-    *amount = val;
-    return amount;
-}
-
-/* Define a class hierarchy to temporarily store transaction/split properties
- * found in one import line. There is a generic parent class and an implementation
- * template class. This template class is further specialized for each data type
- * we support (currently time64, Account, string and gnc_numeric).
- */
-struct GncTransProperty
-{
-    virtual ~GncTransProperty()
-      //Remove pure designation.
-        {}
-    bool m_valid = false;
-};
-
-template<class T>
-struct GncTransPropImpl
-: public GncTransProperty
-{
-public:
-    ~GncTransPropImpl(){};
-    GncTransPropImpl(const std::string& val, int fmt)
-    {
-        m_valid = false;
-    };
-
-    static GncTransProperty* make_new(const std::string& val,int fmt = 0)
-        { return nullptr; }
-
-    T m_value;
-};
-
-template<>
-struct GncTransPropImpl<time64*>
-: public GncTransProperty
-{
-    GncTransPropImpl(const std::string& val, int fmt)
-    {
-        m_value = convert_date_col_str (val, fmt);
-        m_valid = (m_value != nullptr);
-    }
-    ~GncTransPropImpl()
-        { if (m_value) delete m_value; }
-
-    static std::shared_ptr<GncTransProperty> make_new(const std::string& val,int fmt)
-    { return std::shared_ptr<GncTransProperty>(new GncTransPropImpl<time64*>(val, fmt)); }
-
-    time64* m_value;
-};
-
-
-template<>
-struct GncTransPropImpl<std::string*>
-: public GncTransProperty
-{
-    GncTransPropImpl(const std::string& val, int fmt = 0)
-    {
-        m_value = new std::string(val);
-        m_valid = (m_value != nullptr);
-    }
-    ~GncTransPropImpl()
-        { if (m_value) delete m_value; }
-
-    static std::shared_ptr<GncTransProperty> make_new(const std::string& val,int fmt = 0)
-        { return std::shared_ptr<GncTransProperty>(new GncTransPropImpl<std::string*>(val)); } /* Note fmt is not used for strings */
-
-    std::string* m_value;
-};
-
-template<>
-struct GncTransPropImpl<Account *>
-: public GncTransProperty
-{
-    GncTransPropImpl(const std::string& val, int fmt = 0)
-    {
-        m_value = gnc_csv_account_map_search (val.c_str());
-        m_valid = (m_value != nullptr);
-    }
-    GncTransPropImpl(Account* val)
-        { m_value = val; }
-    ~GncTransPropImpl(){};
-
-    static std::shared_ptr<GncTransProperty> make_new(const std::string& val,int fmt = 0)
-        { return std::shared_ptr<GncTransProperty>(new GncTransPropImpl<Account*>(val)); } /* Note fmt is not used in for accounts */
-
-    Account * m_value;
-};
-
-template<>
-struct GncTransPropImpl<gnc_numeric *>
-: public GncTransProperty
-{
-    GncTransPropImpl(const std::string& val, int fmt)
-    {
-        m_value = convert_amount_col_str (val, fmt);
-        m_valid = (m_value != nullptr);
-    }
-    ~GncTransPropImpl()
-        { if (m_value) delete m_value; }
-
-    static std::shared_ptr<GncTransProperty> make_new(const std::string& val,int fmt)
-    { return std::shared_ptr<GncTransProperty>(new GncTransPropImpl<gnc_numeric*>(val, fmt)); }
-
-    gnc_numeric * m_value;
-};
-
-/** Adds a split to a transaction.
- * @param trans The transaction to add a split to
- * @param account The account used for the split
- * @param book The book where the split should be stored
- * @param amount The amount of the split
- */
-static void trans_add_split (Transaction* trans, Account* account, QofBook* book,
-                            gnc_numeric amount, const std::string& num, const std::string& memo)
-{
-    auto split = xaccMallocSplit (book);
-    xaccSplitSetAccount (split, account);
-    xaccSplitSetParent (split, trans);
-    xaccSplitSetAmount (split, amount);
-    xaccSplitSetValue (split, amount);
-    if (!memo.empty())
-        xaccSplitSetMemo (split, memo.c_str());
-    /* set tran-num and/or split-action per book option
-     * note this function does nothing if num is NULL also */
-    if (!num.empty())
-        gnc_set_num_action (trans, split, num.c_str(), NULL);
-}
-
-
-/* Shorthand aliases for the container to keep track of property types (a map)
- * and its iterator (a pair)
- */
-using prop_pair_t = std::pair<GncTransPropType, std::shared_ptr<GncTransProperty>>;
-
-/** Tests a TransPropertyList for having enough essential properties.
+/** Checks whether the parsed line contains all essential properties.
  * Essential properties are
  * - "Date"
  * - at least one of "Balance", "Deposit", or "Withdrawal"
  * - "Account"
  * Note account isn't checked for here as this has been done before
- * @param list The list we are checking
- * @param error Contains an error message on failure
- * @return true if there are enough essentials; false otherwise
+ * @param parse_line The line we are checking
+ * @exception std::invalid_argument in an essential property is missing
  */
-static bool trans_properties_verify_essentials (prop_map_t& trans_props, gchar** error)
+static void trans_properties_verify_essentials (parse_line_t& orig_line)
 {
-    /* Make sure this is a transaction with all the columns we need. */
-    bool have_date = (trans_props.find (GncTransPropType::DATE) != trans_props.end());
-    bool have_amount = ((trans_props.find (GncTransPropType::DEPOSIT) != trans_props.end()) ||
-                        (trans_props.find (GncTransPropType::WITHDRAWAL) != trans_props.end()) ||
-                        (trans_props.find (GncTransPropType::BALANCE) != trans_props.end()));
-
-    std::string error_message {""};
-    if (!have_date)
-        error_message += N_("No date column.");
-    if (!have_amount)
+    std::string error_message;
+    std::shared_ptr<GncPreTrans> trans_props;
+    std::shared_ptr<GncPreSplit> split_props;
+
+    std::tie(std::ignore, error_message, trans_props, split_props) = orig_line;
+
+    auto trans_error = trans_props->verify_essentials();
+    auto split_error = split_props->verify_essentials();
+
+    error_message.clear();
+    if (!trans_error.empty())
     {
-        if (!have_date)
+        error_message = trans_error;
+        if (!split_error.empty())
             error_message += "\n";
-        error_message += N_("No balance, deposit, or withdrawal column.");
     }
-    if (!have_date || !have_amount)
-        *error = g_strdup (error_message.c_str());
+    if (!split_error.empty())
+        error_message += split_error;
 
-    return have_amount && have_date;
+    if (!error_message.empty())
+        throw std::invalid_argument(error_message);
 }
-
 /** Create a Transaction from a map of transaction properties.
  * Note: this function assumes all properties in the map have been verified
  *       to be valid. No further checks are performed here other than that
  *       the required properties are in the map
- * @param transprops The map of transaction properties
- * @param error Contains an error on failure
+ * @param orig_line The current line being parsed
  * @return On success, a GncCsvTransLine; on failure, the trans pointer is NULL
  */
-static GncCsvTransLine* trans_properties_to_trans (prop_map_t& trans_props, gchar** error)
+static GncCsvTransLine* trans_properties_to_trans (parse_line_t& orig_line)
 {
-
-    if (!trans_properties_verify_essentials(trans_props, error))
-        return NULL;
-
-    auto property = trans_props.find (GncTransPropType::ACCOUNT)->second;
-    auto account = dynamic_cast<GncTransPropImpl<Account*>*>(property.get())->m_value;
-
-    GncCsvTransLine* trans_line = g_new (GncCsvTransLine, 1);
-
-    /* The balance is 0 by default. */
-    trans_line->balance_set = false;
-    trans_line->balance = double_to_gnc_numeric (0.0, xaccAccountGetCommoditySCU (account),
-                          GNC_HOW_RND_ROUND_HALF_UP);
+    std::string error_message;
+    std::shared_ptr<GncPreTrans> trans_props;
+    std::shared_ptr<GncPreSplit> split_props;
+    std::tie(std::ignore, error_message, trans_props, split_props) = orig_line;
+    auto account = split_props->get_account();
 
     QofBook* book = gnc_account_get_book (account);
     gnc_commodity* currency = xaccAccountGetCommodity (account);
-    trans_line->trans = xaccMallocTransaction (book);
-    xaccTransBeginEdit (trans_line->trans);
-    xaccTransSetCurrency (trans_line->trans, currency);
-
-    /* Go through each of the properties and edit the transaction accordingly. */
-    std::string num;
-    std::string memo;
-    std::string omemo;
-    Account *oaccount = NULL;
-    bool amount_set = false;
-    gnc_numeric amount = trans_line->balance;
-
-    for (auto prop_pair : trans_props)
-    {
-        auto type = prop_pair.first;
-        auto prop = prop_pair.second;
-        switch (type)
-        {
-        case GncTransPropType::DATE:
-            {
-                auto transdate = dynamic_cast<GncTransPropImpl<time64*>*>(prop.get())->m_value;
-                xaccTransSetDatePostedSecsNormalized (trans_line->trans, *transdate);
-            }
-            break;
-
-        case GncTransPropType::DESCRIPTION:
-            {
-                auto propstring = dynamic_cast<GncTransPropImpl<std::string*>*>(prop.get())->m_value;
-                xaccTransSetDescription (trans_line->trans, propstring->c_str());
-            }
-            break;
-
-        case GncTransPropType::NOTES:
-            {
-                auto propstring = dynamic_cast<GncTransPropImpl<std::string*>*>(prop.get())->m_value;
-                xaccTransSetNotes (trans_line->trans, propstring->c_str());
-            }
-            break;
-
-        case GncTransPropType::OACCOUNT:
-            oaccount = dynamic_cast<GncTransPropImpl<Account*>*>(prop.get())->m_value;
-            break;
-
-        case GncTransPropType::MEMO:
-            memo = *dynamic_cast<GncTransPropImpl<std::string*>*>(prop.get())->m_value;
-            break;
-
-        case GncTransPropType::OMEMO:
-            omemo = *dynamic_cast<GncTransPropImpl<std::string*>*>(prop.get())->m_value;
-            break;
 
-        case GncTransPropType::NUM:
-            /* the 'num' is saved and passed to 'trans_add_split' below where
-             * 'gnc_set_num_action' is used to set tran-num and/or split-action
-             * per book option */
-            num = *dynamic_cast<GncTransPropImpl<std::string*>*>(prop.get())->m_value;
-            break;
+    auto trans = trans_props->create_trans (book, currency);
 
-        case GncTransPropType::DEPOSIT: /* Add deposits to the existing amount. */
-            {
-                auto propval = dynamic_cast<GncTransPropImpl<gnc_numeric*>*>(prop.get())->m_value;
-                amount = gnc_numeric_add (*propval,
-                                         amount,
-                                         xaccAccountGetCommoditySCU (account),
-                                         GNC_HOW_RND_ROUND_HALF_UP);
-                amount_set = true;
-                /* We will use the "Deposit" and "Withdrawal" columns in preference to "Balance". */
-                trans_line->balance_set = false;
-            }
-            break;
+    if (!trans)
+        return nullptr;
 
-        case GncTransPropType::WITHDRAWAL: /* Withdrawals are just negative deposits. */
-            {
-                auto propval = dynamic_cast<GncTransPropImpl<gnc_numeric*>*>(prop.get())->m_value;
-                amount = gnc_numeric_add (gnc_numeric_neg(*propval),
-                                         amount,
-                                         xaccAccountGetCommoditySCU (account),
-                                         GNC_HOW_RND_ROUND_HALF_UP);
-                amount_set = true;
-                /* We will use the "Deposit" and "Withdrawal" columns in preference to "Balance". */
-                trans_line->balance_set = false;
-            }
-            break;
+    GncCsvTransLine* trans_line = g_new (GncCsvTransLine, 1);
+    trans_line->balance_set = false;
+    trans_line->balance = gnc_numeric_zero();
 
-        case GncTransPropType::BALANCE: /* The balance gets stored in a separate field in trans_line. */
-            /* We will use the "Deposit" and "Withdrawal" columns in preference to "Balance". */
-            if (!amount_set)
-            {
-                auto propval = dynamic_cast<GncTransPropImpl<gnc_numeric*>*>(prop.get())->m_value;
-                /* This gets put into the actual transaction at the end of gnc_csv_parse_to_trans. */
-                trans_line->balance = *propval;
-                trans_line->balance_set = true;
-            }
-            break;
-        default:
-            break;
-        }
+    auto balance = split_props->create_split(trans);
+    if (balance)
+    {
+        trans_line->balance_set = true;
+        trans_line->balance = *balance;
     }
 
-    /* Add a split with the cumulative amount value. */
-    trans_add_split (trans_line->trans, account, book, amount, num, memo);
-
-    if (oaccount)
-        /* Note: the current importer assumes at most 2 splits. This means the second split amount
-         * will be the negative of the the first split amount. We also only set the num field once,
-         * for the first split.
-         */
-        trans_add_split (trans_line->trans, oaccount, book, gnc_numeric_neg(amount), "", omemo);
-
     return trans_line;
 }
 
@@ -753,8 +326,15 @@ void GncTxImport::adjust_balances (Account *account)
 
 }
 
-void GncTxImport::parse_line_to_trans (StrVec& line, prop_map_t& trans_props)
+void GncTxImport::parse_line_to_trans (parse_line_t& orig_line)
 {
+    StrVec line;
+    std::string error_message;
+    std::shared_ptr<GncPreTrans> trans_props;
+    std::shared_ptr<GncPreSplit> split_props;
+    std::tie(line, error_message, trans_props, split_props) = orig_line;
+    error_message.clear();
+
     /* Convert this import line into a map of transaction/split properties. */
     auto col_types_it = column_types.cbegin();
     auto line_it = line.cbegin();
@@ -763,49 +343,68 @@ void GncTxImport::parse_line_to_trans (StrVec& line, prop_map_t& trans_props)
             line_it != line.cend();
             ++col_types_it, ++line_it)
     {
-        std::shared_ptr<GncTransProperty> property;
-        switch (*col_types_it)
+        try
         {
-            case GncTransPropType::DATE:
-                property = GncTransPropImpl<time64*>::make_new (*line_it, date_format);
-            break;
-
-            case GncTransPropType::DESCRIPTION:
-            case GncTransPropType::NOTES:
-            case GncTransPropType::MEMO:
-            case GncTransPropType::OMEMO:
-            case GncTransPropType::NUM:
-                property = GncTransPropImpl<std::string*>::make_new (*line_it);
-                break;
-
-            case GncTransPropType::ACCOUNT:
-            case GncTransPropType::OACCOUNT:
-                property = GncTransPropImpl<Account*>::make_new (*line_it);
-                break;
-
-            case GncTransPropType::BALANCE:
-            case GncTransPropType::DEPOSIT:
-            case GncTransPropType::WITHDRAWAL:
-                property = GncTransPropImpl<gnc_numeric*>::make_new (*line_it, currency_format);
-                break;
-
-            default:
+            if (*col_types_it == GncTransPropType::NONE)
                 continue; /* We do nothing with "None"-type columns. */
-                break;
+            else if  (*col_types_it <= GncTransPropType::TRANS_PROPS)
+                trans_props->set_property(*col_types_it, *line_it, date_format);
+            else
+                split_props->set_property(*col_types_it, *line_it, currency_format);
+        }
+        catch (const std::invalid_argument&)
+        {
+            parse_errors = true;
+            error_message += _(gnc_csv_col_type_strs[*col_types_it]);
+            error_message += _(" column could not be understood.");
+            error_message += "\n";
         }
+    }
 
-        if (property->m_valid)
-            trans_props.insert(prop_pair_t(*col_types_it, property));
+    if (!error_message.empty())
+        throw std::invalid_argument(error_message);
+
+    // Add an ACCOUNT property with the default account if no account column was set by the user
+    auto line_acct = split_props->get_account();
+    if (!line_acct)
+    {
+        if (home_account)
+            split_props->set_account(home_account);
         else
         {
-            std::string error_message {_(gnc_csv_col_type_strs[*col_types_it])};
-            error_message += _(" column could not be understood.");
-            throw std::invalid_argument (error_message);
+            // Oops - the user didn't select an Account column *and* we didn't get a default value either!
+            // Note if you get here this suggests a bug in the code!
+            parse_errors = true;
+            error_message = _("No account column selected and no default account specified either.\n"
+                                       "This should never happen. Please report this as a bug.");
+            throw std::invalid_argument(error_message);
         }
     }
 
+    /* If column parsing was successful, convert trans properties into a trans line. */
+    try
+    {
+        trans_properties_verify_essentials (orig_line);
+
+        /* If all went well, add this transaction to the list. */
+        /* We want to keep the transactions sorted by date in case we have
+         * to calculate the transaction's amount based on the user provided balances.
+         * The multimap should deal with this for us. */
+        auto trans_line = trans_properties_to_trans (orig_line);
+        if (trans_line)
+        {
+            auto trans_date = xaccTransGetDate (trans_line->trans);
+            transactions.insert (std::pair<time64, GncCsvTransLine*>(trans_date,trans_line));
+        }
+    }
+    catch (const std::invalid_argument& e)
+    {
+        parse_errors = true;
+        error_message = e.what();
+    }
 }
 
+
 /** Creates a list of transactions from parsed data. Transactions that
  * could be created from rows are placed in transactions;
  * rows that fail are placed in error_lines. (Note: there
@@ -843,14 +442,13 @@ int GncTxImport::parse_to_trans (Account* account,
     else
         std::advance(orig_lines_max, end_row);
 
-    Account *home_account = NULL;
+    home_account = account;
     auto odd_line = false;
     parse_errors = false;
     for (orig_lines_it, odd_line;
             orig_lines_it != orig_lines_max;
             ++orig_lines_it, odd_line = !odd_line)
     {
-        prop_map_t trans_props;
         auto orig_line = *orig_lines_it;
 
         /* Skip current line if:
@@ -865,60 +463,17 @@ int GncTxImport::parse_to_trans (Account* account,
 
         try
         {
-            parse_line_to_trans (std::get<0>(orig_line), trans_props);
+            parse_line_to_trans (orig_line);
         }
-        catch (const std::invalid_argument& e)
+        catch (const std::invalid_argument&)
         {
-            parse_errors = true;
-            std::get<1>(orig_line) = e.what();
-            continue;
-        }
-
-        // Add an ACCOUNT property with the default account if no account column was set by the user
-        auto acct_prop_it = trans_props.find (GncTransPropType::ACCOUNT);
-        if (acct_prop_it != trans_props.end())
-            home_account = dynamic_cast<GncTransPropImpl<Account*>*>(acct_prop_it->second.get())->m_value;
-        else
-        {
-            // If there is no ACCOUNT property by now, try to use the default account passed in
-            if (account)
-            {
-                auto property = std::shared_ptr<GncTransProperty>(new GncTransPropImpl<Account*>(account));
-                trans_props.insert(prop_pair_t(GncTransPropType::ACCOUNT, property));
-                home_account = account;
-            }
-            else
-            {
-                // Oops - the user didn't select an Account column *and* we didn't get a default value either!
-                // Note if you get here this suggests a bug in the code!
-                parse_errors = true;
-                std::get<1>(orig_line) = _("No account column selected and no default account specified either.");
-                continue;
-            }
-        }
-
-        /* If column parsing was successful, convert trans properties into a trans line. */
-        gchar *error_message = NULL;
-        auto trans_line = trans_properties_to_trans (trans_props, &error_message);
-        if (trans_line == NULL)
-        {
-            parse_errors = true;
-            std::get<1>(orig_line) = error_message;
-            g_free (error_message);
             continue;
         }
-
-        /* If all went well, add this transaction to the list. */
-        /* We want to keep the transactions sorted by date in case we have
-         * to calculate the transaction's amount based on the user provided balances.
-         * The multimap should deal with this for us. */
-        auto trans_date = xaccTransGetDate (trans_line->trans);
-        transactions.insert (std::pair<time64, GncCsvTransLine*>(trans_date,trans_line));
     }
 
     if (std::find(column_types.begin(),column_types.end(), GncTransPropType::BALANCE) !=
         column_types.end()) // This is only used if we have one home account
-        adjust_balances (account ? account : home_account);
+        adjust_balances (home_account);
 
     return 0;
 }
diff --git a/src/import-export/csv-imp/gnc-tx-import.hpp b/src/import-export/csv-imp/gnc-tx-import.hpp
index 9ea3d67..f9ab224 100644
--- a/src/import-export/csv-imp/gnc-tx-import.hpp
+++ b/src/import-export/csv-imp/gnc-tx-import.hpp
@@ -39,36 +39,11 @@ extern "C" {
 #include <vector>
 #include <map>
 #include <memory>
-#include <boost/optional.hpp>
 
 #include "gnc-tokenizer.hpp"
+#include "gnc-trans-props.hpp"
 
 
-/** Enumeration for column types. These are the different types of
- * columns that can exist in a CSV/Fixed-Width file. There should be
- * no two columns with the same type except for the GncTransPropType::NONE
- * type. */
-enum class GncTransPropType {
-    NONE,
-    DATE,
-    NUM,
-    DESCRIPTION,
-    NOTES,
-    ACCOUNT,
-    DEPOSIT,
-    WITHDRAWAL,
-    BALANCE,
-    MEMO,
-    OACCOUNT,
-    OMEMO
-};
-
-/** Maps all column types to a string representation.
- *  The actual definition is in gnc-tx-import.cpp.
- *  Attention: that definition should be adjusted for any
- *  changes to enum class GncTransPropType ! */
-extern std::map<GncTransPropType, const char*> gnc_csv_col_type_strs;
-
 /* TODO We now sort transactions by date, not line number, so we
  * should probably get rid of this struct and uses of it. */
 
@@ -94,25 +69,6 @@ extern const gchar* currency_format_user[];
 extern const int num_date_formats;
 extern const gchar* date_format_user[];
 
-struct GncPreTrans
-{
-    boost::optional<time64> m_date;
-    boost::optional<std::string> m_num;
-    boost::optional<std::string> m_desc;
-    boost::optional<std::string> m_notes;
-};
-
-struct GncPreSplit
-{
-    boost::optional<Account*> m_account;
-    boost::optional<gnc_numeric> m_deposit;
-    boost::optional<gnc_numeric> m_withdrawal;
-    boost::optional<gnc_numeric> m_balance;
-    boost::optional<std::string> m_memo;
-    boost::optional<Account*> m_oaccount;
-    boost::optional<std::string> m_omemo;
-};
-
 /** Tuple to hold
  *  - a tokenized line of input
  *  - an optional error string
@@ -123,9 +79,6 @@ using parse_line_t = std::tuple<StrVec,
                                 std::shared_ptr<GncPreTrans>,
                                 std::shared_ptr<GncPreSplit>>;
 
-struct GncTransProperty;
-using prop_map_t = std::map<GncTransPropType, std::shared_ptr<GncTransProperty>>;
-
 /** The actual TxImport class
  * It's intended to use in the following sequence of actions:
  * - set a file format
@@ -165,12 +118,12 @@ public:
     bool parse_errors;          /**< Indicates whether the last parse_to_trans run had any errors */
 
 private:
-    void parse_line_to_trans (StrVec& line, prop_map_t& trans_props);
+    void parse_line_to_trans (parse_line_t& orig_line);
     void adjust_balances (Account *account);
 
     GncImpFileFormat file_fmt = GncImpFileFormat::UNKNOWN;
+    Account *home_account = NULL;
 };
 
-time64 parse_date (const std::string &date_str, int format);
 
 #endif

commit d642d0804bcc93340482dfa39c4e4c20d11fa735
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Mon Nov 28 17:29:51 2016 +0100

    Refactor parse_to_trans into smaller functions

diff --git a/src/import-export/csv-imp/gnc-tx-import.cpp b/src/import-export/csv-imp/gnc-tx-import.cpp
index ce9c5ef..de0428e 100644
--- a/src/import-export/csv-imp/gnc-tx-import.cpp
+++ b/src/import-export/csv-imp/gnc-tx-import.cpp
@@ -530,7 +530,6 @@ static void trans_add_split (Transaction* trans, Account* account, QofBook* book
  * and its iterator (a pair)
  */
 using prop_pair_t = std::pair<GncTransPropType, std::shared_ptr<GncTransProperty>>;
-using prop_map_t = std::map<GncTransPropType, std::shared_ptr<GncTransProperty>>;
 
 /** Tests a TransPropertyList for having enough essential properties.
  * Essential properties are
@@ -754,6 +753,59 @@ void GncTxImport::adjust_balances (Account *account)
 
 }
 
+void GncTxImport::parse_line_to_trans (StrVec& line, prop_map_t& trans_props)
+{
+    /* Convert this import line into a map of transaction/split properties. */
+    auto col_types_it = column_types.cbegin();
+    auto line_it = line.cbegin();
+    for (col_types_it, line_it;
+            col_types_it != column_types.cend() &&
+            line_it != line.cend();
+            ++col_types_it, ++line_it)
+    {
+        std::shared_ptr<GncTransProperty> property;
+        switch (*col_types_it)
+        {
+            case GncTransPropType::DATE:
+                property = GncTransPropImpl<time64*>::make_new (*line_it, date_format);
+            break;
+
+            case GncTransPropType::DESCRIPTION:
+            case GncTransPropType::NOTES:
+            case GncTransPropType::MEMO:
+            case GncTransPropType::OMEMO:
+            case GncTransPropType::NUM:
+                property = GncTransPropImpl<std::string*>::make_new (*line_it);
+                break;
+
+            case GncTransPropType::ACCOUNT:
+            case GncTransPropType::OACCOUNT:
+                property = GncTransPropImpl<Account*>::make_new (*line_it);
+                break;
+
+            case GncTransPropType::BALANCE:
+            case GncTransPropType::DEPOSIT:
+            case GncTransPropType::WITHDRAWAL:
+                property = GncTransPropImpl<gnc_numeric*>::make_new (*line_it, currency_format);
+                break;
+
+            default:
+                continue; /* We do nothing with "None"-type columns. */
+                break;
+        }
+
+        if (property->m_valid)
+            trans_props.insert(prop_pair_t(*col_types_it, property));
+        else
+        {
+            std::string error_message {_(gnc_csv_col_type_strs[*col_types_it])};
+            error_message += _(" column could not be understood.");
+            throw std::invalid_argument (error_message);
+        }
+    }
+
+}
+
 /** Creates a list of transactions from parsed data. Transactions that
  * could be created from rows are placed in transactions;
  * rows that fail are placed in error_lines. (Note: there
@@ -811,68 +863,22 @@ int GncTxImport::parse_to_trans (Account* account,
            (!redo_errors && skip_rows && odd_line))
             continue;
 
-        auto line = std::get<0>(orig_line);
-        GncCsvTransLine* trans_line = NULL;
-
-        /* Convert this import line into a map of transaction/split properties. */
-        auto loop_err = false;
-        auto col_types_it = column_types.cbegin();
-        auto line_it = line.cbegin();
-        for (col_types_it, line_it;
-                col_types_it != column_types.cend() &&
-                line_it != line.cend();
-                ++col_types_it, ++line_it)
+        try
         {
-            std::shared_ptr<GncTransProperty> property;
-            switch (*col_types_it)
-            {
-                case GncTransPropType::DATE:
-                    property = GncTransPropImpl<time64*>::make_new (*line_it, date_format);
-                break;
-
-                case GncTransPropType::DESCRIPTION:
-                case GncTransPropType::NOTES:
-                case GncTransPropType::MEMO:
-                case GncTransPropType::OMEMO:
-                case GncTransPropType::NUM:
-                    property = GncTransPropImpl<std::string*>::make_new (*line_it);
-                    break;
-
-                case GncTransPropType::ACCOUNT:
-                case GncTransPropType::OACCOUNT:
-                    property = GncTransPropImpl<Account*>::make_new (*line_it);
-                    if (*col_types_it == GncTransPropType::ACCOUNT)
-                        home_account = dynamic_cast<GncTransPropImpl<Account*>*>(property.get())->m_value;
-                    break;
-
-                case GncTransPropType::BALANCE:
-                case GncTransPropType::DEPOSIT:
-                case GncTransPropType::WITHDRAWAL:
-                    property = GncTransPropImpl<gnc_numeric*>::make_new (*line_it, currency_format);
-                    break;
-
-                default:
-                    continue; /* We do nothing with "None"-type columns. */
-                    break;
-            }
-
-            if (property->m_valid)
-                trans_props.insert(prop_pair_t(*col_types_it, property));
-            else
-            {
-                parse_errors = loop_err = true;
-                std::string error_message {_(gnc_csv_col_type_strs[*col_types_it])};
-                error_message += _(" column could not be understood.");
-                std::get<1>(orig_line) = std::move(error_message);
-                break;
-            }
+            parse_line_to_trans (std::get<0>(orig_line), trans_props);
         }
-
-        if (loop_err)
+        catch (const std::invalid_argument& e)
+        {
+            parse_errors = true;
+            std::get<1>(orig_line) = e.what();
             continue;
+        }
 
         // Add an ACCOUNT property with the default account if no account column was set by the user
-        if (std::find (column_types.begin(), column_types.end(), GncTransPropType::ACCOUNT) == column_types.end())
+        auto acct_prop_it = trans_props.find (GncTransPropType::ACCOUNT);
+        if (acct_prop_it != trans_props.end())
+            home_account = dynamic_cast<GncTransPropImpl<Account*>*>(acct_prop_it->second.get())->m_value;
+        else
         {
             // If there is no ACCOUNT property by now, try to use the default account passed in
             if (account)
@@ -893,7 +899,7 @@ int GncTxImport::parse_to_trans (Account* account,
 
         /* If column parsing was successful, convert trans properties into a trans line. */
         gchar *error_message = NULL;
-        trans_line = trans_properties_to_trans (trans_props, &error_message);
+        auto trans_line = trans_properties_to_trans (trans_props, &error_message);
         if (trans_line == NULL)
         {
             parse_errors = true;
diff --git a/src/import-export/csv-imp/gnc-tx-import.hpp b/src/import-export/csv-imp/gnc-tx-import.hpp
index 6f9aed4..9ea3d67 100644
--- a/src/import-export/csv-imp/gnc-tx-import.hpp
+++ b/src/import-export/csv-imp/gnc-tx-import.hpp
@@ -123,6 +123,9 @@ using parse_line_t = std::tuple<StrVec,
                                 std::shared_ptr<GncPreTrans>,
                                 std::shared_ptr<GncPreSplit>>;
 
+struct GncTransProperty;
+using prop_map_t = std::map<GncTransPropType, std::shared_ptr<GncTransProperty>>;
+
 /** The actual TxImport class
  * It's intended to use in the following sequence of actions:
  * - set a file format
@@ -162,6 +165,7 @@ public:
     bool parse_errors;          /**< Indicates whether the last parse_to_trans run had any errors */
 
 private:
+    void parse_line_to_trans (StrVec& line, prop_map_t& trans_props);
     void adjust_balances (Account *account);
 
     GncImpFileFormat file_fmt = GncImpFileFormat::UNKNOWN;

commit ab6dc0f5900f720130f2d64770e0a4febffd3ef8
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Mon Nov 28 15:21:41 2016 +0100

    Add property structs for transaction vs split properties

diff --git a/src/import-export/csv-imp/gnc-tx-import.cpp b/src/import-export/csv-imp/gnc-tx-import.cpp
index 87c788b..ce9c5ef 100644
--- a/src/import-export/csv-imp/gnc-tx-import.cpp
+++ b/src/import-export/csv-imp/gnc-tx-import.cpp
@@ -307,7 +307,8 @@ void GncTxImport::parse (bool guessColTypes)
     orig_lines.clear();
     for (auto tokenized_line : tokenizer->get_tokens())
     {
-        orig_lines.push_back (std::make_tuple (tokenized_line, std::string()));
+        orig_lines.push_back (std::make_tuple (tokenized_line, std::string(),
+                std::make_shared<GncPreTrans>(), std::make_shared<GncPreSplit>()));
         auto length = tokenized_line.size();
         if (length > max_cols)
             max_cols = length;
diff --git a/src/import-export/csv-imp/gnc-tx-import.hpp b/src/import-export/csv-imp/gnc-tx-import.hpp
index a3d88be6..6f9aed4 100644
--- a/src/import-export/csv-imp/gnc-tx-import.hpp
+++ b/src/import-export/csv-imp/gnc-tx-import.hpp
@@ -39,6 +39,7 @@ extern "C" {
 #include <vector>
 #include <map>
 #include <memory>
+#include <boost/optional.hpp>
 
 #include "gnc-tokenizer.hpp"
 
@@ -93,8 +94,34 @@ extern const gchar* currency_format_user[];
 extern const int num_date_formats;
 extern const gchar* date_format_user[];
 
-/** Tuple to hold a tokenized line of input and an optional error string */
-using parse_line_t = std::tuple<StrVec, std::string>;
+struct GncPreTrans
+{
+    boost::optional<time64> m_date;
+    boost::optional<std::string> m_num;
+    boost::optional<std::string> m_desc;
+    boost::optional<std::string> m_notes;
+};
+
+struct GncPreSplit
+{
+    boost::optional<Account*> m_account;
+    boost::optional<gnc_numeric> m_deposit;
+    boost::optional<gnc_numeric> m_withdrawal;
+    boost::optional<gnc_numeric> m_balance;
+    boost::optional<std::string> m_memo;
+    boost::optional<Account*> m_oaccount;
+    boost::optional<std::string> m_omemo;
+};
+
+/** Tuple to hold
+ *  - a tokenized line of input
+ *  - an optional error string
+ *  - a struct to hold user selected properties for a transaction
+ *  - a struct to hold user selected properties for one or two splits in the above transaction */
+using parse_line_t = std::tuple<StrVec,
+                                std::string,
+                                std::shared_ptr<GncPreTrans>,
+                                std::shared_ptr<GncPreSplit>>;
 
 /** The actual TxImport class
  * It's intended to use in the following sequence of actions:

commit 91df5edaa50d13211580a9ca51fda874789ff33f
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Mon Nov 28 14:39:02 2016 +0100

    Refactor pair into a tuple in preparation of extending it

diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index e24bc59..894c35d 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -1727,7 +1727,7 @@ static void gnc_csv_preview_update_assist (CsvImportTrans* info)
     for (auto parse_line : info->parse_data->orig_lines)
     {
         // When previewing errors skip all lines that don't have errors
-        if (info->previewing_errors && parse_line.second.empty())
+        if (info->previewing_errors && std::get<1>(parse_line).empty())
             continue;
 
         GtkTreeIter iter;
@@ -1736,14 +1736,14 @@ static void gnc_csv_preview_update_assist (CsvImportTrans* info)
         /* Row Color column */
         gtk_list_store_set (store, &iter, 0, NULL, -1);
 
-        for (auto cell_str_it = parse_line.first.cbegin(); cell_str_it != parse_line.first.cend(); cell_str_it++)
+        for (auto cell_str_it = std::get<0>(parse_line).cbegin(); cell_str_it != std::get<0>(parse_line).cend(); cell_str_it++)
         {
             /* Set the value of the proper column in the list store. */
-            uint pos = cell_str_it - parse_line.first.cbegin() + 1;
+            uint pos = cell_str_it - std::get<0>(parse_line).cbegin() + 1;
             gtk_list_store_set (store, &iter, pos, cell_str_it->c_str(), -1);
         }
         /* Add the optional error messages in the last column of the store */
-        gtk_list_store_set (store, &iter, ncols + 1, parse_line.second.c_str(), -1);
+        gtk_list_store_set (store, &iter, ncols + 1, std::get<1>(parse_line).c_str(), -1);
     }
     gtk_tree_view_set_model (info->treeview, GTK_TREE_MODEL(store));
 
diff --git a/src/import-export/csv-imp/gnc-tx-import.cpp b/src/import-export/csv-imp/gnc-tx-import.cpp
index 10c87a4..87c788b 100644
--- a/src/import-export/csv-imp/gnc-tx-import.cpp
+++ b/src/import-export/csv-imp/gnc-tx-import.cpp
@@ -307,7 +307,7 @@ void GncTxImport::parse (bool guessColTypes)
     orig_lines.clear();
     for (auto tokenized_line : tokenizer->get_tokens())
     {
-        orig_lines.push_back (std::make_pair (tokenized_line, std::string()));
+        orig_lines.push_back (std::make_tuple (tokenized_line, std::string()));
         auto length = tokenized_line.size();
         if (length > max_cols)
             max_cols = length;
@@ -771,7 +771,7 @@ int GncTxImport::parse_to_trans (Account* account,
     if (!redo_errors) /* If we're redoing errors, we save freeing until the end. */
     {
         for (auto orig_line : orig_lines)
-            orig_line.second.clear();
+            std::get<1>(orig_line).clear();
 
         /* FIXME handle memory leak here!
          * Existing transactions in the map should probably removed before emptying the map
@@ -798,6 +798,7 @@ int GncTxImport::parse_to_trans (Account* account,
             ++orig_lines_it, odd_line = !odd_line)
     {
         prop_map_t trans_props;
+        auto orig_line = *orig_lines_it;
 
         /* Skip current line if:
            1. only looking for lines with error AND no error on current line
@@ -805,11 +806,11 @@ int GncTxImport::parse_to_trans (Account* account,
            2. looking for all lines AND
               skip_rows is enabled AND
               current line is an odd line */
-        if ((redo_errors && orig_lines_it->second.empty()) ||
+        if ((redo_errors && std::get<1>(orig_line).empty()) ||
            (!redo_errors && skip_rows && odd_line))
             continue;
 
-        auto line = orig_lines_it->first;
+        auto line = std::get<0>(orig_line);
         GncCsvTransLine* trans_line = NULL;
 
         /* Convert this import line into a map of transaction/split properties. */
@@ -861,7 +862,7 @@ int GncTxImport::parse_to_trans (Account* account,
                 parse_errors = loop_err = true;
                 std::string error_message {_(gnc_csv_col_type_strs[*col_types_it])};
                 error_message += _(" column could not be understood.");
-                orig_lines_it->second = std::move(error_message);
+                std::get<1>(orig_line) = std::move(error_message);
                 break;
             }
         }
@@ -884,7 +885,7 @@ int GncTxImport::parse_to_trans (Account* account,
                 // Oops - the user didn't select an Account column *and* we didn't get a default value either!
                 // Note if you get here this suggests a bug in the code!
                 parse_errors = true;
-                orig_lines_it->second = _("No account column selected and no default account specified either.");
+                std::get<1>(orig_line) = _("No account column selected and no default account specified either.");
                 continue;
             }
         }
@@ -895,7 +896,7 @@ int GncTxImport::parse_to_trans (Account* account,
         if (trans_line == NULL)
         {
             parse_errors = true;
-            orig_lines_it->second = error_message;
+            std::get<1>(orig_line) = error_message;
             g_free (error_message);
             continue;
         }
diff --git a/src/import-export/csv-imp/gnc-tx-import.hpp b/src/import-export/csv-imp/gnc-tx-import.hpp
index 6ab24f2..a3d88be6 100644
--- a/src/import-export/csv-imp/gnc-tx-import.hpp
+++ b/src/import-export/csv-imp/gnc-tx-import.hpp
@@ -93,8 +93,8 @@ extern const gchar* currency_format_user[];
 extern const int num_date_formats;
 extern const gchar* date_format_user[];
 
-/** Pair to hold a tokenized line of input and an optional error string */
-using parse_line_t = std::pair<StrVec, std::string>;
+/** Tuple to hold a tokenized line of input and an optional error string */
+using parse_line_t = std::tuple<StrVec, std::string>;
 
 /** The actual TxImport class
  * It's intended to use in the following sequence of actions:

commit ffa68c6b84ab08b0b320c1d47936899f13bd713a
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Mon Nov 28 14:38:38 2016 +0100

    Factor out balance calculating function

diff --git a/src/import-export/csv-imp/gnc-tx-import.cpp b/src/import-export/csv-imp/gnc-tx-import.cpp
index 1bab59f..10c87a4 100644
--- a/src/import-export/csv-imp/gnc-tx-import.cpp
+++ b/src/import-export/csv-imp/gnc-tx-import.cpp
@@ -702,6 +702,57 @@ static GncCsvTransLine* trans_properties_to_trans (prop_map_t& trans_props, gcha
     return trans_line;
 }
 
+void GncTxImport::adjust_balances (Account *account)
+{
+    Split      *split, *osplit;
+
+    /* balance_offset is how much the balance currently in the account
+     * differs from what it will be after the transactions are
+     * imported. This will be sum of all the previous transactions for
+     * any given transaction. */
+    auto balance_offset = double_to_gnc_numeric (0.0, xaccAccountGetCommoditySCU (account),
+                                 GNC_HOW_RND_ROUND_HALF_UP);
+    for (auto trans_iter : transactions)
+    {
+        auto trans_line = trans_iter.second;
+        if (trans_line->balance_set)
+        {
+            time64 date = xaccTransGetDate (trans_line->trans);
+            /* Find what the balance should be by adding the offset to the actual balance. */
+            gnc_numeric existing_balance = gnc_numeric_add (balance_offset,
+                                           xaccAccountGetBalanceAsOfDate (account, date),
+                                           xaccAccountGetCommoditySCU (account),
+                                           GNC_HOW_RND_ROUND_HALF_UP);
+
+            /* The amount of the transaction is the difference between the new and existing balance. */
+            gnc_numeric amount = gnc_numeric_sub (trans_line->balance,
+                                                 existing_balance,
+                                                 xaccAccountGetCommoditySCU (account),
+                                                 GNC_HOW_RND_ROUND_HALF_UP);
+
+            // Find home account split
+            split  = xaccTransFindSplitByAccount (trans_line->trans, account);
+            xaccSplitSetAmount (split, amount);
+            xaccSplitSetValue (split, amount);
+
+            // If we have two splits, change other side
+            if (xaccTransCountSplits (trans_line->trans) == 2)
+            {
+                osplit = xaccSplitGetOtherSplit (split);
+                xaccSplitSetAmount (split, amount);
+                xaccSplitSetValue (split, gnc_numeric_neg (amount));
+            }
+
+            /* This new transaction needs to be added to the balance offset. */
+            balance_offset = gnc_numeric_add (balance_offset,
+                                             amount,
+                                             xaccAccountGetCommoditySCU (account),
+                                             GNC_HOW_RND_ROUND_HALF_UP);
+        }
+    }
+
+}
+
 /** Creates a list of transactions from parsed data. Transactions that
  * could be created from rows are placed in transactions;
  * rows that fail are placed in error_lines. (Note: there
@@ -859,57 +910,7 @@ int GncTxImport::parse_to_trans (Account* account,
 
     if (std::find(column_types.begin(),column_types.end(), GncTransPropType::BALANCE) !=
         column_types.end()) // This is only used if we have one home account
-    {
-        Split      *split, *osplit;
-
-        if (account != NULL)
-            home_account = account;
-
-        /* balance_offset is how much the balance currently in the account
-         * differs from what it will be after the transactions are
-         * imported. This will be sum of all the previous transactions for
-         * any given transaction. */
-        auto balance_offset = double_to_gnc_numeric (0.0, xaccAccountGetCommoditySCU (home_account),
-                                     GNC_HOW_RND_ROUND_HALF_UP);
-        for (auto trans_iter : transactions)
-        {
-            auto trans_line = trans_iter.second;
-            if (trans_line->balance_set)
-            {
-                time64 date = xaccTransGetDate (trans_line->trans);
-                /* Find what the balance should be by adding the offset to the actual balance. */
-                gnc_numeric existing_balance = gnc_numeric_add (balance_offset,
-                                               xaccAccountGetBalanceAsOfDate (home_account, date),
-                                               xaccAccountGetCommoditySCU (home_account),
-                                               GNC_HOW_RND_ROUND_HALF_UP);
-
-                /* The amount of the transaction is the difference between the new and existing balance. */
-                gnc_numeric amount = gnc_numeric_sub (trans_line->balance,
-                                                     existing_balance,
-                                                     xaccAccountGetCommoditySCU (home_account),
-                                                     GNC_HOW_RND_ROUND_HALF_UP);
-
-                // Find home account split
-                split  = xaccTransFindSplitByAccount (trans_line->trans, home_account);
-                xaccSplitSetAmount (split, amount);
-                xaccSplitSetValue (split, amount);
-
-                // If we have two splits, change other side
-                if (xaccTransCountSplits (trans_line->trans) == 2)
-                {
-                    osplit = xaccSplitGetOtherSplit (split);
-                    xaccSplitSetAmount (split, amount);
-                    xaccSplitSetValue (split, gnc_numeric_neg (amount));
-                }
-
-                /* This new transaction needs to be added to the balance offset. */
-                balance_offset = gnc_numeric_add (balance_offset,
-                                                 amount,
-                                                 xaccAccountGetCommoditySCU (home_account),
-                                                 GNC_HOW_RND_ROUND_HALF_UP);
-            }
-        }
-    }
+        adjust_balances (account ? account : home_account);
 
     return 0;
 }
diff --git a/src/import-export/csv-imp/gnc-tx-import.hpp b/src/import-export/csv-imp/gnc-tx-import.hpp
index a340854..6ab24f2 100644
--- a/src/import-export/csv-imp/gnc-tx-import.hpp
+++ b/src/import-export/csv-imp/gnc-tx-import.hpp
@@ -135,6 +135,8 @@ public:
     bool parse_errors;          /**< Indicates whether the last parse_to_trans run had any errors */
 
 private:
+    void adjust_balances (Account *account);
+
     GncImpFileFormat file_fmt = GncImpFileFormat::UNKNOWN;
 };
 

commit 0d4d92fbb4eb97b9006c6029480040c1450e3c0a
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Wed Oct 12 12:31:33 2016 +0200

    Drop unused parameter

diff --git a/src/import-export/csv-imp/gnc-tx-import.cpp b/src/import-export/csv-imp/gnc-tx-import.cpp
index 2ee774f..1bab59f 100644
--- a/src/import-export/csv-imp/gnc-tx-import.cpp
+++ b/src/import-export/csv-imp/gnc-tx-import.cpp
@@ -588,11 +588,6 @@ static GncCsvTransLine* trans_properties_to_trans (prop_map_t& trans_props, gcha
     trans_line->balance = double_to_gnc_numeric (0.0, xaccAccountGetCommoditySCU (account),
                           GNC_HOW_RND_ROUND_HALF_UP);
 
-    /* We make the line_no -1 just to mark that it hasn't been set. We
-     * may get rid of line_no soon anyway, so it's not particularly
-     * important. */
-    trans_line->line_no = -1;
-
     QofBook* book = gnc_account_get_book (account);
     gnc_commodity* currency = xaccAccountGetCommodity (account);
     trans_line->trans = xaccMallocTransaction (book);
diff --git a/src/import-export/csv-imp/gnc-tx-import.hpp b/src/import-export/csv-imp/gnc-tx-import.hpp
index 017b35c..a340854 100644
--- a/src/import-export/csv-imp/gnc-tx-import.hpp
+++ b/src/import-export/csv-imp/gnc-tx-import.hpp
@@ -80,7 +80,6 @@ extern std::map<GncTransPropType, const char*> gnc_csv_col_type_strs;
  * corrected rows into the right places. */
 struct GncCsvTransLine
 {
-    int line_no;
     Transaction* trans;
     gnc_numeric balance;  /**< The (supposed) balance after this transaction takes place */
     bool balance_set;     /**< true if balance has been set from user data, false otherwise */

commit f64d217ed3642eff538ba89f38c7bca076e26073
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Tue Oct 11 23:11:39 2016 +0200

    Drop remaining GError boilerplate from the c++ importer

diff --git a/src/import-export/csv-imp/gnc-tx-import.cpp b/src/import-export/csv-imp/gnc-tx-import.cpp
index 73c5603..2ee774f 100644
--- a/src/import-export/csv-imp/gnc-tx-import.cpp
+++ b/src/import-export/csv-imp/gnc-tx-import.cpp
@@ -53,12 +53,6 @@ extern "C" {
 #include "gnc-csv-tokenizer.hpp"
 #include "gnc-fw-tokenizer.hpp"
 
-GQuark
-gnc_csv_imp_error_quark (void)
-{
-  return g_quark_from_static_string ("g-tx-import-error-quark");
-}
-
 G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT;
 
 //const int num_date_formats = 5;
diff --git a/src/import-export/csv-imp/gnc-tx-import.hpp b/src/import-export/csv-imp/gnc-tx-import.hpp
index 19ee290..017b35c 100644
--- a/src/import-export/csv-imp/gnc-tx-import.hpp
+++ b/src/import-export/csv-imp/gnc-tx-import.hpp
@@ -68,19 +68,6 @@ enum class GncTransPropType {
  *  changes to enum class GncTransPropType ! */
 extern std::map<GncTransPropType, const char*> gnc_csv_col_type_strs;
 
-/** Error domain for the csv importer. */
-#define GNC_CSV_IMP_ERROR gnc_csv_imp_error_quark ()
-GQuark gnc_csv_imp_error_quark (void);
-
-/** Enumeration for error types. These are the different types of
- * errors that various functions used for the CSV/Fixed-Width importer
- * can have. */
-enum GncCsvErrorType {
-    GNC_CSV_IMP_ERROR_OPEN,
-    GNC_CSV_IMP_ERROR_ENCODING,
-    GNC_CSV_IMP_ERROR_PARSE
-};
-
 /* TODO We now sort transactions by date, not line number, so we
  * should probably get rid of this struct and uses of it. */
 

commit 430875772be18bbe273ee001407c448cfef9930a
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Tue Oct 11 23:05:18 2016 +0200

    Replace GError usage in parse function with proper try catch blocks
    
    In addition fix a crash while loading importer settings that would reduce
    the number of columns found in the import file.

diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index bad00c7..e24bc59 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -201,168 +201,152 @@ void
 csv_import_trans_load_settings (CsvImportTrans *info)
 {
     GtkTreeIter   iter;
-    gchar        *group = NULL, *name = NULL;
 
     // Get the Active Selection
-    if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX(info->settings_combo), &iter))
-    {
-        GtkTreeModel *model;
-        GtkAdjustment *adj;
-        int i;
+    if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX(info->settings_combo), &iter))
+        return;
 
-        model = gtk_combo_box_get_model (GTK_COMBO_BOX(info->settings_combo));
-        gtk_tree_model_get (model, &iter, SET_GROUP, &group, SET_NAME, &name, -1);
+    GtkTreeModel *model;
+    gchar *group = NULL;
 
-        // Test for default selection, return
-        if (g_strcmp0 (group, NULL) == 0)
-            return;
+    model = gtk_combo_box_get_model (GTK_COMBO_BOX(info->settings_combo));
+    gtk_tree_model_get (model, &iter, SET_GROUP, &group, -1);
 
-        // Load the settings from the keyfile
-        if (gnc_csv_trans_load_settings (info->settings_data, group))
-        {
-            GtkWidget    *dialog;
-            const gchar  *title = _("Load the Import Settings.");
+    // Test for default selection, return
+    if (!group)
+        return;
 
-            dialog = gtk_message_dialog_new (GTK_WINDOW(info->window),
-                                        (GtkDialogFlags) 0,
-                                        GTK_MESSAGE_ERROR,
-                                        GTK_BUTTONS_OK,
-                                        "%s", title);
-            gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
-                "%s", _("There were problems reading some saved settings, continuing to load.\n Please review and save again."));
-            gtk_dialog_run (GTK_DIALOG (dialog));
-            gtk_widget_destroy (dialog);
-        }
+    // Load the settings from the keyfile
+    if (gnc_csv_trans_load_settings (info->settings_data, group))
+    {
+        const gchar *title = _("Load the Import Settings.");
+        GtkWidget *dialog = gtk_message_dialog_new (GTK_WINDOW(info->window),
+                                    (GtkDialogFlags) 0,
+                                    GTK_MESSAGE_ERROR,
+                                    GTK_BUTTONS_OK,
+                                    "%s", title);
+        gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+            "%s", _("There were problems reading some saved settings, continuing to load.\n Please review and save again."));
+        gtk_dialog_run (GTK_DIALOG (dialog));
+        gtk_widget_destroy (dialog);
+    }
+    g_free (group);
 
-        // Set start row
-        info->parse_data->start_row = info->settings_data->header_rows;
-        adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(info->start_row_spin));
-        gtk_spin_button_set_value (GTK_SPIN_BUTTON(info->start_row_spin), info->settings_data->header_rows);
+    // Set start row
+    info->parse_data->start_row = info->settings_data->header_rows;
+    GtkAdjustment *adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(info->start_row_spin));
+    gtk_spin_button_set_value (GTK_SPIN_BUTTON(info->start_row_spin), info->settings_data->header_rows);
 
-        // Set end row
-        info->parse_data->end_row = info->num_of_rows - info->settings_data->footer_rows;
-        adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(info->end_row_spin));
-        gtk_adjustment_set_upper (adj, info->num_of_rows);
-        gtk_spin_button_set_value (GTK_SPIN_BUTTON(info->end_row_spin), info->num_of_rows - info->settings_data->footer_rows);
+    // Set end row
+    info->parse_data->end_row = info->num_of_rows - info->settings_data->footer_rows;
+    adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(info->end_row_spin));
+    gtk_adjustment_set_upper (adj, info->num_of_rows);
+    gtk_spin_button_set_value (GTK_SPIN_BUTTON(info->end_row_spin), info->num_of_rows - info->settings_data->footer_rows);
+
+    // Set Alternate rows
+    info->parse_data->skip_rows = info->settings_data->skip_alt_rows;
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->skip_rows), info->settings_data->skip_alt_rows);
 
-        // Set Alternate rows
-        info->parse_data->skip_rows = info->settings_data->skip_alt_rows;
-        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->skip_rows), info->settings_data->skip_alt_rows);
+    // Set Import Format
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->csv_button), info->settings_data->csv_format);
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->fixed_button), !info->settings_data->csv_format);
 
-        // Set Import Format
-        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->csv_button), info->settings_data->csv_format);
-        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->fixed_button), !info->settings_data->csv_format);
+    // This section deals with the combo's and character encoding
+    info->parse_data->date_format = info->settings_data->date_active;
+    gtk_combo_box_set_active (GTK_COMBO_BOX(info->date_format_combo), info->settings_data->date_active);
 
-        // This Section deals with the separators
+    info->parse_data->currency_format = info->settings_data->currency_active;
+    gtk_combo_box_set_active (GTK_COMBO_BOX(info->currency_format_combo), info->settings_data->currency_active);
+    info->parse_data->convert_encoding (info->settings_data->encoding);
+    go_charmap_sel_set_encoding (info->encselector, info->settings_data->encoding);
+
+    try
+    {
         if (info->settings_data->csv_format)
         {
-            try
-            {
-                info->parse_data->file_format (GncImpFileFormat::CSV);
-                for (i = 0; i < SEP_NUM_OF_TYPES; i++)
-                {
-                    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->sep_buttons[i]), info->settings_data->separator[i]);
-                }
-                gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->custom_cbutton), info->settings_data->custom);
-                if (info->settings_data->custom)
-                    gtk_entry_set_text (GTK_ENTRY(info->custom_entry), info->settings_data->custom_entry);
-                else
-                    gtk_entry_set_text (GTK_ENTRY(info->custom_entry), "");
+            // Handle separators, only relevant if the file format is csv
+            info->parse_data->file_format (GncImpFileFormat::CSV);
+            for (int i = 0; i < SEP_NUM_OF_TYPES; i++)
+                gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->sep_buttons[i]), info->settings_data->separator[i]);
+            gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->custom_cbutton), info->settings_data->custom);
+            if (info->settings_data->custom)
+                gtk_entry_set_text (GTK_ENTRY(info->custom_entry), info->settings_data->custom_entry);
+            else
+                gtk_entry_set_text (GTK_ENTRY(info->custom_entry), "");
 
-                sep_button_clicked (NULL, info);
-            }
-            catch (...)
-            {
-                // FIXME Handle file loading errors (possibly thrown by file_format above)
-            }
+            sep_button_clicked (NULL, info);
         }
-
-        // This section deals with the combo's and character encoding
-        info->parse_data->date_format = info->settings_data->date_active;
-        gtk_combo_box_set_active (GTK_COMBO_BOX(info->date_format_combo), info->settings_data->date_active);
-
-        info->parse_data->currency_format = info->settings_data->currency_active;
-        gtk_combo_box_set_active (GTK_COMBO_BOX(info->currency_format_combo), info->settings_data->currency_active);
-        info->parse_data->convert_encoding (info->settings_data->encoding);
-        go_charmap_sel_set_encoding (info->encselector, info->settings_data->encoding);
-
-        // This section deals with the column widths (which are only used for fixed width files)
-        if (!info->settings_data->csv_format)
+        else
         {
-            try
-            {
-                info->parse_data->file_format (GncImpFileFormat::FIXED_WIDTH);
-                GncFwTokenizer *fwtok = dynamic_cast<GncFwTokenizer*>(info->parse_data->tokenizer.get());
-                if (info->settings_data->column_widths, NULL)
-                    fwtok->cols_from_string (std::string(info->settings_data->column_widths));
+            // Handle column widths, only relevant if the file format is fixed width
+            info->parse_data->file_format (GncImpFileFormat::FIXED_WIDTH);
+            GncFwTokenizer *fwtok = dynamic_cast<GncFwTokenizer*>(info->parse_data->tokenizer.get());
+            if (info->settings_data->column_widths, NULL)
+                fwtok->cols_from_string (std::string(info->settings_data->column_widths));
 
-                GError  *error = NULL;
-                if (info->parse_data->parse (false, &error))
-                {
-                    gnc_error_dialog (NULL, "%s", _("There was a problem with the column widths, please review."));
-                    g_error_free (error);
-                    g_free (group);
-                    g_free (name);
-                    return;
-                }
-                gnc_csv_preview_update_assist (info);
-            }
-            catch (...)
-            {
-                // FIXME Handle file loading errors (possibly thrown by file_format above)
-            }
+            info->parse_data->parse (false);
+            gnc_csv_preview_update_assist (info);
         }
+    }
+    catch (std::range_error &e)
+    {
+        // Catch parse errors
+        gnc_error_dialog (NULL, "%s", _("There was a problem with the column widths, please review."));
+        return;
+    }
+    catch (...)
+    {
+        // FIXME Handle file loading errors (possibly thrown by file_format above)
+        PWARN("Got an error during file loading");
+    }
 
-        // This section deals with the column types
-        if (info->settings_data->column_types)
-        {
-            GtkTreeModel *ctstore;
-            GtkTreeIter   iter;
-            gchar       **columns;
-            int           i;
-            bool          error = false;
+    // This section deals with the column types
+    if (info->settings_data->column_types)
+    {
+        GtkTreeModel *ctstore;
+        GtkTreeIter   iter;
+        gchar       **columns;
+        uint          i;
+        bool          error = false;
 
-            columns = g_strsplit (info->settings_data->column_types, ",", -1);
+        columns = g_strsplit (info->settings_data->column_types, ",", -1);
 
-            // ctstore contains the column types and their (translated) string representation appearing in the column types treeview.
-            ctstore = gtk_tree_view_get_model (info->ctreeview);
+        // ctstore contains the column types and their (translated) string representation appearing in the column types treeview.
+        ctstore = gtk_tree_view_get_model (info->ctreeview);
 
-            // Get an iterator for the first (and only) row.
-            gtk_tree_model_get_iter_first (ctstore, &iter);
+        // Get an iterator for the first (and only) row.
+        gtk_tree_model_get_iter_first (ctstore, &iter);
 
-            for (i=0; columns[i] != NULL; i++)
-            {
-                auto col_type = GncTransPropType::NONE;
-                int saved_col_type = atoi (columns[i]);
+        for (i=0; columns[i] != NULL && i < info->parse_data->column_types.size(); i++)
+        {
+            auto col_type = GncTransPropType::NONE;
+            int saved_col_type = atoi (columns[i]);
 
-                if (saved_col_type >= static_cast<int>(GncTransPropType::NONE) &&
-                    saved_col_type <= static_cast<int>(GncTransPropType::OMEMO))
-                {
-                    col_type = static_cast<GncTransPropType>(saved_col_type);
-                    info->parse_data->column_types.at(i) = col_type;
-                    /* Set the column type. Store is arranged so that every two
-                     * columns is a pair of
-                     * - the column type as a user visible (translated) string
-                     * - the internal type for this column
-                     * So ctstore looks like:
-                     * col_type_str 0, col_type, col_type_str 1, col_type 1, ..., col_type_str ncols, col_type ncols. */
-                    if ((2 * i + 1) < gtk_tree_model_get_n_columns (ctstore))
-                        gtk_list_store_set (GTK_LIST_STORE(ctstore), &iter,
-                                2 * i, _(gnc_csv_col_type_strs[col_type]),
-                                2 * i + 1, col_type,
-                                -1);
-                }
-                else
-                    error = true;
+            if (saved_col_type >= static_cast<int>(GncTransPropType::NONE) &&
+                saved_col_type <= static_cast<int>(GncTransPropType::OMEMO))
+            {
+                col_type = static_cast<GncTransPropType>(saved_col_type);
+                info->parse_data->column_types.at(i) = col_type;
+                /* Set the column type. Store is arranged so that every two
+                 * columns is a pair of
+                 * - the column type as a user visible (translated) string
+                 * - the internal type for this column
+                 * So ctstore looks like:
+                 * col_type_str 0, col_type, col_type_str 1, col_type 1, ..., col_type_str ncols, col_type ncols. */
+                if ((2 * i + 1) < static_cast<uint>(gtk_tree_model_get_n_columns (ctstore)))
+                    gtk_list_store_set (GTK_LIST_STORE(ctstore), &iter,
+                            2 * i, _(gnc_csv_col_type_strs[col_type]),
+                            2 * i + 1, col_type,
+                            -1);
             }
-            if (error)
-                gnc_error_dialog (NULL, "%s", _("There was a problem with the column types, please review."));
-
-            g_strfreev (columns);
+            else
+                error = true;
         }
+        if (error)
+            gnc_error_dialog (NULL, "%s", _("There was a problem with the column types, please review."));
+
+        g_strfreev (columns);
     }
-    g_free (group);
-    g_free (name);
 }
 
 
@@ -378,7 +362,7 @@ csv_import_trans_delete_settings_cb (GtkWidget *button, CsvImportTrans *info)
     GtkTreeIter   iter;
     GtkWidget    *dialog;
     gint          response;
-    gchar        *group = NULL, *name = NULL;
+    gchar        *group = NULL;
     const gchar  *title = _("Delete the Import Settings.");
 
     // Get the Key file
@@ -389,7 +373,7 @@ csv_import_trans_delete_settings_cb (GtkWidget *button, CsvImportTrans *info)
     {
         GtkTreeModel *model;
         model = gtk_combo_box_get_model (GTK_COMBO_BOX(info->settings_combo));
-        gtk_tree_model_get (model, &iter, SET_GROUP, &group, SET_NAME, &name, -1);
+        gtk_tree_model_get (model, &iter, SET_GROUP, &group, -1);
 
         if (g_strcmp0 (group, NULL) == 0)
         {
@@ -424,7 +408,6 @@ csv_import_trans_delete_settings_cb (GtkWidget *button, CsvImportTrans *info)
             }
         }
         g_free (group);
-        g_free (name);
     }
 }
 
@@ -683,7 +666,6 @@ csv_import_trans_file_chooser_confirm_cb (GtkWidget *button, CsvImportTrans *inf
     GtkAssistant *assistant = GTK_ASSISTANT(info->window);
     gint num = gtk_assistant_get_current_page (assistant);
     GtkWidget *page = gtk_assistant_get_nth_page (assistant, num);
-    GError* error = NULL;
 
     gtk_assistant_set_page_complete (assistant, page, FALSE);
 
@@ -711,20 +693,19 @@ csv_import_trans_file_chooser_confirm_cb (GtkWidget *button, CsvImportTrans *inf
     {
         parse_data->file_format (GncImpFileFormat::CSV);
         parse_data->load_file (info->file_name);
+        parse_data->parse (true);
     }
-    catch (std::ifstream::failure& ios_err)
+    catch (std::ifstream::failure& e)
     {
-        /* If we couldn't load the file ... */
-        gnc_error_dialog (NULL, "%s", ios_err.what());
+        /* File loading failed ... */
+        gnc_error_dialog (NULL, "%s", e.what());
             delete parse_data;
             return;
     }
-
-    /* Parse the data. */
-    if (parse_data->parse (true, &error))
+    catch (std::range_error &e)
     {
-        /* If we couldn't parse the data ... */
-        gnc_error_dialog (NULL, "%s", error->message);
+        /* Parsing failed ... */
+        gnc_error_dialog (NULL, "%s", e.what());
         delete parse_data;
         return;
     }
@@ -956,8 +937,11 @@ void sep_button_clicked (GtkWidget* widget, CsvImportTrans* info)
     /* Parse the data using the new options. We don't want to reguess
      * the column types because we want to leave the user's
      * configurations intact. */
-    GError* error;
-    if (info->parse_data->parse (false, &error))
+    try
+    {
+        info->parse_data->parse (false);
+    }
+    catch (std::range_error &e)
     {
         /* Warn the user there was a problem and try to undo what caused
          * the error. (This will cause a reparsing and ideally a usable
@@ -1005,6 +989,7 @@ static void separated_or_fixed_selected (GtkToggleButton* csv_button, CsvImportT
         catch (...)
         {
             // FIXME Handle file loading errors (possibly thrown by file_format above)
+            PWARN("Got an error during file loading");
         }
     }
 
@@ -1014,13 +999,7 @@ static void separated_or_fixed_selected (GtkToggleButton* csv_button, CsvImportT
         info->parse_data->file_format (GncImpFileFormat::FIXED_WIDTH);
 
         /* Reparse the data. */
-        GError* error = NULL;
-        if (info->parse_data->parse (false, &error))
-        {
-            /* Show an error dialog explaining the problem. */
-            gnc_error_dialog (NULL, "%s", error->message);
-            return;
-        }
+        info->parse_data->parse (false);
 
         /* Show the new data. */
         gnc_csv_preview_update_assist (info);
@@ -1028,10 +1007,16 @@ static void separated_or_fixed_selected (GtkToggleButton* csv_button, CsvImportT
         /* Refresh the row highlighting */
         row_selection_update (info);
     }
+    catch (std::range_error &e)
+    {
+        /* Parsing failed ... */
+        gnc_error_dialog (NULL, "%s", e.what());
+        return;
+    }
     catch (...)
     {
         // FIXME Handle file loading errors (possibly thrown by file_format above)
-        // or parse errors from parse above
+        PWARN("Got an error during file loading");
     }
 }
 
@@ -1060,11 +1045,14 @@ static void encoding_selected (GOCharmapSel* selector, const char* encoding,
     /* If this is the second time the function is called ... */
     if (info->encoding_selected_called)
     {
-        std::string previous_encoding = info->parse_data->tokenizer->encoding().c_str();
-        GError* error = NULL;
+        std::string previous_encoding = info->parse_data->tokenizer->encoding();
         /* Try converting the new encoding and reparsing. */
-        info->parse_data->convert_encoding (encoding);
-        if (info->parse_data->parse (false, &error))
+        try
+        {
+            info->parse_data->convert_encoding (encoding);
+            info->parse_data->parse (false);
+        }
+        catch (...)
         {
             /* If it fails, change back to the old encoding. */
             gnc_error_dialog (NULL, "%s", _("Invalid encoding selected"));
@@ -1219,10 +1207,13 @@ fixed_context_menu_handler (GnumericPopupMenuElement const *element,
         ; /* Nothing */
     }
 
-    GError* error = NULL;
-    if (info->parse_data->parse (false, &error))
+    try
+    {
+        info->parse_data->parse (false);
+    }
+    catch(std::range_error& e)
     {
-        gnc_error_dialog (NULL, "%s", error->message);
+        gnc_error_dialog (NULL, "%s", e.what());
         return FALSE;
     }
     gnc_csv_preview_update_assist (info);
@@ -1401,10 +1392,13 @@ split_column (CsvImportTrans* info, int col, int dx)
 
     GncFwTokenizer *fwtok = dynamic_cast<GncFwTokenizer*>(info->parse_data->tokenizer.get());
     fwtok->col_split (col, rel_pos);
-    GError* error = NULL;
-    if (info->parse_data->parse (false, &error))
+    try
+    {
+        info->parse_data->parse (false);
+    }
+    catch (std::range_error& e)
     {
-        gnc_error_dialog (NULL, "%s", error->message);
+        gnc_error_dialog (NULL, "%s", e.what());
         return;
     }
     gnc_csv_preview_update_assist (info);
diff --git a/src/import-export/csv-imp/gnc-tx-import.cpp b/src/import-export/csv-imp/gnc-tx-import.cpp
index 4015470..73c5603 100644
--- a/src/import-export/csv-imp/gnc-tx-import.cpp
+++ b/src/import-export/csv-imp/gnc-tx-import.cpp
@@ -260,8 +260,6 @@ GncImpFileFormat GncTxImport::file_format()
  * the wrong encoding.
  * @param parse_data Data that is being parsed
  * @param encoding Encoding that data should be translated using
- * @param error Will point to an error on failure
- * @return 0 on success, 1 on failure
  */
 void GncTxImport::convert_encoding (const std::string& encoding)
 {
@@ -279,9 +277,7 @@ void GncTxImport::convert_encoding (const std::string& encoding)
  * called until it succeeds.
  * @param parse_data Data that is being parsed
  * @param filename Name of the file that should be opened
- * @param error Will contain an error if there is a failure
  * @exception may throw std::ifstream::failure on any io error
- * @return 0 on success, 1 on failure
  */
 void GncTxImport::load_file (const std::string& filename)
 {
@@ -307,12 +303,10 @@ void GncTxImport::load_file (const std::string& filename)
  * TRUE before it is ever called with it as FALSE.) (Note: if
  * guessColTypes is TRUE, all the column types will be GncTransPropType::NONE
  * right now.)
- * @param parse_data Data that is being parsed
  * @param guessColTypes TRUE to guess what the types of columns are based on the cell contents
- * @param error Will contain an error if there is a failure
- * @return 0 on success, 1 on failure
+ * @exception throws std::range_error if parsing failed
  */
-int GncTxImport::parse (bool guessColTypes, GError** error)
+void GncTxImport::parse (bool guessColTypes)
 {
     uint max_cols = 0;
     tokenizer->tokenize();
@@ -328,8 +322,8 @@ int GncTxImport::parse (bool guessColTypes, GError** error)
     /* If it failed, generate an error. */
     if (orig_lines.size() == 0)
     {
-        g_set_error (error, GNC_CSV_IMP_ERROR, GNC_CSV_IMP_ERROR_PARSE, "Parsing failed.");
-        return 1;
+        throw (std::range_error ("Parsing failed."));
+        return;
     }
 
     if (guessColTypes)
@@ -345,7 +339,6 @@ int GncTxImport::parse (bool guessColTypes, GError** error)
          * on the contents of each column. */
         /* TODO Make it actually guess. */
     }
-    return 0;
 }
 
 
diff --git a/src/import-export/csv-imp/gnc-tx-import.hpp b/src/import-export/csv-imp/gnc-tx-import.hpp
index 67c97c6..19ee290 100644
--- a/src/import-export/csv-imp/gnc-tx-import.hpp
+++ b/src/import-export/csv-imp/gnc-tx-import.hpp
@@ -133,7 +133,7 @@ public:
     void load_file (const std::string& filename);
     void convert_encoding (const std::string& encoding);
 
-    int parse (bool guessColTypes, GError** error);
+    void parse (bool guessColTypes);
     int parse_to_trans (Account* account, bool redo_errors);
     bool check_for_column_type (GncTransPropType type);
 

commit d64c66e68d8f6dbef40a615a323617d026927da9
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Mon Oct 10 23:01:05 2016 +0200

    Replace GError usage in file_format and load_file with proper try catch blocks

diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index 8b4a48c..bad00c7 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -256,18 +256,25 @@ csv_import_trans_load_settings (CsvImportTrans *info)
         // This Section deals with the separators
         if (info->settings_data->csv_format)
         {
-            info->parse_data->file_format (GncImpFileFormat::CSV, NULL);
-            for (i = 0; i < SEP_NUM_OF_TYPES; i++)
+            try
             {
-                gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->sep_buttons[i]), info->settings_data->separator[i]);
-            }
-            gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->custom_cbutton), info->settings_data->custom);
-            if (info->settings_data->custom)
-                gtk_entry_set_text (GTK_ENTRY(info->custom_entry), info->settings_data->custom_entry);
-            else
-                gtk_entry_set_text (GTK_ENTRY(info->custom_entry), "");
+                info->parse_data->file_format (GncImpFileFormat::CSV);
+                for (i = 0; i < SEP_NUM_OF_TYPES; i++)
+                {
+                    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->sep_buttons[i]), info->settings_data->separator[i]);
+                }
+                gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->custom_cbutton), info->settings_data->custom);
+                if (info->settings_data->custom)
+                    gtk_entry_set_text (GTK_ENTRY(info->custom_entry), info->settings_data->custom_entry);
+                else
+                    gtk_entry_set_text (GTK_ENTRY(info->custom_entry), "");
 
-            sep_button_clicked (NULL, info);
+                sep_button_clicked (NULL, info);
+            }
+            catch (...)
+            {
+                // FIXME Handle file loading errors (possibly thrown by file_format above)
+            }
         }
 
         // This section deals with the combo's and character encoding
@@ -282,21 +289,28 @@ csv_import_trans_load_settings (CsvImportTrans *info)
         // This section deals with the column widths (which are only used for fixed width files)
         if (!info->settings_data->csv_format)
         {
-            info->parse_data->file_format (GncImpFileFormat::FIXED_WIDTH, NULL);
-            GncFwTokenizer *fwtok = dynamic_cast<GncFwTokenizer*>(info->parse_data->tokenizer.get());
-            if (info->settings_data->column_widths, NULL)
-                fwtok->cols_from_string (std::string(info->settings_data->column_widths));
+            try
+            {
+                info->parse_data->file_format (GncImpFileFormat::FIXED_WIDTH);
+                GncFwTokenizer *fwtok = dynamic_cast<GncFwTokenizer*>(info->parse_data->tokenizer.get());
+                if (info->settings_data->column_widths, NULL)
+                    fwtok->cols_from_string (std::string(info->settings_data->column_widths));
 
-            GError  *error = NULL;
-            if (info->parse_data->parse (false, &error))
+                GError  *error = NULL;
+                if (info->parse_data->parse (false, &error))
+                {
+                    gnc_error_dialog (NULL, "%s", _("There was a problem with the column widths, please review."));
+                    g_error_free (error);
+                    g_free (group);
+                    g_free (name);
+                    return;
+                }
+                gnc_csv_preview_update_assist (info);
+            }
+            catch (...)
             {
-                gnc_error_dialog (NULL, "%s", _("There was a problem with the column widths, please review."));
-                g_error_free (error);
-                g_free (group);
-                g_free (name);
-                return;
+                // FIXME Handle file loading errors (possibly thrown by file_format above)
             }
-            gnc_csv_preview_update_assist (info);
         }
 
         // This section deals with the column types
@@ -693,19 +707,17 @@ csv_import_trans_file_chooser_confirm_cb (GtkWidget *button, CsvImportTrans *inf
     /* Load the file into parse_data. */
     auto parse_data = new GncTxImport;
     /* Assume data is CSV. User can later override to Fixed Width if needed */
-    parse_data->file_format (GncImpFileFormat::CSV, &error);
-    if (parse_data->load_file (info->file_name, &error))
+    try
+    {
+        parse_data->file_format (GncImpFileFormat::CSV);
+        parse_data->load_file (info->file_name);
+    }
+    catch (std::ifstream::failure& ios_err)
     {
         /* If we couldn't load the file ... */
-        gnc_error_dialog (NULL, "%s", error->message);
-        if (error->code == GNC_CSV_IMP_ERROR_OPEN)
-        {
+        gnc_error_dialog (NULL, "%s", ios_err.what());
             delete parse_data;
             return;
-        }
-        /* If we couldn't guess the encoding, we are content with just
-         * displaying an error message and move on with a blank
-         * display. */
     }
 
     /* Parse the data. */
@@ -983,29 +995,44 @@ static void separated_or_fixed_selected (GtkToggleButton* csv_button, CsvImportT
     /* Set the parsing type correctly. */
     if (gtk_toggle_button_get_active (csv_button)) /* If we're in CSV mode ... */
     {
-        info->parse_data->file_format (GncImpFileFormat::CSV, NULL);
-        sep_button_clicked (NULL, info);
-        // Note: sep_button_clicked also handles reparsing the data, so we're done here
-        return;
+        try
+        {
+            info->parse_data->file_format (GncImpFileFormat::CSV);
+            sep_button_clicked (NULL, info);
+            // Note: sep_button_clicked also handles reparsing the data, so we're done here
+            return;
+        }
+        catch (...)
+        {
+            // FIXME Handle file loading errors (possibly thrown by file_format above)
+        }
     }
 
     /* So we're in fixed-width mode ... */
-    info->parse_data->file_format (GncImpFileFormat::FIXED_WIDTH, NULL);
-
-    /* Reparse the data. */
-    GError* error = NULL;
-    if (info->parse_data->parse (false, &error))
+    try
     {
-        /* Show an error dialog explaining the problem. */
-        gnc_error_dialog (NULL, "%s", error->message);
-        return;
-    }
+        info->parse_data->file_format (GncImpFileFormat::FIXED_WIDTH);
 
-    /* Show the new data. */
-    gnc_csv_preview_update_assist (info);
+        /* Reparse the data. */
+        GError* error = NULL;
+        if (info->parse_data->parse (false, &error))
+        {
+            /* Show an error dialog explaining the problem. */
+            gnc_error_dialog (NULL, "%s", error->message);
+            return;
+        }
 
-    /* Refresh the row highlighting */
-    row_selection_update (info);
+        /* Show the new data. */
+        gnc_csv_preview_update_assist (info);
+
+        /* Refresh the row highlighting */
+        row_selection_update (info);
+    }
+    catch (...)
+    {
+        // FIXME Handle file loading errors (possibly thrown by file_format above)
+        // or parse errors from parse above
+    }
 }
 
 
diff --git a/src/import-export/csv-imp/gnc-tx-import.cpp b/src/import-export/csv-imp/gnc-tx-import.cpp
index 21a1aa9..4015470 100644
--- a/src/import-export/csv-imp/gnc-tx-import.cpp
+++ b/src/import-export/csv-imp/gnc-tx-import.cpp
@@ -220,11 +220,17 @@ GncTxImport::~GncTxImport()
 {
 }
 
-int GncTxImport::file_format(GncImpFileFormat format,
-                                  GError** error)
+/** Sets the file format for the file to import, which
+ *  may cause the file to be reloaded as well if the
+ *  previously set file format was different and a
+ *  filename was already set.
+ *  @param format the new format to set
+ *  @exception the reloading of the file may throw std::ifstream::failure
+ */
+void GncTxImport::file_format(GncImpFileFormat format)
 {
     if (file_fmt == format)
-        return 0;
+        return;
 
     auto new_encoding = std::string("UTF-8");
     auto new_imp_file = std::string();
@@ -242,7 +248,7 @@ int GncTxImport::file_format(GncImpFileFormat format,
     // Set up new tokenizer with common settings
     // recovered from old tokenizer
     tokenizer->encoding(new_encoding);
-    return load_file(new_imp_file, error);
+    load_file(new_imp_file);
 }
 GncImpFileFormat GncTxImport::file_format()
 {
@@ -274,25 +280,23 @@ void GncTxImport::convert_encoding (const std::string& encoding)
  * @param parse_data Data that is being parsed
  * @param filename Name of the file that should be opened
  * @param error Will contain an error if there is a failure
+ * @exception may throw std::ifstream::failure on any io error
  * @return 0 on success, 1 on failure
  */
-int GncTxImport::load_file (const std::string& filename,
-                                GError** error)
+void GncTxImport::load_file (const std::string& filename)
 {
 
     /* Get the raw data first and handle an error if one occurs. */
     try
     {
         tokenizer->load_file (filename);
-        return 0;
+        return;
     }
     catch (std::ifstream::failure& ios_err)
     {
-        /* TODO Handle file opening errors more specifically,
-         * e.g. inexistent file versus no read permission. */
+        // Just log the error and pass it on the call stack for proper handling
         PWARN ("Error: %s", ios_err.what());
-        g_set_error (error, GNC_CSV_IMP_ERROR, GNC_CSV_IMP_ERROR_OPEN, "%s", _("File opening failed."));
-        return 1;
+        throw;
     }
 }
 
diff --git a/src/import-export/csv-imp/gnc-tx-import.hpp b/src/import-export/csv-imp/gnc-tx-import.hpp
index eef1b67..67c97c6 100644
--- a/src/import-export/csv-imp/gnc-tx-import.hpp
+++ b/src/import-export/csv-imp/gnc-tx-import.hpp
@@ -127,10 +127,10 @@ public:
     GncTxImport(GncImpFileFormat format = GncImpFileFormat::UNKNOWN);
     ~GncTxImport();
 
-    int file_format(GncImpFileFormat format, GError** error);
+    void file_format(GncImpFileFormat format);
     GncImpFileFormat file_format();
 
-    int load_file (const std::string& filename, GError** error);
+    void load_file (const std::string& filename);
     void convert_encoding (const std::string& encoding);
 
     int parse (bool guessColTypes, GError** error);

commit b9e73d923e96884e7ac5233274b7786bfa43c2cb
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sat Jan 14 15:56:34 2017 +0100

    Throw in case date can't be parsed instead of returning -1 as date

diff --git a/src/import-export/csv-imp/gnc-tx-import.cpp b/src/import-export/csv-imp/gnc-tx-import.cpp
index 06b0a80..21a1aa9 100644
--- a/src/import-export/csv-imp/gnc-tx-import.cpp
+++ b/src/import-export/csv-imp/gnc-tx-import.cpp
@@ -149,20 +149,21 @@ std::map<GncTransPropType, const char*> gnc_csv_col_type_strs = {
  * 01/02/2003.
  * @param date_str The string containing a date being parsed
  * @param format An index specifying a format in date_format_user
- * @return The parsed value of date_str on success or -1 on failure
+ * @exception std::invalid_argument if the string can't be parsed into a date.
+ * @return The parsed value of date_str on success, throws on failure
  */
 time64 parse_date (const std::string &date_str, int format)
 {
     boost::regex r(date_regex[format]);
     boost::smatch what;
     if(!boost::regex_search(date_str, what, r))
-        return -1;  // regex didn't find a match
+        throw std::invalid_argument ("String doesn't appear to be formatted as a date.");  // regex didn't find a match
 
     // Attention: different behavior from 2.6.x series !
     // If date format without year was selected, the match
     // should NOT have found a year.
     if ((format >= 3) && (what.length("YEAR") != 0))
-        return -1;
+        throw std::invalid_argument ("String appears to contain a year while the selected format forbids this.");
 
     auto day = std::stoi (what.str("DAY"));
     auto month = std::stoi (what.str("MONTH"));
@@ -351,15 +352,17 @@ int GncTxImport::parse (bool guessColTypes, GError** error)
  */
 static time64* convert_date_col_str (const std::string &str, int date_format)
 {
-    auto parsed_date = parse_date (str.c_str(), date_format);
-    if (parsed_date == -1)
-        return nullptr;
-    else
+    try
     {
+        auto parsed_date = parse_date (str.c_str(), date_format);
         auto mydate = new time64;
         *mydate = parsed_date;
         return mydate;
     }
+    catch (std::invalid_argument)
+    {
+        return nullptr;
+    }
 }
 
 
diff --git a/src/import-export/csv-imp/test/test-tx-import.cpp b/src/import-export/csv-imp/test/test-tx-import.cpp
index ff1b612..1df18a4 100644
--- a/src/import-export/csv-imp/test/test-tx-import.cpp
+++ b/src/import-export/csv-imp/test/test-tx-import.cpp
@@ -69,7 +69,7 @@ time64 parse_date (const char* date_str, int format)// C: 14 in 7 SCM: 9 in 2 Lo
 */
 TEST(GncTxImportTest, parse_date)
 {
-    time64 rawtime = gnc_time (NULL);
+    time64 rawtime = gnc_time (nullptr);
     struct tm *tm = gnc_gmtime (&rawtime);
     int curr_year = tm->tm_year;
 
@@ -177,17 +177,19 @@ TEST(GncTxImportTest, parse_date)
         gboolean success = TRUE;
         int got_year = 0, got_month = 0, got_day = 0;
 
-        rawtime = parse_date (std::string(test_dates[i].date_str), test_dates[i].date_fmt);
-        if (rawtime == -1)
-            got_year = got_month = got_day = -1;
-        else
+        try
         {
+            rawtime = parse_date (std::string(test_dates[i].date_str), test_dates[i].date_fmt);
             tm = gnc_gmtime (&rawtime);
             got_year = tm->tm_year;
             got_month = tm->tm_mon;
             got_day = tm->tm_mday;
             gnc_tm_free(tm);
         }
+        catch (std::invalid_argument)
+        {
+            got_year = got_month = got_day = -1;
+        }
 
         if ((got_year  != test_dates[i].exp_year) ||
             (got_month != test_dates[i].exp_month) ||

commit bbac6aa1c5ff7d26cf091e6195f5894b22967b42
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Mon Oct 10 16:09:24 2016 +0200

    Various small C++ and other cleanups
    
    - Use CamelCase for type definitions and _ for function names
    - m_ for member variables
    - avoid typedef (not needed for structs, replace by using for aliases)
    - use std::string for composing strings
    - fix condition part in for loop (can't use , need &&)
    - markup comment for doxygen inclusion

diff --git a/src/import-export/csv-imp/gnc-csv-tokenizer.cpp b/src/import-export/csv-imp/gnc-csv-tokenizer.cpp
index 8e7ebaf..6a24826 100644
--- a/src/import-export/csv-imp/gnc-csv-tokenizer.cpp
+++ b/src/import-export/csv-imp/gnc-csv-tokenizer.cpp
@@ -23,7 +23,7 @@ int GncCsvTokenizer::tokenize()
 
     boost::escaped_list_separator<char> sep("\\", m_sep_str, "\"");
 
-    std::vector<std::string> vec;
+    StrVec vec;
     std::string line;
     std::string buffer;
 
diff --git a/src/import-export/csv-imp/gnc-dummy-tokenizer.cpp b/src/import-export/csv-imp/gnc-dummy-tokenizer.cpp
index 87902b7..c54517e 100644
--- a/src/import-export/csv-imp/gnc-dummy-tokenizer.cpp
+++ b/src/import-export/csv-imp/gnc-dummy-tokenizer.cpp
@@ -12,7 +12,7 @@
 
 int GncDummyTokenizer::tokenize()
 {
-    std::vector<std::string> vec;
+    StrVec vec;
     std::string line;
 
     m_tokenized_contents.clear();
diff --git a/src/import-export/csv-imp/gnc-fw-tokenizer.cpp b/src/import-export/csv-imp/gnc-fw-tokenizer.cpp
index 9bad3d4..4d85751 100644
--- a/src/import-export/csv-imp/gnc-fw-tokenizer.cpp
+++ b/src/import-export/csv-imp/gnc-fw-tokenizer.cpp
@@ -194,11 +194,11 @@ void GncFwTokenizer::load_file(const std::string& path)
 
 int GncFwTokenizer::tokenize()
 {
-    typedef boost::tokenizer< boost::offset_separator > Tokenizer;
+    using Tokenizer = boost::tokenizer< boost::offset_separator > ;
 
     boost::offset_separator sep(m_col_vec.begin(), m_col_vec.end(), false);
 
-    std::vector<std::string> vec;
+    StrVec vec;
     std::string line;
     std::string buffer;
 
diff --git a/src/import-export/csv-imp/gnc-tokenizer.cpp b/src/import-export/csv-imp/gnc-tokenizer.cpp
index 51cd77d..cd7640c 100644
--- a/src/import-export/csv-imp/gnc-tokenizer.cpp
+++ b/src/import-export/csv-imp/gnc-tokenizer.cpp
@@ -17,7 +17,7 @@ extern "C" {
 #include <goffice/go-glib-extras.h>
 }
 
-std::unique_ptr<GncTokenizer> GncTokenizerFactory(GncImpFileFormat fmt)
+std::unique_ptr<GncTokenizer> gnc_tokenizer_factory(GncImpFileFormat fmt)
 {
     std::unique_ptr<GncTokenizer> tok(nullptr);
     switch (fmt)
@@ -88,7 +88,7 @@ GncTokenizer::encoding()
 }
 
 
-const std::vector<str_vec>&
+const std::vector<StrVec>&
 GncTokenizer::get_tokens()
 {
     return m_tokenized_contents;
diff --git a/src/import-export/csv-imp/gnc-tokenizer.hpp b/src/import-export/csv-imp/gnc-tokenizer.hpp
index 0e5363b..73fc472 100644
--- a/src/import-export/csv-imp/gnc-tokenizer.hpp
+++ b/src/import-export/csv-imp/gnc-tokenizer.hpp
@@ -45,7 +45,7 @@ extern "C" {
 #include <string>
 #include <memory>
 
-using str_vec = std::vector<std::string>;
+using StrVec = std::vector<std::string>;
 
 /** Enumeration for file formats supported by this importer. */
 enum class GncImpFileFormat {
@@ -72,11 +72,11 @@ public:
     void encoding(const std::string& encoding);
     const std::string& encoding();
     virtual int  tokenize() = 0;
-    const std::vector<str_vec>& get_tokens();
+    const std::vector<StrVec>& get_tokens();
     
 protected:
     std::string m_utf8_contents;
-    std::vector<str_vec> m_tokenized_contents;
+    std::vector<StrVec> m_tokenized_contents;
 
 private:
     std::string m_imp_file_str;
@@ -86,6 +86,6 @@ private:
 
 
 // Function to instantiate specializations of the GncTokenizer
-std::unique_ptr<GncTokenizer> GncTokenizerFactory(GncImpFileFormat fmt);
+std::unique_ptr<GncTokenizer> gnc_tokenizer_factory(GncImpFileFormat fmt);
 
 #endif
diff --git a/src/import-export/csv-imp/gnc-tx-import.cpp b/src/import-export/csv-imp/gnc-tx-import.cpp
index b6208e2..06b0a80 100644
--- a/src/import-export/csv-imp/gnc-tx-import.cpp
+++ b/src/import-export/csv-imp/gnc-tx-import.cpp
@@ -210,7 +210,7 @@ GncTxImport::GncTxImport(GncImpFileFormat format)
     parse_errors = false;
 
     file_fmt = format;
-    tokenizer = GncTokenizerFactory(file_fmt);
+    tokenizer = gnc_tokenizer_factory(file_fmt);
 }
 
 /** Destructor for GncTxImport.
@@ -236,7 +236,7 @@ int GncTxImport::file_format(GncImpFileFormat format,
     }
 
     file_fmt = format;
-    tokenizer = GncTokenizerFactory(file_fmt);
+    tokenizer = gnc_tokenizer_factory(file_fmt);
 
     // Set up new tokenizer with common settings
     // recovered from old tokenizer
@@ -431,7 +431,7 @@ public:
     static GncTransProperty* make_new(const std::string& val,int fmt = 0)
         { return nullptr; }
 
-    T value;
+    T m_value;
 };
 
 template<>
@@ -440,16 +440,16 @@ struct GncTransPropImpl<time64*>
 {
     GncTransPropImpl(const std::string& val, int fmt)
     {
-        value = convert_date_col_str (val, fmt);
-        m_valid = (value != nullptr);
+        m_value = convert_date_col_str (val, fmt);
+        m_valid = (m_value != nullptr);
     }
     ~GncTransPropImpl()
-        { if (value) delete value; }
+        { if (m_value) delete m_value; }
 
     static std::shared_ptr<GncTransProperty> make_new(const std::string& val,int fmt)
     { return std::shared_ptr<GncTransProperty>(new GncTransPropImpl<time64*>(val, fmt)); }
 
-    time64* value;
+    time64* m_value;
 };
 
 
@@ -459,16 +459,16 @@ struct GncTransPropImpl<std::string*>
 {
     GncTransPropImpl(const std::string& val, int fmt = 0)
     {
-        value = new std::string(val);
-        m_valid = (value != nullptr);
+        m_value = new std::string(val);
+        m_valid = (m_value != nullptr);
     }
     ~GncTransPropImpl()
-        { if (value) delete value; }
+        { if (m_value) delete m_value; }
 
     static std::shared_ptr<GncTransProperty> make_new(const std::string& val,int fmt = 0)
         { return std::shared_ptr<GncTransProperty>(new GncTransPropImpl<std::string*>(val)); } /* Note fmt is not used for strings */
 
-    std::string* value;
+    std::string* m_value;
 };
 
 template<>
@@ -477,17 +477,17 @@ struct GncTransPropImpl<Account *>
 {
     GncTransPropImpl(const std::string& val, int fmt = 0)
     {
-        value = gnc_csv_account_map_search (val.c_str());
-        m_valid = (value != nullptr);
+        m_value = gnc_csv_account_map_search (val.c_str());
+        m_valid = (m_value != nullptr);
     }
     GncTransPropImpl(Account* val)
-        { value = val; }
+        { m_value = val; }
     ~GncTransPropImpl(){};
 
     static std::shared_ptr<GncTransProperty> make_new(const std::string& val,int fmt = 0)
         { return std::shared_ptr<GncTransProperty>(new GncTransPropImpl<Account*>(val)); } /* Note fmt is not used in for accounts */
 
-    Account* value;
+    Account * m_value;
 };
 
 template<>
@@ -496,16 +496,16 @@ struct GncTransPropImpl<gnc_numeric *>
 {
     GncTransPropImpl(const std::string& val, int fmt)
     {
-        value = convert_amount_col_str (val, fmt);
-        m_valid = (value != nullptr);
+        m_value = convert_amount_col_str (val, fmt);
+        m_valid = (m_value != nullptr);
     }
     ~GncTransPropImpl()
-        { if (value) delete value; }
+        { if (m_value) delete m_value; }
 
     static std::shared_ptr<GncTransProperty> make_new(const std::string& val,int fmt)
     { return std::shared_ptr<GncTransProperty>(new GncTransPropImpl<gnc_numeric*>(val, fmt)); }
 
-    gnc_numeric* value;
+    gnc_numeric * m_value;
 };
 
 /** Adds a split to a transaction.
@@ -585,7 +585,7 @@ static GncCsvTransLine* trans_properties_to_trans (prop_map_t& trans_props, gcha
         return NULL;
 
     auto property = trans_props.find (GncTransPropType::ACCOUNT)->second;
-    auto account = dynamic_cast<GncTransPropImpl<Account*>*>(property.get())->value;
+    auto account = dynamic_cast<GncTransPropImpl<Account*>*>(property.get())->m_value;
 
     GncCsvTransLine* trans_line = g_new (GncCsvTransLine, 1);
 
@@ -621,47 +621,47 @@ static GncCsvTransLine* trans_properties_to_trans (prop_map_t& trans_props, gcha
         {
         case GncTransPropType::DATE:
             {
-                auto transdate = dynamic_cast<GncTransPropImpl<time64*>*>(prop.get())->value;
+                auto transdate = dynamic_cast<GncTransPropImpl<time64*>*>(prop.get())->m_value;
                 xaccTransSetDatePostedSecsNormalized (trans_line->trans, *transdate);
             }
             break;
 
         case GncTransPropType::DESCRIPTION:
             {
-                auto propstring = dynamic_cast<GncTransPropImpl<std::string*>*>(prop.get())->value;
+                auto propstring = dynamic_cast<GncTransPropImpl<std::string*>*>(prop.get())->m_value;
                 xaccTransSetDescription (trans_line->trans, propstring->c_str());
             }
             break;
 
         case GncTransPropType::NOTES:
             {
-                auto propstring = dynamic_cast<GncTransPropImpl<std::string*>*>(prop.get())->value;
+                auto propstring = dynamic_cast<GncTransPropImpl<std::string*>*>(prop.get())->m_value;
                 xaccTransSetNotes (trans_line->trans, propstring->c_str());
             }
             break;
 
         case GncTransPropType::OACCOUNT:
-            oaccount = dynamic_cast<GncTransPropImpl<Account*>*>(prop.get())->value;
+            oaccount = dynamic_cast<GncTransPropImpl<Account*>*>(prop.get())->m_value;
             break;
 
         case GncTransPropType::MEMO:
-            memo = *dynamic_cast<GncTransPropImpl<std::string*>*>(prop.get())->value;
+            memo = *dynamic_cast<GncTransPropImpl<std::string*>*>(prop.get())->m_value;
             break;
 
         case GncTransPropType::OMEMO:
-            omemo = *dynamic_cast<GncTransPropImpl<std::string*>*>(prop.get())->value;
+            omemo = *dynamic_cast<GncTransPropImpl<std::string*>*>(prop.get())->m_value;
             break;
 
         case GncTransPropType::NUM:
             /* the 'num' is saved and passed to 'trans_add_split' below where
              * 'gnc_set_num_action' is used to set tran-num and/or split-action
              * per book option */
-            num = *dynamic_cast<GncTransPropImpl<std::string*>*>(prop.get())->value;
+            num = *dynamic_cast<GncTransPropImpl<std::string*>*>(prop.get())->m_value;
             break;
 
         case GncTransPropType::DEPOSIT: /* Add deposits to the existing amount. */
             {
-                auto propval = dynamic_cast<GncTransPropImpl<gnc_numeric*>*>(prop.get())->value;
+                auto propval = dynamic_cast<GncTransPropImpl<gnc_numeric*>*>(prop.get())->m_value;
                 amount = gnc_numeric_add (*propval,
                                          amount,
                                          xaccAccountGetCommoditySCU (account),
@@ -674,7 +674,7 @@ static GncCsvTransLine* trans_properties_to_trans (prop_map_t& trans_props, gcha
 
         case GncTransPropType::WITHDRAWAL: /* Withdrawals are just negative deposits. */
             {
-                auto propval = dynamic_cast<GncTransPropImpl<gnc_numeric*>*>(prop.get())->value;
+                auto propval = dynamic_cast<GncTransPropImpl<gnc_numeric*>*>(prop.get())->m_value;
                 amount = gnc_numeric_add (gnc_numeric_neg(*propval),
                                          amount,
                                          xaccAccountGetCommoditySCU (account),
@@ -689,7 +689,7 @@ static GncCsvTransLine* trans_properties_to_trans (prop_map_t& trans_props, gcha
             /* We will use the "Deposit" and "Withdrawal" columns in preference to "Balance". */
             if (!amount_set)
             {
-                auto propval = dynamic_cast<GncTransPropImpl<gnc_numeric*>*>(prop.get())->value;
+                auto propval = dynamic_cast<GncTransPropImpl<gnc_numeric*>*>(prop.get())->m_value;
                 /* This gets put into the actual transaction at the end of gnc_csv_parse_to_trans. */
                 trans_line->balance = *propval;
                 trans_line->balance_set = true;
@@ -777,7 +777,7 @@ int GncTxImport::parse_to_trans (Account* account,
         auto col_types_it = column_types.cbegin();
         auto line_it = line.cbegin();
         for (col_types_it, line_it;
-                col_types_it != column_types.cend(),
+                col_types_it != column_types.cend() &&
                 line_it != line.cend();
                 ++col_types_it, ++line_it)
         {
@@ -800,7 +800,7 @@ int GncTxImport::parse_to_trans (Account* account,
                 case GncTransPropType::OACCOUNT:
                     property = GncTransPropImpl<Account*>::make_new (*line_it);
                     if (*col_types_it == GncTransPropType::ACCOUNT)
-                        home_account = dynamic_cast<GncTransPropImpl<Account*>*>(property.get())->value;
+                        home_account = dynamic_cast<GncTransPropImpl<Account*>*>(property.get())->m_value;
                     break;
 
                 case GncTransPropType::BALANCE:
@@ -819,11 +819,9 @@ int GncTxImport::parse_to_trans (Account* account,
             else
             {
                 parse_errors = loop_err = true;
-                gchar *error_message = g_strdup_printf (_("%s column could not be understood."),
-                                                _(gnc_csv_col_type_strs[*col_types_it]));
-                orig_lines_it->second = error_message;
-
-                g_free (error_message);
+                std::string error_message {_(gnc_csv_col_type_strs[*col_types_it])};
+                error_message += _(" column could not be understood.");
+                orig_lines_it->second = std::move(error_message);
                 break;
             }
         }
diff --git a/src/import-export/csv-imp/gnc-tx-import.hpp b/src/import-export/csv-imp/gnc-tx-import.hpp
index f22ef8c..eef1b67 100644
--- a/src/import-export/csv-imp/gnc-tx-import.hpp
+++ b/src/import-export/csv-imp/gnc-tx-import.hpp
@@ -91,13 +91,13 @@ enum GncCsvErrorType {
  * "correct" the error list. If we keep the line numbers of valid
  * transactions, we can then put transactions created from the newly
  * corrected rows into the right places. */
-typedef struct
+struct GncCsvTransLine
 {
     int line_no;
     Transaction* trans;
     gnc_numeric balance;  /**< The (supposed) balance after this transaction takes place */
     bool balance_set;     /**< true if balance has been set from user data, false otherwise */
-} GncCsvTransLine;
+};
 
 /* A set of currency formats that the user sees. */
 extern const int num_currency_formats;
@@ -108,9 +108,9 @@ extern const int num_date_formats;
 extern const gchar* date_format_user[];
 
 /** Pair to hold a tokenized line of input and an optional error string */
-using parse_line_t = std::pair<str_vec, std::string>;
+using parse_line_t = std::pair<StrVec, std::string>;
 
-/* The actual TxImport class
+/** The actual TxImport class
  * It's intended to use in the following sequence of actions:
  * - set a file format
  * - load a file
diff --git a/src/import-export/csv-imp/test/test-tokenizer.cpp b/src/import-export/csv-imp/test/test-tokenizer.cpp
index a434e33..65c2d27 100644
--- a/src/import-export/csv-imp/test/test-tokenizer.cpp
+++ b/src/import-export/csv-imp/test/test-tokenizer.cpp
@@ -56,8 +56,8 @@ class GncTokenizerTest : public ::testing::Test
 public:
     GncTokenizerTest()
     {
-        fw_tok = GncTokenizerFactory(GncImpFileFormat::FIXED_WIDTH);
-        csv_tok = GncTokenizerFactory(GncImpFileFormat::CSV);
+        fw_tok = gnc_tokenizer_factory(GncImpFileFormat::FIXED_WIDTH);
+        csv_tok = gnc_tokenizer_factory(GncImpFileFormat::CSV);
     }
 
     std::string get_filepath(const std::string& filename);

commit 2638056203f8065664469fc81247f71d46adc0e9
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Mon Oct 10 15:13:36 2016 +0200

    Use gnc-date functions while parsing date strings for consistent timezone handling

diff --git a/src/import-export/csv-imp/gnc-tx-import.cpp b/src/import-export/csv-imp/gnc-tx-import.cpp
index 0c12a11..b6208e2 100644
--- a/src/import-export/csv-imp/gnc-tx-import.cpp
+++ b/src/import-export/csv-imp/gnc-tx-import.cpp
@@ -153,9 +153,6 @@ std::map<GncTransPropType, const char*> gnc_csv_col_type_strs = {
  */
 time64 parse_date (const std::string &date_str, int format)
 {
-    struct tm retvalue, test_retvalue; /* The time in a broken-down structure */
-    int orig_year = -1, orig_month = -1, orig_day = -1;
-
     boost::regex r(date_regex[format]);
     boost::smatch what;
     if(!boost::regex_search(date_str, what, r))
@@ -167,56 +164,34 @@ time64 parse_date (const std::string &date_str, int format)
     if ((format >= 3) && (what.length("YEAR") != 0))
         return -1;
 
+    auto day = std::stoi (what.str("DAY"));
+    auto month = std::stoi (what.str("MONTH"));
 
-    /* Put some sane values in retvalue by using a fixed time for
-     * the non-year-month-day parts of the date. */
-    time64 rawtime; /* The integer time */
-    gnc_time (&rawtime);
-    gnc_localtime_r (&rawtime, &retvalue);
-    retvalue.tm_hour = 11;
-    retvalue.tm_min = 0;
-    retvalue.tm_sec = 0;
-    retvalue.tm_isdst = -1;
-
-    retvalue.tm_mday = std::stoi (what.str("DAY"));
-    retvalue.tm_mon = std::stoi (what.str("MONTH")) - 1;
-
+    int year;
     if (format < 3)
     {
-        retvalue.tm_year = std::stoi (what.str("YEAR"));
+        /* The input dates have a year, so use that one */
+        year = std::stoi (what.str("YEAR"));
 
         /* Handle two-digit years. */
-        if (retvalue.tm_year < 100)
+        if (year < 100)
         {
             /* We allow two-digit years in the range 1969 - 2068. */
-            if (retvalue.tm_year < 69)
-                retvalue.tm_year += 100;
+            if (year < 69)
+                year += 2000;
+            else
+                year += 1900;
         }
-        else
-            retvalue.tm_year -= 1900;
     }
-
-    /* Convert back to an integer. If gnc_mktime leaves retvalue unchanged,
-     * everything is okay; otherwise, an error has occurred. */
-    /* We have to use a "test" date value to account for changes in
-     * daylight savings time, which can cause a date change with gnc_mktime
-     * near midnight, causing the code to incorrectly think a date is
-     * incorrect. */
-
-    orig_day   = retvalue.tm_mday;
-    orig_month = retvalue.tm_mon;
-    orig_year  = retvalue.tm_year;
-
-    test_retvalue = retvalue;
-    gnc_mktime (&test_retvalue);
-    retvalue.tm_isdst = test_retvalue.tm_isdst;
-    rawtime = gnc_mktime (&retvalue);
-    if (retvalue.tm_mday == orig_day &&
-            retvalue.tm_mon == orig_month &&
-            retvalue.tm_year == orig_year)
-        return rawtime;
     else
-        return -1;
+    {
+        /* The input dates don't have a year, so work with today's year.
+         */
+        gnc_timespec2dmy(timespec_now(), nullptr, nullptr, &year);
+    }
+
+    auto ts = gnc_dmy2timespec_neutral(day, month, year);
+    return ts.tv_sec;
 }
 
 /** Constructor for GncTxImport.

commit c0d518e8708e0840fe47579348a4c6e91ad4051c
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Fri Sep 16 23:18:41 2016 +0200

    Migrate the glib based test case for GncTxImport to google-test

diff --git a/src/import-export/csv-imp/test/CMakeLists.txt b/src/import-export/csv-imp/test/CMakeLists.txt
index e43a6f5..0074739 100644
--- a/src/import-export/csv-imp/test/CMakeLists.txt
+++ b/src/import-export/csv-imp/test/CMakeLists.txt
@@ -36,5 +36,10 @@ IF (NOT WIN32)
     gtest_csv_imp_INCLUDES gtest_csv_imp_LIBS
     SRCDIR=${CMAKE_SOURCE_DIR}/src/import-export/csv-imp/test)
 
+  SET(test_tx_import_SOURCES
+    test-tx-import.cpp
+    ${GTEST_SRC})
+  GNC_ADD_TEST(test-tx_import "${test_tx_import_SOURCES}"
+    gtest_csv_imp_INCLUDES gtest_csv_imp_LIBS)
 ENDIF()
 
diff --git a/src/import-export/csv-imp/test/Makefile.am b/src/import-export/csv-imp/test/Makefile.am
index bf7205d..05738e3 100644
--- a/src/import-export/csv-imp/test/Makefile.am
+++ b/src/import-export/csv-imp/test/Makefile.am
@@ -22,14 +22,11 @@ test_csv_impdir = ${top_srcdir}/${MODULEPATH}/test
 #support library.
 test_csv_imp_SOURCES = \
   test-csv-imp.c \
-  utest-gnc-csv-model.c \
-  utest-gnc-csv-imp-trans.cpp
+  utest-gnc-csv-model.c
 
 test_csv_imp_HEADERS =
 
-#The tests might require more libraries, but try to keep them
-#as independent as possible.
-test_csv_imp_LDADD = \
+IMP_TEST_LDFLAGS = \
   ${top_builddir}/${MODULEPATH}/libgncmod-csv-import.la \
   ${top_builddir}/src/import-export/libgncmod-generic-import.la \
   ${top_builddir}/src/gnome/libgnc-gnome.la \
@@ -46,6 +43,11 @@ test_csv_imp_LDADD = \
   ${GLIB_LIBS} \
   $(BOOST_LDFLAGS)
 
+#The tests might require more libraries, but try to keep them
+#as independent as possible.
+test_csv_imp_LDADD = \
+   $(IMP_TEST_LDFLAGS)
+
 test_csv_imp_CFLAGS = \
 	-DTESTPROG=test_csv-imp \
 	${DEFAULT_INCLUDES} \
@@ -68,8 +70,7 @@ test_csv_imp_CFLAGS = \
   ${GTK_CFLAGS} \
   ${GLIB_CFLAGS}
 
-test_csv_imp_CPPFLAGS = \
-	-DTESTPROG=test_csv-imp \
+IMP_TEST_CPPFLAGS =  \
 	${DEFAULT_INCLUDES} \
 	-I$(top_srcdir)/${MODULEPATH}/ \
   -I${top_srcdir}/src/test-core \
@@ -91,6 +92,10 @@ test_csv_imp_CPPFLAGS = \
   ${GLIB_CFLAGS} \
   $(BOOST_CPPFLAGS)
 
+test_csv_imp_CPPFLAGS = \
+	-DTESTPROG=test_csv-imp \
+	$(IMP_TEST_CPPFLAGS)
+
 GNC_TEST_DEPS = \
 --library-dir    ${top_builddir}/${MODULEPATH} \
 --library-dir    ${top_builddir}/src/import-export \
@@ -118,41 +123,6 @@ TESTS_ENVIRONMENT = \
 
 test_tokenizer_SOURCES = \
     test-tokenizer.cpp
-test_tokenizer_LDADD = \
-  -L${top_builddir}/${MODULEPATH} \
-  -L${top_builddir}/src/import-export \
-  -L${top_builddir}/src/gnome \
-  -L${top_builddir}/src/gnome-utils \
-  -L${top_builddir}/src/gnome-search \
-  -L${top_builddir}/src/register/ledger-core \
-  -L${top_builddir}/src/register/register-core \
-  -L${top_builddir}/src/register/register-gnome \
-  -L${top_builddir}/src/report/report-system \
-  -L${top_builddir}/src/report/report-gnome \
-  -L${top_builddir}/src/html \
-  -L${top_builddir}/src/app-utils \
-  -L${top_builddir}/src/backend/xml \
-  -L${top_builddir}/src/engine \
-  -L${top_builddir}/src/core-utils \
-  -L${top_builddir}/src/gnc-module \
-  -L${top_builddir}/src/libqof/qof \
-  -L${top_builddir}/lib/stf \
-  ${top_builddir}/${MODULEPATH}/libgncmod-csv-import.la \
-  ${top_builddir}/src/import-export/libgncmod-generic-import.la \
-  ${top_builddir}/src/gnome/libgnc-gnome.la \
-  ${top_builddir}/src/gnome-utils/libgncmod-gnome-utils.la \
-  ${top_builddir}/src/register/ledger-core/libgncmod-ledger-core.la \
-  ${top_builddir}/src/report/report-gnome/libgncmod-report-gnome.la \
-  ${top_builddir}/src/app-utils/libgncmod-app-utils.la \
-  ${top_builddir}/src/backend/xml/libgnc-backend-xml-utils.la \
-  ${top_builddir}/src/engine/libgncmod-engine.la \
-  ${top_builddir}/src/core-utils/libgnc-core-utils.la \
-  ${top_builddir}/src/gnc-module/libgnc-module.la \
-  ${top_builddir}/src/libqof/qof/libgnc-qof.la \
-  ${top_builddir}/lib/stf/libgnc-stf.la \
-  ${GLIB_LIBS} \
-  $(GTEST_LIBS) \
-  $(BOOST_LDFLAGS)
 
 if !GOOGLE_TEST_LIBS
 nodist_test_tokenizer_SOURCES = \
@@ -161,27 +131,30 @@ endif
 
 test_tokenizer_CPPFLAGS = \
   -I$(GTEST_HEADERS) \
-  -I$(top_srcdir)/$(MODULEPATH) \
-  -I${top_srcdir}/src/test-core \
-  -I${top_srcdir}/src \
-  -I${top_srcdir}/src/import-export \
-  -I${top_srcdir}/src/gnome \
-  -I${top_srcdir}/src/register/ledger-core \
-  -I${top_srcdir}/src/register/register-gnome \
-  -I${top_srcdir}/src/register/register-core \
-  -I${top_srcdir}/src/gnome-utils \
-  -I${top_srcdir}/src/app-utils \
-  -I${top_srcdir}/src/engine \
-  -I${top_srcdir}/src/core-utils \
-  -I${top_srcdir}/src/gnc-module \
-  -I${top_srcdir}/src/libqof/qof \
-  -I${top_srcdir}/lib/libc \
-  -I${top_srcdir}/lib \
-  ${GTK_CFLAGS} \
-  ${GLIB_CFLAGS} \
-  $(BOOST_CPPFLAGS)
+  $(IMP_TEST_CPPFLAGS)
+
+test_tokenizer_LDADD = \
+  ${IMP_TEST_LDFLAGS} \
+  $(GTEST_LIBS)
+
+test_tx_import_SOURCES = \
+    test-tx-import.cpp
+
+if !GOOGLE_TEST_LIBS
+nodist_test_tx_import_SOURCES = \
+        ${GTEST_SRC}/src/gtest_main.cc
+endif
+
+test_tx_import_CPPFLAGS = \
+  -I$(GTEST_HEADERS) \
+  $(IMP_TEST_CPPFLAGS)
+
+test_tx_import_LDADD = \
+  ${IMP_TEST_LDFLAGS} \
+  $(GTEST_LIBS)
 
-check_PROGRAMS += test-tokenizer
+check_PROGRAMS += test-tokenizer \
+                  test-tx-import
 
 EXTRA_DIST= \
   sample1.csv
diff --git a/src/import-export/csv-imp/test/test-csv-imp.c b/src/import-export/csv-imp/test/test-csv-imp.c
index 42e0cea..6665de1 100644
--- a/src/import-export/csv-imp/test/test-csv-imp.c
+++ b/src/import-export/csv-imp/test/test-csv-imp.c
@@ -32,7 +32,6 @@
  * each sub-suite; avoids having header files. */
 
 extern GTestSuite *test_suite_gnc_csv_model();
-extern GTestSuite *test_suite_gnc_csv_imp_trans();
 
 int
 main (int   argc,
@@ -56,7 +55,6 @@ main (int   argc,
      * details. Unfortunately, GLib-Testing doesn't provide the automatic
      * registration features of more sophisticated frameworks. */
     test_suite_gnc_csv_model();
-    test_suite_gnc_csv_imp_trans();
 
     return g_test_run();
 }
diff --git a/src/import-export/csv-imp/test/test-tx-import.cpp b/src/import-export/csv-imp/test/test-tx-import.cpp
new file mode 100644
index 0000000..ff1b612
--- /dev/null
+++ b/src/import-export/csv-imp/test/test-tx-import.cpp
@@ -0,0 +1,209 @@
+/********************************************************************
+ * test-tx-import.cpp: test suite for the GncTxImport class.        *
+ * Copyright 2016 Geert Janssens <geert.gnucash at kobaltwit.be>       *
+ *                                                                  *
+ * 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 2 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, you can retrieve it from        *
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html            *
+ * or contact:                                                      *
+ *                                                                  *
+ * Free Software Foundation           Voice:  +1-617-542-5942       *
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
+ ********************************************************************/
+
+#include <guid.hpp>
+#include "../gnc-tokenizer.hpp"
+#include "../gnc-csv-tokenizer.hpp"
+#include "../gnc-fw-tokenizer.hpp"
+#include <gtest/gtest.h>
+#include <iostream>
+#include <fstream>      // fstream
+
+#include <string>
+#include <stdlib.h>     /* getenv */
+
+/* Add specific headers for this class */
+#include "../gnc-tx-import.hpp"
+
+//typedef struct
+//{
+//    GncTxImport* parse_data;
+//} Fixture;
+
+typedef struct
+{
+    int          date_fmt;
+    const gchar *date_str;
+    int          exp_year;
+    int          exp_month;
+    int          exp_day;
+} parse_date_data;
+
+class GncTxImportTest : public ::testing::Test
+{
+public:
+    GncTxImportTest()
+    {
+        tx_importer = std::unique_ptr<GncTxImport>(new GncTxImport);
+    }
+
+
+protected:
+    std::unique_ptr<GncTxImport> tx_importer;
+};
+
+
+/* parse_date
+time64 parse_date (const char* date_str, int format)// C: 14 in 7 SCM: 9 in 2 Local: 1:0:0
+*/
+TEST(GncTxImportTest, parse_date)
+{
+    time64 rawtime = gnc_time (NULL);
+    struct tm *tm = gnc_gmtime (&rawtime);
+    int curr_year = tm->tm_year;
+
+
+    /* Note: tm_year = year - 1900 and tm_mon = 0-11
+     * I'm writing the expected values as subtractions for easier
+     * comparison with the date string under test
+     */
+    parse_date_data test_dates[] =
+    {
+        // supported combinations  -/.'
+        { 0, "2013-08-01", 2013 - 1900,  8 - 1,  1},
+        { 0,  "2013-8-01", 2013 - 1900,  8 - 1,  1},
+        { 0,  "2013-08-1", 2013 - 1900,  8 - 1,  1},
+        { 0,   "2013-8-1", 2013 - 1900,  8 - 1,  1},
+        { 0,   "13-08-01", 2013 - 1900,  8 - 1,  1},
+        { 0,    "13-8-01", 2013 - 1900,  8 - 1,  1},
+        { 0,    "13-08-1", 2013 - 1900,  8 - 1,  1},
+        { 0,     "13-8-1", 2013 - 1900,  8 - 1,  1},
+        { 0, "2009/11/04", 2009 - 1900, 11 - 1,  4},
+        { 0,  "1985.3.12", 1985 - 1900,  3 - 1, 12},
+        { 0,      "3'6'8", 2003 - 1900,  6 - 1,  8},
+        { 0,   "20130801", 2013 - 1900,  8 - 1,  1},
+        { 1, "01-08-2013", 2013 - 1900,  8 - 1,  1},
+        { 1,  "01-8-2013", 2013 - 1900,  8 - 1,  1},
+        { 1,  "1-08-2013", 2013 - 1900,  8 - 1,  1},
+        { 1,   "1-8-2013", 2013 - 1900,  8 - 1,  1},
+        { 1,   "01-08-13", 2013 - 1900,  8 - 1,  1},
+        { 1,    "01-8-13", 2013 - 1900,  8 - 1,  1},
+        { 1,    "1-08-13", 2013 - 1900,  8 - 1,  1},
+        { 1,     "1-8-13", 2013 - 1900,  8 - 1,  1},
+        { 1, "04/11/2009", 2009 - 1900, 11 - 1,  4},
+        { 1,  "12.3.1985", 1985 - 1900,  3 - 1, 12},
+        { 1,      "8'6'3", 2003 - 1900,  6 - 1,  8},
+        { 1,   "01082013", 2013 - 1900,  8 - 1,  1},
+        { 2, "08-01-2013", 2013 - 1900,  8 - 1,  1},
+        { 2,  "8-01-2013", 2013 - 1900,  8 - 1,  1},
+        { 2,  "08-1-2013", 2013 - 1900,  8 - 1,  1},
+        { 2,   "8-1-2013", 2013 - 1900,  8 - 1,  1},
+        { 2,   "08-01-13", 2013 - 1900,  8 - 1,  1},
+        { 2,    "8-01-13", 2013 - 1900,  8 - 1,  1},
+        { 2,    "08-1-13", 2013 - 1900,  8 - 1,  1},
+        { 2,     "8-1-13", 2013 - 1900,  8 - 1,  1},
+        { 2, "11/04/2009", 2009 - 1900, 11 - 1,  4},
+        { 2,  "3.12.1985", 1985 - 1900,  3 - 1, 12},
+        { 2,      "6'8'3", 2003 - 1900,  6 - 1,  8},
+        { 2,   "08012013", 2013 - 1900,  8 - 1,  1},
+        { 3,      "01-08",   curr_year,  8 - 1,  1},
+        { 3,       "01-8",   curr_year,  8 - 1,  1},
+        { 3,       "1-08",   curr_year,  8 - 1,  1},
+        { 3,        "1-8",   curr_year,  8 - 1,  1},
+        { 3,      "04/11",   curr_year, 11 - 1,  4},
+        { 3,       "12.3",   curr_year,  3 - 1, 12},
+        { 3,        "8'6",   curr_year,  6 - 1,  8},
+        { 3,       "0108",   curr_year,  8 - 1,  1},
+        { 4,      "08-01",   curr_year,  8 - 1,  1},
+        { 4,       "8-01",   curr_year,  8 - 1,  1},
+        { 4,       "08-1",   curr_year,  8 - 1,  1},
+        { 4,        "8-1",   curr_year,  8 - 1,  1},
+        { 4,      "11/04",   curr_year, 11 - 1,  4},
+        { 4,       "3.12",   curr_year,  3 - 1, 12},
+        { 4,        "6'8",   curr_year,  6 - 1,  8},
+        { 4,       "0801",   curr_year,  8 - 1,  1},
+
+        // ambiguous date formats
+        // current parser doesn't know how to disambiguate
+        // and hence refuses to parse
+        // can possibly improved with a smarter parser
+        { 0,     "130801",          -1,     -1, -1},
+        { 1,     "010813",          -1,     -1, -1},
+        { 2,     "080113",          -1,     -1, -1},
+
+        // Combinations that don't make sense
+        // but can still be entered by a user
+        // Should ideally all result in refusal to parse...
+        { 0,      "08-01",          -1,     -1, -1},
+        { 0,       "0801",          -1,     -1, -1},
+        { 1,      "01-08",          -1,     -1, -1},
+        { 1,       "0108",          -1,     -1, -1},
+        { 2,      "08-01",          -1,     -1, -1},
+        { 2,       "0801",          -1,     -1, -1},
+        { 3, "01-08-2013",          -1,     -1, -1},
+        { 3,   "01-08-13",          -1,     -1, -1},
+        { 3,   "08-08-08",          -1,     -1, -1},
+        { 3,   "01082013",          -1,     -1, -1},
+        { 3,     "010813",          -1,     -1, -1},
+        { 3,   "20130108",          -1,     -1, -1},
+        { 4, "08-01-2013",          -1,     -1, -1},
+        { 4,   "08-01-13",          -1,     -1, -1},
+        { 4, "2013-08-01",          -1,     -1, -1},
+        { 4,   "09-08-01",          -1,     -1, -1},
+        { 4,   "08012013",          -1,     -1, -1},
+        { 4,     "080113",          -1,     -1, -1},
+        { 4,   "20130801",          -1,     -1, -1},
+
+        // Sentinel to mark the end of available tests
+        { 0,         NULL,           0,      0,  0},
+
+    };
+    int i = 0;
+
+    gnc_tm_free(tm);
+    while (test_dates[i].date_str)
+    {
+        gboolean success = TRUE;
+        int got_year = 0, got_month = 0, got_day = 0;
+
+        rawtime = parse_date (std::string(test_dates[i].date_str), test_dates[i].date_fmt);
+        if (rawtime == -1)
+            got_year = got_month = got_day = -1;
+        else
+        {
+            tm = gnc_gmtime (&rawtime);
+            got_year = tm->tm_year;
+            got_month = tm->tm_mon;
+            got_day = tm->tm_mday;
+            gnc_tm_free(tm);
+        }
+
+        if ((got_year  != test_dates[i].exp_year) ||
+            (got_month != test_dates[i].exp_month) ||
+            (got_day   != test_dates[i].exp_day))
+        {
+            g_error ("Parse_date failed for date '%s' and format '%d'.\n"
+                            "Expected result: year %d, month %d, day %d\n"
+                            "Obtained result: year %d, month %d, day %d",
+                            test_dates[i].date_str,
+                            test_dates[i].date_fmt,
+                            test_dates[i].exp_year,
+                            test_dates[i].exp_month,
+                            test_dates[i].exp_day,
+                            got_year, got_month, got_day);
+        }
+
+        i++;
+    }
+}
diff --git a/src/import-export/csv-imp/test/utest-gnc-csv-imp-trans.cpp b/src/import-export/csv-imp/test/utest-gnc-csv-imp-trans.cpp
deleted file mode 100644
index d5b58ac..0000000
--- a/src/import-export/csv-imp/test/utest-gnc-csv-imp-trans.cpp
+++ /dev/null
@@ -1,461 +0,0 @@
-/********************************************************************
- * utest-gnc-csv-model.c: GLib g_test test suite for gnc-csv-model.c.		    *
- * Copyright 2015 Geert Janssens <geert.gnucash at kobaltwit.be>		    *
- *                                                                  *
- * 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 2 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, you can retrieve it from        *
- * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html            *
- * or contact:                                                      *
- *                                                                  *
- * Free Software Foundation           Voice:  +1-617-542-5942       *
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
- * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
- ********************************************************************/
-#include "guid.hpp"
-
-extern "C" {
-#include <config.h>
-#include <string.h>
-#include <glib.h>
-#include <unittest-support.h>
-
-}
-/* Add specific headers for this class */
-#include "import-export/csv-imp/gnc-csv-imp-trans.hpp"
-
-//typedef struct
-//{
-//    GncCsvParseData* parse_data;
-//} Fixture;
-
-typedef struct
-{
-    int          date_fmt;
-    const gchar *date_str;
-    int          exp_year;
-    int          exp_month;
-    int          exp_day;
-} parse_date_data;
-
-//typedef struct
-//{
-//    const gchar *csv_line;
-//    int          num_fields;
-//    const gchar *fields [8];
-//} parse_test_data;
-//
-//static const gchar* samplefile1 = "sample1.csv";
-//
-//static parse_test_data comma_separated [] = {
-//        { "Date,Num,Description,Notes,Account,Deposit,Withdrawal,Balance", 8, { "Date","Num","Description","Notes","Account","Deposit","Withdrawal","Balance" } },
-//        { "05/01/15,45,Acme Inc.,,Miscellaneous,,\"1,100.00\",", 8, { "05/01/15","45","Acme Inc.","","Miscellaneous","","1,100.00","" } },
-//        { "05/01/15,45,Acme Inc.,,Miscellaneous,", 4, { "05/01/15","45","Acme Inc.","",NULL,NULL,NULL,NULL } },
-//        { NULL, 0, { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } },
-//};
-//
-//static parse_test_data semicolon_separated [] = {
-//        { "Date;Num;Description;Notes;Account;Deposit;Withdrawal;Balance", 8, { "Date","Num","Description","Notes","Account","Deposit","Withdrawal","Balance" } },
-//        { "05/01/15;45;Acme Inc.;;Miscellaneous;;\"1,100.00\";", 8, { "05/01/15","45","Acme Inc.","","Miscellaneous","","1,100.00","" } },
-//        { "05/01/15;45;Acme Inc.;;Miscellaneous;", 4, { "05/01/15","45","Acme Inc.","",NULL,NULL,NULL,NULL } },
-//        { NULL, 0, { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } },
-//};
-//
-//static char* get_filepath(const char* filename, gboolean test_existence)
-//{
-//    char *result;
-//
-//    const char *srcdir = g_getenv("SRCDIR");
-//    if (!srcdir)
-//    {
-//        g_test_message("No env variable SRCDIR exists, assuming \".\"\n");
-//        srcdir = ".";
-//    }
-//
-//    result = g_strdup_printf("%s/%s", srcdir, filename);
-//
-//    g_test_message("Using file path %s\n", result);
-//
-//    // Test whether the file really exists
-//    if (test_existence)
-//        g_assert(g_file_test(result, G_FILE_TEST_EXISTS));
-//
-//    return result;
-//}
-//
-//static void
-//setup( Fixture *fixture, gconstpointer pData )
-//{
-//    fixture->parse_data = gnc_csv_new_parse_data ();
-//}
-//
-//static void
-//setup_one_file( Fixture *fixture, gconstpointer pData )
-//{
-//    const gchar *filename = (const gchar*) pData;
-//    char *filepath = get_filepath (filename, TRUE);
-//    GError *the_error = NULL;
-//    int resultcode = 0;
-//
-//    fixture->parse_data = gnc_csv_new_parse_data ();
-//    resultcode = gnc_csv_load_file (fixture->parse_data, filepath,
-//                                    &the_error);
-//    g_assert (resultcode == 0);
-//    g_free(filepath);
-//}
-//
-//static void
-//teardown( Fixture *fixture, gconstpointer pData )
-//{
-//    gnc_csv_parse_data_free (fixture->parse_data);
-//}
-
-static const gchar *suitename = "/import-export/csv-imp/gnc-csv-imp-trans";
-extern "C"  {
-void test_suite_gnc_csv_imp_trans ( void );
-}
-
-/* parse_date
-time64 parse_date (const char* date_str, int format)// C: 14 in 7 SCM: 9 in 2 Local: 1:0:0
-*/
-static void
-test_parse_date (void)
-{
-    time64 rawtime = gnc_time (NULL);
-    struct tm *tm = gnc_gmtime (&rawtime);
-    int curr_year = tm->tm_year;
-
-
-    /* Note: tm_year = year - 1900 and tm_mon = 0-11
-     * I'm writing the expected values as subtractions for easier
-     * comparison with the date string under test
-     */
-    parse_date_data test_dates[] =
-    {
-        // supported combinations  -/.'
-        { 0, "2013-08-01", 2013 - 1900,  8 - 1,  1},
-        { 0,  "2013-8-01", 2013 - 1900,  8 - 1,  1},
-        { 0,  "2013-08-1", 2013 - 1900,  8 - 1,  1},
-        { 0,   "2013-8-1", 2013 - 1900,  8 - 1,  1},
-        { 0,   "13-08-01", 2013 - 1900,  8 - 1,  1},
-        { 0,    "13-8-01", 2013 - 1900,  8 - 1,  1},
-        { 0,    "13-08-1", 2013 - 1900,  8 - 1,  1},
-        { 0,     "13-8-1", 2013 - 1900,  8 - 1,  1},
-        { 0, "2009/11/04", 2009 - 1900, 11 - 1,  4},
-        { 0,  "1985.3.12", 1985 - 1900,  3 - 1, 12},
-        { 0,      "3'6'8", 2003 - 1900,  6 - 1,  8},
-        { 0,   "20130801", 2013 - 1900,  8 - 1,  1},
-        { 1, "01-08-2013", 2013 - 1900,  8 - 1,  1},
-        { 1,  "01-8-2013", 2013 - 1900,  8 - 1,  1},
-        { 1,  "1-08-2013", 2013 - 1900,  8 - 1,  1},
-        { 1,   "1-8-2013", 2013 - 1900,  8 - 1,  1},
-        { 1,   "01-08-13", 2013 - 1900,  8 - 1,  1},
-        { 1,    "01-8-13", 2013 - 1900,  8 - 1,  1},
-        { 1,    "1-08-13", 2013 - 1900,  8 - 1,  1},
-        { 1,     "1-8-13", 2013 - 1900,  8 - 1,  1},
-        { 1, "04/11/2009", 2009 - 1900, 11 - 1,  4},
-        { 1,  "12.3.1985", 1985 - 1900,  3 - 1, 12},
-        { 1,      "8'6'3", 2003 - 1900,  6 - 1,  8},
-        { 1,   "01082013", 2013 - 1900,  8 - 1,  1},
-        { 2, "08-01-2013", 2013 - 1900,  8 - 1,  1},
-        { 2,  "8-01-2013", 2013 - 1900,  8 - 1,  1},
-        { 2,  "08-1-2013", 2013 - 1900,  8 - 1,  1},
-        { 2,   "8-1-2013", 2013 - 1900,  8 - 1,  1},
-        { 2,   "08-01-13", 2013 - 1900,  8 - 1,  1},
-        { 2,    "8-01-13", 2013 - 1900,  8 - 1,  1},
-        { 2,    "08-1-13", 2013 - 1900,  8 - 1,  1},
-        { 2,     "8-1-13", 2013 - 1900,  8 - 1,  1},
-        { 2, "11/04/2009", 2009 - 1900, 11 - 1,  4},
-        { 2,  "3.12.1985", 1985 - 1900,  3 - 1, 12},
-        { 2,      "6'8'3", 2003 - 1900,  6 - 1,  8},
-        { 2,   "08012013", 2013 - 1900,  8 - 1,  1},
-        { 3,      "01-08",   curr_year,  8 - 1,  1},
-        { 3,       "01-8",   curr_year,  8 - 1,  1},
-        { 3,       "1-08",   curr_year,  8 - 1,  1},
-        { 3,        "1-8",   curr_year,  8 - 1,  1},
-        { 3,      "04/11",   curr_year, 11 - 1,  4},
-        { 3,       "12.3",   curr_year,  3 - 1, 12},
-        { 3,        "8'6",   curr_year,  6 - 1,  8},
-        { 3,       "0108",   curr_year,  8 - 1,  1},
-        { 4,      "08-01",   curr_year,  8 - 1,  1},
-        { 4,       "8-01",   curr_year,  8 - 1,  1},
-        { 4,       "08-1",   curr_year,  8 - 1,  1},
-        { 4,        "8-1",   curr_year,  8 - 1,  1},
-        { 4,      "11/04",   curr_year, 11 - 1,  4},
-        { 4,       "3.12",   curr_year,  3 - 1, 12},
-        { 4,        "6'8",   curr_year,  6 - 1,  8},
-        { 4,       "0801",   curr_year,  8 - 1,  1},
-
-        // ambiguous date formats
-        // current parser doesn't know how to disambiguate
-        // and hence refuses to parse
-        // can possibly improved with a smarter parser
-        { 0,     "130801",          -1,     -1, -1},
-        { 1,     "010813",          -1,     -1, -1},
-        { 2,     "080113",          -1,     -1, -1},
-
-        // Combinations that don't make sense
-        // but can still be entered by a user
-        // Should ideally all result in refusal to parse...
-        { 0,      "08-01",          -1,     -1, -1},
-        { 0,       "0801",          -1,     -1, -1},
-        { 1,      "01-08",          -1,     -1, -1},
-        { 1,       "0108",          -1,     -1, -1},
-        { 2,      "08-01",          -1,     -1, -1},
-        { 2,       "0801",          -1,     -1, -1},
-        { 3, "01-08-2013",          -1,     -1, -1},
-        { 3,   "01-08-13",          -1,     -1, -1},
-        { 3,   "08-08-08",          -1,     -1, -1},
-        { 3,   "01082013",          -1,     -1, -1},
-        { 3,     "010813",          -1,     -1, -1},
-        { 3,   "20130108",          -1,     -1, -1},
-        { 4, "08-01-2013",          -1,     -1, -1},
-        { 4,   "08-01-13",          -1,     -1, -1},
-        { 4, "2013-08-01",          -1,     -1, -1},
-        { 4,   "09-08-01",          -1,     -1, -1},
-        { 4,   "08012013",          -1,     -1, -1},
-        { 4,     "080113",          -1,     -1, -1},
-        { 4,   "20130801",          -1,     -1, -1},
-
-        // Sentinel to mark the end of available tests
-        { 0,         NULL,           0,      0,  0},
-
-    };
-    int i = 0;
-
-    gnc_tm_free(tm);
-    while (test_dates[i].date_str)
-    {
-        gboolean success = TRUE;
-        int got_year = 0, got_month = 0, got_day = 0;
-
-        rawtime = parse_date (std::string(test_dates[i].date_str), test_dates[i].date_fmt);
-        if (rawtime == -1)
-            got_year = got_month = got_day = -1;
-        else
-        {
-            tm = gnc_gmtime (&rawtime);
-            got_year = tm->tm_year;
-            got_month = tm->tm_mon;
-            got_day = tm->tm_mday;
-            gnc_tm_free(tm);
-        }
-
-        if ((got_year  != test_dates[i].exp_year) ||
-            (got_month != test_dates[i].exp_month) ||
-            (got_day   != test_dates[i].exp_day))
-        {
-            g_error ("Parse_date failed for date '%s' and format '%d'.\n"
-                            "Expected result: year %d, month %d, day %d\n"
-                            "Obtained result: year %d, month %d, day %d",
-                            test_dates[i].date_str,
-                            test_dates[i].date_fmt,
-                            test_dates[i].exp_year,
-                            test_dates[i].exp_month,
-                            test_dates[i].exp_day,
-                            got_year, got_month, got_day);
-        }
-
-        i++;
-    }
-
-
-}
-/* gnc_csv_new_parse_data
-GncCsvParseData* gnc_csv_new_parse_data (void)// C: 1 in 1  Local: 0:0:0
-*/
-//static void
-//test_gnc_csv_new_parse_data (void)
-//{
-//    GncCsvParseData* parse_data = gnc_csv_new_parse_data ();
-//    g_assert (parse_data != NULL);
-//    g_assert (parse_data->chunk != NULL);
-//    gnc_csv_parse_data_free (parse_data);
-//}
-
-/* gnc_csv_parse_data_free
-void gnc_csv_parse_data_free (GncCsvParseData* parse_data)// C: 3 in 1  Local: 0:0:0
-*/
-// Basic freeing of memory - no test
-
-/* gnc_csv_convert_encoding
-int gnc_csv_convert_encoding (GncCsvParseData* parse_data, const char* encoding,// C: 1  Local: 1:0:0
-*/
-/* static void
-test_gnc_csv_convert_encoding (Fixture *fixture, gconstpointer pData)
-{
-}*/
-/* gnc_csv_load_file
-int gnc_csv_load_file (GncCsvParseData* parse_data, const char* filename,// C: 1  Local: 0:0:0
-*/
-//static void
-//test_gnc_csv_load_file (Fixture *fixture, gconstpointer pData)
-//{
-//
-//    char *file1 = get_filepath ("notexist.csv", FALSE);
-//    char *file2 = get_filepath ("sample1.csv", TRUE);
-//    GError *the_error = NULL;
-//    int resultcode = 0;
-//
-//    /* Test loading of a non-existing file */
-//    resultcode = gnc_csv_load_file (fixture->parse_data, file1,
-//                                    &the_error);
-//    g_assert ((the_error->domain == GNC_CSV_IMP_ERROR) &&
-//              (the_error->code == GNC_CSV_IMP_ERROR_OPEN));
-//
-//    /* Test loading of a valid csv file */
-//    resultcode = gnc_csv_load_file (fixture->parse_data, file2,
-//                                    &the_error);
-//    g_assert (resultcode == 0);
-//}
-/* gnc_csv_parse
-int gnc_csv_parse (GncCsvParseData* parse_data, gboolean guessColTypes, GError** error)// C: 13 in 1  Local: 0:0:0
-*/
-//static void
-//test_gnc_csv_parse_from_file (Fixture *fixture, gconstpointer pData)
-//{
-//    GError *the_error = NULL;
-//    int resultcode = 0;
-//
-//    /* Test basic parsing of the loaded file
-//     * A few fields are sampled in the parsed data. */
-//    resultcode = gnc_csv_parse (fixture->parse_data, TRUE, &the_error);
-//    g_assert (g_strcmp0 ((char*)((GPtrArray*)(fixture->parse_data->orig_lines->pdata[0]))->pdata[0],
-//                         "Date") == 0);
-//    g_assert (g_strcmp0 ((char*)((GPtrArray*)(fixture->parse_data->orig_lines->pdata[1]))->pdata[6],
-//                         "1,100.00") == 0);
-//}
-
-/* Test parsing for several different prepared strings
- * These tests bypass file loading, rather taking a
- * prepared set of strings as input. This makes it
- * easier to add test cases without having to create new test files
- * each time to load from.
- * Note this bypasses encoding configuration, which should be tested
- * independently.
- */
-
-/* This helper function will run the parse step on the given data
- * with the parser as configured by the calling test function.
- * This allows the same code to be used with different csv test strings
- * and parser option combinations.
- */
-//static void
-//test_gnc_csv_parse_helper (GncCsvParseData *parse_data, gconstpointer pData)
-//{
-//    parse_test_data *test_data = (parse_test_data *) pData;
-//    GError *the_error = NULL;
-//    int resultcode = 0;
-//    int i = 0;
-//
-//    while (test_data[i].csv_line)
-//    {
-//        int j;
-//        parse_test_data cur_line = test_data[i];
-//
-//        g_test_message("Using string %s\n", cur_line.csv_line);
-//        g_free (parse_data->file_str.begin);
-//        parse_data->file_str.begin = g_strdup (cur_line.csv_line);
-//        parse_data->file_str.end = parse_data->file_str.begin + strlen (parse_data->file_str.begin);
-//        resultcode = gnc_csv_parse (parse_data, TRUE, &the_error);
-//        g_assert (resultcode == 0);
-//        for (j=0; j < cur_line.num_fields; j++)
-//        {
-//            g_assert (g_strcmp0 ((char*)((GPtrArray*)(parse_data->orig_lines->pdata[0]))->pdata[j],
-//                     (cur_line.fields[j])) == 0);
-//        }
-//
-//        i++;
-//    }
-//}
-//
-//static void
-//test_gnc_csv_parse_comma_sep (Fixture *fixture, gconstpointer pData)
-//{
-//    GSList* sep_list = NULL;
-//
-//    sep_list = g_slist_append (sep_list, ",");
-//    stf_parse_options_csv_set_separators (fixture->parse_data->options, NULL, sep_list);
-//    g_slist_free (sep_list);
-//
-//    test_gnc_csv_parse_helper (fixture->parse_data, pData);
-//}
-//
-//static void
-//test_gnc_csv_parse_semicolon_sep (Fixture *fixture, gconstpointer pData)
-//{
-//    GSList* sep_list = NULL;
-//
-//    sep_list = g_slist_append (sep_list, ";");
-//    stf_parse_options_csv_set_separators (fixture->parse_data->options, NULL, sep_list);
-//    g_slist_free (sep_list);
-//
-//    test_gnc_csv_parse_helper (fixture->parse_data, pData);
-//}
-
-/* trans_property_free
-static void trans_property_free (TransProperty* prop)// Local: 2:0:0
-*/
-// Internal helper function - no test
-/* trans_property_set
-static gboolean trans_property_set (TransProperty* prop, char* str)// Local: 1:0:0
-*/
-// Internal helper function - no test
-/* trans_property_list_free
-static void trans_property_list_free (TransPropertyList* list)// Local: 1:0:0
-*/
-// Internal helper function - no test
-/* trans_property_list_add
-static void trans_property_list_add (TransProperty* property)// Local: 1:0:0
-*/
-// Internal helper function - no test
-/* trans_add_split
-static void trans_add_split (Transaction* trans, Account* account, QofBook* book,// Local: 2:0:0
-*/
-// Internal helper function - no test
-/* trans_property_list_verify_essentials
-static gboolean trans_property_list_verify_essentials (TransPropertyList* list, gchar** error)// Local: 1:0:0
-*/
-// Internal helper function - no test
-/* gnc_csv_parse_to_trans
-int gnc_csv_parse_to_trans (GncCsvParseData* parse_data, Account* account,// C: 2 in 1  Local: 0:0:0
-*/
-/* static void
-test_gnc_csv_parse_to_trans (Fixture *fixture, gconstpointer pData)
-{
-}*/
-
-extern "C" {
-void
-test_suite_gnc_csv_imp_trans (void)
-{
-
-// GNC_TEST_ADD (suitename, "parse date with year", Fixture, NULL, setup, test_parse_date_with_year, teardown);
-// GNC_TEST_ADD (suitename, "parse date without year", Fixture, NULL, setup, test_parse_date_without_year, teardown);
-GNC_TEST_ADD_FUNC (suitename, "parse date", test_parse_date);
-//GNC_TEST_ADD_FUNC (suitename, "gnc csv new parse data", test_gnc_csv_new_parse_data);
-// GNC_TEST_ADD (suitename, "gnc csv parse data free", Fixture, NULL, setup, test_gnc_csv_parse_data_free, teardown);
-// GNC_TEST_ADD (suitename, "gnc csv convert encoding", Fixture, NULL, setup, test_gnc_csv_convert_encoding, teardown);
-//GNC_TEST_ADD (suitename, "gnc csv load file", Fixture, NULL, setup, test_gnc_csv_load_file, teardown);
-//GNC_TEST_ADD (suitename, "gnc csv parse from file", Fixture, samplefile1, setup_one_file, test_gnc_csv_parse_from_file, teardown);
-//GNC_TEST_ADD (suitename, "parse comma", Fixture, comma_separated, setup, test_gnc_csv_parse_comma_sep, teardown);
-//GNC_TEST_ADD (suitename, "parse semicolon", Fixture, semicolon_separated, setup, test_gnc_csv_parse_semicolon_sep, teardown);
-// GNC_TEST_ADD (suitename, "trans property free", Fixture, NULL, setup, test_trans_property_free, teardown);
-// GNC_TEST_ADD (suitename, "trans property set", Fixture, NULL, setup, test_trans_property_set, teardown);
-// GNC_TEST_ADD (suitename, "trans property list free", Fixture, NULL, setup, test_trans_property_list_free, teardown);
-// GNC_TEST_ADD (suitename, "trans property list add", Fixture, NULL, setup, test_trans_property_list_add, teardown);
-// GNC_TEST_ADD (suitename, "trans add split", Fixture, NULL, setup, test_trans_add_split, teardown);
-// GNC_TEST_ADD (suitename, "trans property list verify essentials", Fixture, NULL, setup, test_trans_property_list_verify_essentials, teardown);
-// GNC_TEST_ADD (suitename, "gnc csv parse to trans", Fixture, NULL, setup, test_gnc_csv_parse_to_trans, teardown);
-
-}
-}

commit 9525d9b84badd15d90b079a1135e0177070deecb
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Fri Sep 16 23:17:26 2016 +0200

    Use consistent name for importer class and its filename
    
    The class was still called after the original struct in c and the file
    had an almost ok name from when I started the conversion.
    Add some usage information to the class as well.

diff --git a/src/import-export/csv-imp/CMakeLists.txt b/src/import-export/csv-imp/CMakeLists.txt
index 789ec1b..72143a2 100644
--- a/src/import-export/csv-imp/CMakeLists.txt
+++ b/src/import-export/csv-imp/CMakeLists.txt
@@ -12,12 +12,12 @@ SET(csv_import_SOURCES
   gnc-csv-account-map.c
   gnc-csv-model.c
   gnc-csv-gnumeric-popup.c
-  gnc-csv-imp-trans.cpp
   gnc-csv-tokenizer.cpp
   gnc-csv-trans-settings.c
   gnc-dummy-tokenizer.cpp
   gnc-fw-tokenizer.cpp
   gnc-tokenizer.cpp
+  gnc-tx-import.cpp
   ${CMAKE_SOURCE_DIR}/lib/stf/stf-parse.c
   ${CMAKE_SOURCE_DIR}/lib/goffice/go-charmap-sel.c
   ${CMAKE_SOURCE_DIR}/lib/goffice/go-optionmenu.c
@@ -37,12 +37,12 @@ SET(csv_import_noinst_HEADERS
   gnc-csv-account-map.h
   gnc-csv-model.h
   gnc-csv-gnumeric-popup.h
-  gnc-csv-imp-trans.hpp
   gnc-csv-tokenizer.hpp
   gnc-csv-trans-settings.h
   gnc-dummy-tokenizer.hpp
   gnc-fw-tokenizer.hpp
   gnc-tokenizer.hpp
+  gnc-tx-import.hpp
   ${CMAKE_SOURCE_DIR}/lib/stf/stf-parse.h
   ${CMAKE_SOURCE_DIR}/lib/goffice/go-charmap-sel.h
   ${CMAKE_SOURCE_DIR}/lib/goffice/go-optionmenu.h
diff --git a/src/import-export/csv-imp/Makefile.am b/src/import-export/csv-imp/Makefile.am
index 0c4920a..e3c4775 100644
--- a/src/import-export/csv-imp/Makefile.am
+++ b/src/import-export/csv-imp/Makefile.am
@@ -12,12 +12,12 @@ libgncmod_csv_import_la_SOURCES = \
   csv-fixed-trans-import.c \
   gnc-csv-account-map.c \
   gnc-csv-model.c \
-  gnc-csv-imp-trans.cpp \
   gnc-csv-tokenizer.cpp \
   gnc-csv-gnumeric-popup.c \
   gnc-dummy-tokenizer.cpp \
   gnc-fw-tokenizer.cpp \
   gnc-tokenizer.cpp \
+  gnc-tx-import.cpp \
   gnc-csv-trans-settings.c
 
 noinst_HEADERS = \
@@ -29,12 +29,12 @@ noinst_HEADERS = \
   csv-fixed-trans-import.h \
   gnc-csv-account-map.h \
   gnc-csv-model.h \
-  gnc-csv-imp-trans.hpp \
   gnc-csv-tokenizer.hpp \
   gnc-csv-gnumeric-popup.h \
   gnc-dummy-tokenizer.hpp \
   gnc-fw-tokenizer.hpp \
   gnc-tokenizer.hpp \
+  gnc-tx-import.hpp \
   gnc-csv-trans-settings.h
 
 libgncmod_csv_import_la_LDFLAGS = -avoid-version
diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index bbfe500..8b4a48c 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -57,7 +57,7 @@ extern "C"
 #include <goffice/go-charmap-sel.h>
 }
 
-#include "gnc-csv-imp-trans.hpp"
+#include "gnc-tx-import.hpp"
 #include "gnc-fw-tokenizer.hpp"
 #include "gnc-csv-tokenizer.hpp"
 
@@ -95,7 +95,7 @@ typedef struct
     int              end_row;                       /**< The liststore end row, max number of rows -1 */
     int              home_account_number;           /**< The number of unique home account strings */
 
-    GncCsvParseData *parse_data;                    /**< The actual data we are previewing */
+    GncTxImport *parse_data;                    /**< The actual data we are previewing */
     CsvSettings     *settings_data;                 /**< The settings to be saved and loaded */
     GOCharmapSel    *encselector;                   /**< The widget for selecting the encoding */
     GtkCheckButton  *sep_buttons[SEP_NUM_OF_TYPES]; /**< Checkbuttons for common separators */
@@ -691,7 +691,7 @@ csv_import_trans_file_chooser_confirm_cb (GtkWidget *button, CsvImportTrans *inf
     DEBUG("starting directory is %s", info->starting_dir.c_str());
 
     /* Load the file into parse_data. */
-    auto parse_data = new GncCsvParseData;
+    auto parse_data = new GncTxImport;
     /* Assume data is CSV. User can later override to Fixed Width if needed */
     parse_data->file_format (GncImpFileFormat::CSV, &error);
     if (parse_data->load_file (info->file_name, &error))
diff --git a/src/import-export/csv-imp/gnc-csv-imp-trans.cpp b/src/import-export/csv-imp/gnc-tx-import.cpp
similarity index 97%
rename from src/import-export/csv-imp/gnc-csv-imp-trans.cpp
rename to src/import-export/csv-imp/gnc-tx-import.cpp
index b298284..0c12a11 100644
--- a/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
+++ b/src/import-export/csv-imp/gnc-tx-import.cpp
@@ -1,5 +1,6 @@
 /********************************************************************\
- * gnc-csv-imp-trans.cpp - import transactions from csv files       *
+ * gnc-tx-import.cpp - import transactions from csv or fixed-width  *
+ *                     files                                        *
  *                                                                  *
  * This program is free software; you can redistribute it and/or    *
  * modify it under the terms of the GNU General Public License as   *
@@ -48,14 +49,14 @@ extern "C" {
 #include <boost/regex.hpp>
 #include <boost/regex/icu.hpp>
 
-#include "gnc-csv-imp-trans.hpp"
+#include "gnc-tx-import.hpp"
 #include "gnc-csv-tokenizer.hpp"
 #include "gnc-fw-tokenizer.hpp"
 
 GQuark
 gnc_csv_imp_error_quark (void)
 {
-  return g_quark_from_static_string ("g-csv-imp-error-quark");
+  return g_quark_from_static_string ("g-tx-import-error-quark");
 }
 
 G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT;
@@ -218,10 +219,10 @@ time64 parse_date (const std::string &date_str, int format)
         return -1;
 }
 
-/** Constructor for GncCsvParseData.
+/** Constructor for GncTxImport.
  * @return Pointer to a new GncCSvParseData
  */
-GncCsvParseData::GncCsvParseData(GncImpFileFormat format)
+GncTxImport::GncTxImport(GncImpFileFormat format)
 {
     /* All of the data pointers are initially NULL. This is so that, if
      * gnc_csv_parse_data_free is called before all of the data is
@@ -237,13 +238,13 @@ GncCsvParseData::GncCsvParseData(GncImpFileFormat format)
     tokenizer = GncTokenizerFactory(file_fmt);
 }
 
-/** Destructor for GncCsvParseData.
+/** Destructor for GncTxImport.
  */
-GncCsvParseData::~GncCsvParseData()
+GncTxImport::~GncTxImport()
 {
 }
 
-int GncCsvParseData::file_format(GncImpFileFormat format,
+int GncTxImport::file_format(GncImpFileFormat format,
                                   GError** error)
 {
     if (file_fmt == format)
@@ -267,7 +268,7 @@ int GncCsvParseData::file_format(GncImpFileFormat format,
     tokenizer->encoding(new_encoding);
     return load_file(new_imp_file, error);
 }
-GncImpFileFormat GncCsvParseData::file_format()
+GncImpFileFormat GncTxImport::file_format()
 {
     return file_fmt;
 }
@@ -280,15 +281,15 @@ GncImpFileFormat GncCsvParseData::file_format()
  * @param error Will point to an error on failure
  * @return 0 on success, 1 on failure
  */
-void GncCsvParseData::convert_encoding (const std::string& encoding)
+void GncTxImport::convert_encoding (const std::string& encoding)
 {
     // TODO investigate if we can catch conversion errors and report them
     if (tokenizer)
         tokenizer->encoding(encoding);
 }
 
-/** Loads a file into a GncCsvParseData. This is the first function
- * that must be called after creating a new GncCsvParseData. If this
+/** Loads a file into a GncTxImport. This is the first function
+ * that must be called after creating a new GncTxImport. If this
  * fails because the file couldn't be opened, no more functions can be
  * called on the parse data until this succeeds (or until it fails
  * because of an encoding guess error). If it fails because the
@@ -299,7 +300,7 @@ void GncCsvParseData::convert_encoding (const std::string& encoding)
  * @param error Will contain an error if there is a failure
  * @return 0 on success, 1 on failure
  */
-int GncCsvParseData::load_file (const std::string& filename,
+int GncTxImport::load_file (const std::string& filename,
                                 GError** error)
 {
 
@@ -331,7 +332,7 @@ int GncCsvParseData::load_file (const std::string& filename,
  * @param error Will contain an error if there is a failure
  * @return 0 on success, 1 on failure
  */
-int GncCsvParseData::parse (bool guessColTypes, GError** error)
+int GncTxImport::parse (bool guessColTypes, GError** error)
 {
     uint max_cols = 0;
     tokenizer->tokenize();
@@ -747,7 +748,7 @@ static GncCsvTransLine* trans_properties_to_trans (prop_map_t& trans_props, gcha
  * @param redo_errors TRUE to convert only error data, FALSE for all data
  * @return 0 on success, 1 on failure
  */
-int GncCsvParseData::parse_to_trans (Account* account,
+int GncTxImport::parse_to_trans (Account* account,
                                      bool redo_errors)
 {
     /* Free error_lines and transactions if they
@@ -953,7 +954,7 @@ int GncCsvParseData::parse_to_trans (Account* account,
 
 
 bool
-GncCsvParseData::check_for_column_type (GncTransPropType type)
+GncTxImport::check_for_column_type (GncTransPropType type)
 {
     return (std::find (column_types.begin(), column_types.end(), type) != column_types.end());
 }
diff --git a/src/import-export/csv-imp/gnc-csv-imp-trans.hpp b/src/import-export/csv-imp/gnc-tx-import.hpp
similarity index 85%
rename from src/import-export/csv-imp/gnc-csv-imp-trans.hpp
rename to src/import-export/csv-imp/gnc-tx-import.hpp
index 6aee870..f22ef8c 100644
--- a/src/import-export/csv-imp/gnc-csv-imp-trans.hpp
+++ b/src/import-export/csv-imp/gnc-tx-import.hpp
@@ -1,5 +1,5 @@
 /********************************************************************\
- * gnc-csv-imp-trans.hpp - import transactions from csv files       *
+ * gnc-tx-import.hpp - import transactions from csv files       *
  *                                                                  *
  * This program is free software; you can redistribute it and/or    *
  * modify it under the terms of the GNU General Public License as   *
@@ -20,14 +20,14 @@
 \********************************************************************/
 
 /** @file
-     @brief Class to import transactions from CSV files
+     @brief Class to import transactions from CSV or fixed width files
      *
-     gnc-csv-imp-trans.hpp
+     gnc-tx-import.hpp
      @author Copyright (c) 2015 Geert Janssens <geert at kobaltwit.be>
  */
 
-#ifndef GNC_CSV_IMP_TRANS_HPP
-#define GNC_CSV_IMP_TRANS_HPP
+#ifndef GNC_TX_IMPORT_HPP
+#define GNC_TX_IMPORT_HPP
 
 extern "C" {
 #include "config.h"
@@ -63,7 +63,7 @@ enum class GncTransPropType {
 };
 
 /** Maps all column types to a string representation.
- *  The actual definition is in gnc-csv-imp-trans.cpp.
+ *  The actual definition is in gnc-tx-import.cpp.
  *  Attention: that definition should be adjusted for any
  *  changes to enum class GncTransPropType ! */
 extern std::map<GncTransPropType, const char*> gnc_csv_col_type_strs;
@@ -110,13 +110,22 @@ extern const gchar* date_format_user[];
 /** Pair to hold a tokenized line of input and an optional error string */
 using parse_line_t = std::pair<str_vec, std::string>;
 
-/** Struct containing data for parsing a CSV/Fixed-Width file. */
-class GncCsvParseData
+/* The actual TxImport class
+ * It's intended to use in the following sequence of actions:
+ * - set a file format
+ * - load a file
+ * - optionally convert it's encoding
+ * - parse the file into lines, which in turn are split up in columns
+ *   the result of this step can be queried from tokenizer
+ * - the user should now map the columns to types, which is stored in column_types
+ * - last step is convert the mapped columns into a list of transactions
+ * - this list will then be passed on the the generic importer for further processing */
+class GncTxImport
 {
 public:
     // Constructor - Destructor
-    GncCsvParseData(GncImpFileFormat format = GncImpFileFormat::UNKNOWN);
-    ~GncCsvParseData();
+    GncTxImport(GncImpFileFormat format = GncImpFileFormat::UNKNOWN);
+    ~GncTxImport();
 
     int file_format(GncImpFileFormat format, GError** error);
     GncImpFileFormat file_format();

commit a6aa76fc76d1c22c65eb61fb56f9f93c1783bf08
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Thu Sep 15 16:06:54 2016 +0200

    Fix column sizing for fixed width data
    
    It was written to work with absolute column positions (from stf)
    but boost::tokenizer uses column widths instead.

diff --git a/src/import-export/csv-imp/gnc-fw-tokenizer.cpp b/src/import-export/csv-imp/gnc-fw-tokenizer.cpp
index 0b308ae..9bad3d4 100644
--- a/src/import-export/csv-imp/gnc-fw-tokenizer.cpp
+++ b/src/import-export/csv-imp/gnc-fw-tokenizer.cpp
@@ -18,36 +18,10 @@ GncFwTokenizer::columns(const std::vector<uint>& cols)
 }
 
 
-bool GncFwTokenizer::col_can_add (uint col_end)
-{
-    if (col_end < 0 || col_end > m_longest_line)
-        return false;
-    else
-        return true;
-}
-
-void GncFwTokenizer::col_add (uint col_end)
-{
-    if (col_can_add (col_end))
-    {
-        for (auto col_it = m_col_vec.begin(); col_it != m_col_vec.end(); col_it++)
-        {
-            if (*col_it == col_end)
-                return; // don't add same column end twice in the column list
-            if (*col_it > col_end)
-                m_col_vec.insert (col_it, col_end);
-        }
-
-        // If we got here that means the requested col_end is beyond the currently
-        // inserted columns, so append it
-        m_col_vec.push_back (col_end);
-    }
-}
-
 bool GncFwTokenizer::col_can_delete (uint col_num)
 {
     auto last_col = m_col_vec.size() - 1;
-    if (col_num < 0 || col_num > last_col)
+    if (col_num >= last_col)
         return false;
     else
         return true;
@@ -55,22 +29,18 @@ bool GncFwTokenizer::col_can_delete (uint col_num)
 
 void GncFwTokenizer::col_delete (uint col_num)
 {
-    if (col_can_delete (col_num))
-        m_col_vec.erase (m_col_vec.begin() + col_num);
+    if (!col_can_delete (col_num))
+        return;
+
+    m_col_vec[col_num + 1] += m_col_vec[col_num];
+    m_col_vec.erase (m_col_vec.begin() + col_num);
 }
 
 bool GncFwTokenizer::col_can_narrow (uint col_num)
 {
+    // Can't narrow the last column, it always sticks to the end of the parseable data
     auto last_col = m_col_vec.size() - 1;
-    int col_start, next_col_start;
-
-    if (col_num > last_col)
-        return false;
-
-    col_start = (col_num == 0) ? 0 : m_col_vec[col_num - 1];
-    next_col_start = m_col_vec[col_num];
-
-    if (next_col_start - 1 <= col_start)
+    if (col_num >= last_col)
         return false;
     else
         return true;
@@ -78,24 +48,22 @@ bool GncFwTokenizer::col_can_narrow (uint col_num)
 
 void GncFwTokenizer::col_narrow (uint col_num)
 {
-    if (col_can_narrow (col_num))
-        m_col_vec[col_num]--;
+    if (!col_can_narrow (col_num))
+        return;
+
+    m_col_vec[col_num]--;
+    m_col_vec[col_num + 1]++;
+
+    // Drop column if it has become 0-width now
+    if (m_col_vec[col_num] == 0)
+        m_col_vec.erase (m_col_vec.begin() + col_num);
 }
 
 bool GncFwTokenizer::col_can_widen (uint col_num)
 {
+    // Can't widen the last column, it always sticks to the end of the parseable data
     auto last_col = m_col_vec.size() - 1;
-    int col_end, next_col_end;
-
-    if (col_num > last_col)
-        return false;
-
-    col_end = m_col_vec[col_num];
-    next_col_end = (col_num == last_col - 1)
-                    ? m_longest_line
-                    : m_col_vec[col_num + 1];
-
-    if (col_end + 1 >= next_col_end)
+    if (col_num >= last_col)
         return false;
     else
         return true;
@@ -103,8 +71,15 @@ bool GncFwTokenizer::col_can_widen (uint col_num)
 
 void GncFwTokenizer::col_widen (uint col_num)
 {
-    if (col_can_widen (col_num))
-        m_col_vec[col_num]++;
+    if (!col_can_widen (col_num))
+        return;
+
+    m_col_vec[col_num]++;
+    m_col_vec[col_num + 1]--;
+
+    // Drop next column if it has become 0-width now
+    if (m_col_vec[col_num + 1] == 0)
+        m_col_vec.erase (m_col_vec.begin() + col_num + 1);
 }
 
 bool GncFwTokenizer::col_can_split (uint col_num, uint position)
@@ -113,9 +88,8 @@ bool GncFwTokenizer::col_can_split (uint col_num, uint position)
     if (col_num > last_col)
         return false;
 
-    uint col_start = (col_num == 0) ? 0 : m_col_vec[col_num - 1];
     uint col_end = m_col_vec[col_num];
-    if (position <= col_start || position >= col_end)
+    if (position < 1 || position >= col_end)
         return false;
     else
         return true;
@@ -125,8 +99,8 @@ void GncFwTokenizer::col_split (uint col_num, uint position)
 {
     if (col_can_split (col_num, position))
     {
-        uint col_start = (col_num == 0) ? 0 : m_col_vec[col_num - 1];;
-        m_col_vec.insert (m_col_vec.begin() + col_num, col_start + position);
+        m_col_vec.insert (m_col_vec.begin() + col_num, position);
+        m_col_vec[col_num + 1] -= position;
     }
 }
 
@@ -147,6 +121,16 @@ void GncFwTokenizer::cols_from_string(const std::string& col_str)
     // Clear existing columns first
     columns();
 
+    /* Set an initial column as expected by the code below
+     * If no data is read yet, take a rather large value
+     * Otherwise take the size of the widest line in the data
+     */
+    if (m_longest_line != 0)
+        m_col_vec.push_back (m_longest_line);
+    else
+        m_col_vec.push_back (99999);
+
+    uint col = 0;
     std::istringstream in_stream(col_str);
     std::string col_end_str;
     while (std::getline (in_stream, col_end_str, ','))
@@ -154,9 +138,14 @@ void GncFwTokenizer::cols_from_string(const std::string& col_str)
         if (col_end_str.empty())
             continue;  // Skip empty column positions
 
-        uint charindex = std::stoi (col_end_str);
-        col_add (charindex);
+        uint col_width = std::stoi (col_end_str);
+        col_split (col, col_width);
+        col++;
     }
+
+    // If no data is loaded yet, remove last, unreasonably wide column
+    if (m_longest_line == 0)
+        m_col_vec.pop_back();
 }
 
 
@@ -175,10 +164,31 @@ void GncFwTokenizer::load_file(const std::string& path)
         line.clear();
     }
 
-    /* Set a sane default for the offsets
-     * That is, assume one column with all the data */
     if (m_col_vec.empty())
+        /* Set a sane default for the offsets
+         * That is, assume one column with all the data */
         m_col_vec.push_back(m_longest_line);
+    else
+    {
+        /* Adjust existing last column(s) so the total column width
+         * equals the width of the longest line
+         * This may mean
+         * - widening the last column to widen to the longest line or
+         * - deleting columns/narrowing the last one to reduce to the longest line
+         */
+        uint total_width = 0;
+        for (auto col_width : m_col_vec)
+            total_width += col_width;
+
+        if (m_longest_line > total_width)
+            m_col_vec.back() += m_longest_line - total_width;
+        else if (m_longest_line < total_width)
+        {
+            while (total_width - m_col_vec.back() > m_longest_line)
+                col_delete (m_col_vec[m_col_vec.size() - 2]);
+            m_col_vec.back() -= total_width - m_longest_line;
+        }
+    }
 }
 
 
diff --git a/src/import-export/csv-imp/gnc-fw-tokenizer.hpp b/src/import-export/csv-imp/gnc-fw-tokenizer.hpp
index 6498661..520ae7a 100644
--- a/src/import-export/csv-imp/gnc-fw-tokenizer.hpp
+++ b/src/import-export/csv-imp/gnc-fw-tokenizer.hpp
@@ -61,8 +61,6 @@ public:
     uint get_column (uint num);
 
     // Column manipulators
-    bool col_can_add (uint col_end);
-    void col_add (uint col_end);
     bool col_can_delete (uint col_num);
     void col_delete (uint col_num);
     bool col_can_narrow (uint col_num);

commit bce5eaaa1de99473d265476bfc3f8749966c0b43
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Thu Sep 15 14:26:35 2016 +0200

    Prevent crash when switching to fixed with in assistant
    
    The problem was initially the user doesn't have any column offsets
    defined which the tokenize function wasn't handling properly

diff --git a/src/import-export/csv-imp/gnc-fw-tokenizer.cpp b/src/import-export/csv-imp/gnc-fw-tokenizer.cpp
index 5d7227a..0b308ae 100644
--- a/src/import-export/csv-imp/gnc-fw-tokenizer.cpp
+++ b/src/import-export/csv-imp/gnc-fw-tokenizer.cpp
@@ -174,6 +174,11 @@ void GncFwTokenizer::load_file(const std::string& path)
 
         line.clear();
     }
+
+    /* Set a sane default for the offsets
+     * That is, assume one column with all the data */
+    if (m_col_vec.empty())
+        m_col_vec.push_back(m_longest_line);
 }
 
 
diff --git a/src/import-export/csv-imp/gnc-fw-tokenizer.hpp b/src/import-export/csv-imp/gnc-fw-tokenizer.hpp
index fb06b15..6498661 100644
--- a/src/import-export/csv-imp/gnc-fw-tokenizer.hpp
+++ b/src/import-export/csv-imp/gnc-fw-tokenizer.hpp
@@ -75,7 +75,7 @@ public:
     std::string cols_to_string();
     void cols_from_string(const std::string& col_str);
 
-    void load_file (const std::string& path);
+    void load_file (const std::string& path) override;
     int  tokenize() override;
 
 
diff --git a/src/import-export/csv-imp/gnc-tokenizer.cpp b/src/import-export/csv-imp/gnc-tokenizer.cpp
index e1c42a5..51cd77d 100644
--- a/src/import-export/csv-imp/gnc-tokenizer.cpp
+++ b/src/import-export/csv-imp/gnc-tokenizer.cpp
@@ -88,12 +88,6 @@ GncTokenizer::encoding()
 }
 
 
-int GncTokenizer::tokenize()
-{
-	return 0;
-}
-
-
 const std::vector<str_vec>&
 GncTokenizer::get_tokens()
 {
diff --git a/src/import-export/csv-imp/gnc-tokenizer.hpp b/src/import-export/csv-imp/gnc-tokenizer.hpp
index 09e4445..0e5363b 100644
--- a/src/import-export/csv-imp/gnc-tokenizer.hpp
+++ b/src/import-export/csv-imp/gnc-tokenizer.hpp
@@ -67,11 +67,11 @@ public:
     GncTokenizer& operator=(GncTokenizer&&) = default;      // move assignment
     virtual ~GncTokenizer() = default;                      // destructor
 
-    void load_file(const std::string& path);
+    virtual void load_file(const std::string& path);
     const std::string& current_file();
     void encoding(const std::string& encoding);
     const std::string& encoding();
-    virtual int  tokenize();
+    virtual int  tokenize() = 0;
     const std::vector<str_vec>& get_tokens();
     
 protected:

commit da0120d5332441a1eb3157b030cf3c9e1d5d8833
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Thu Sep 15 11:40:28 2016 +0200

    Readd a column for the error messages when reviewing parse errors

diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index ef73ac8..bbfe500 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -1665,14 +1665,17 @@ static void gnc_csv_preview_update_assist (CsvImportTrans* info)
      * - the column type as a user visible (translated) string
      * - the internal type for this column
      * So store looks like:
-     * col_type_str 0, col_type, col_type_str 1, col_type 1, ..., col_type_str ncols, col_type ncols. */
-    GType* types = g_new (GType, 2 * ncols);
+     * col_type_str 0, col_type, col_type_str 1, col_type 1, ..., col_type_str ncols, col_type ncols.
+     * And then a final column is added for the Error column (which doesn't require a col_type) */
+    GType* headertypes = g_new (GType, 2 * ncols + 1);
     for (guint i = 0; i < 2 * ncols; i += 2)
     {
-        types[i] = G_TYPE_STRING;
-        types[i+1] = G_TYPE_INT;
+        headertypes[i] = G_TYPE_STRING;
+        headertypes[i+1] = G_TYPE_INT;
     }
-    auto ctstore = gtk_list_store_newv (2 * ncols, types);
+    headertypes[2 * ncols] = G_TYPE_STRING;
+    auto ctstore = gtk_list_store_newv (2 * ncols + 1, headertypes);
+    g_free (headertypes);
 
     /* Set all the column types to what's in the parse data. */
     GtkTreeIter iter;
@@ -1684,16 +1687,19 @@ static void gnc_csv_preview_update_assist (CsvImportTrans* info)
                 2 * i + 1, static_cast<int>(info->parse_data->column_types[i]),
                 -1);
     }
+    gtk_list_store_set (ctstore, &iter, 2 * ncols, _("Errors"), -1);
     gtk_tree_view_set_model (info->ctreeview, GTK_TREE_MODEL(ctstore));
 
+
     // Set up file data liststore
 
     /* store is a liststore to hold the data from the file being imported.
        it contains only strings. */
-    for (guint i = 0; i <  ncols + 1; i++)
-        types[i] = G_TYPE_STRING;
-    auto store = gtk_list_store_newv (ncols + 1, types);
-    g_free (types);
+    GType* bodytypes = g_new (GType, ncols + 2);
+    for (guint i = 0; i <  ncols + 2; i++)
+        bodytypes[i] = G_TYPE_STRING;
+    auto store = gtk_list_store_newv (ncols + 2, bodytypes);
+    g_free (bodytypes);
 
     /* Fill the data liststore with data from the file. */
     info->num_of_rows = info->parse_data->orig_lines.size();
@@ -1715,6 +1721,8 @@ static void gnc_csv_preview_update_assist (CsvImportTrans* info)
             uint pos = cell_str_it - parse_line.first.cbegin() + 1;
             gtk_list_store_set (store, &iter, pos, cell_str_it->c_str(), -1);
         }
+        /* Add the optional error messages in the last column of the store */
+        gtk_list_store_set (store, &iter, ncols + 1, parse_line.second.c_str(), -1);
     }
     gtk_tree_view_set_model (info->treeview, GTK_TREE_MODEL(store));
 
@@ -1783,6 +1791,21 @@ static void gnc_csv_preview_update_assist (CsvImportTrans* info)
         g_object_set_data (G_OBJECT(col->button), "col-num",GINT_TO_POINTER(i));
     }
 
+    /* Add a column for the error messages when reviewing errors */
+    if (info->previewing_errors)
+    {
+        auto crenderer = gtk_cell_renderer_text_new();
+        gtk_tree_view_insert_column_with_attributes (info->ctreeview,
+                -1, "", crenderer, "text", 2 * ncols, NULL);
+
+        auto renderer = gtk_cell_renderer_text_new();
+        auto col = gtk_tree_view_column_new_with_attributes ("", renderer, "background", 0,
+                                                             "text", ncols + 1, NULL);
+        gtk_tree_view_insert_column (info->treeview, col, -1);
+        /* Enable resizing of the columns. */
+        gtk_tree_view_column_set_resizable (col, TRUE);
+    }
+
     /* Select the header row */
     gtk_tree_model_get_iter_first (GTK_TREE_MODEL(ctstore), &iter);
     auto selection = gtk_tree_view_get_selection (info->ctreeview);

commit c1d798d0b0748c2237b406212d4f3ea274d32771
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Thu Sep 15 10:23:11 2016 +0200

    Return nullptr in case of invalid numeric field to allow proper error handling

diff --git a/src/import-export/csv-imp/gnc-csv-imp-trans.cpp b/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
index 469da05..b298284 100644
--- a/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
+++ b/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
@@ -396,7 +396,7 @@ static gnc_numeric* convert_amount_col_str (const std::string &str, int currency
 {
     /* If a cell is empty or just spaces return 0 as amount */
     if(!boost::regex_search(str, boost::regex("[0-9]")))
-        return new gnc_numeric({0, 0});
+        return nullptr;
 
     auto expr = boost::make_u32regex("[[:Sc:]]");
     std::string str_no_symbols = boost::u32regex_replace(str, expr, "");

commit e0caec0e225d4ca54a0a89ba592db492ce92dbaa
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Thu Sep 15 10:11:27 2016 +0200

    Rearrange statements in gnc_csv_preview_update_assist
    
    - Use a more logical grouping for setting up liststores and columns
    - Use one common store for all the header line's combo box entries
    - Store column number in each combo box entry for later retrieval

diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index a474b7c..ef73ac8 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -109,7 +109,6 @@ typedef struct
     GtkImage        *instructions_image;            /**< The instructions image */
     bool             encoding_selected_called;      /**< Before encoding_selected is first called, this is false.
                                                        * (See description of encoding_selected.) */
-    bool             not_empty;                     /**< false initially, true after the first type gnc_csv_preview_update_assist is called. */
     bool             previewing_errors;             /**< true if the dialog is displaying
                                                        * error lines, instead of all the file data. */
     int              code_encoding_calls;           /**< Normally this is 0. If the computer
@@ -117,7 +116,6 @@ typedef struct
                                                        * 2. encoding_selected is called twice,
                                                        * each time decrementing this by 1. */
     bool             skip_errors;                   /**< This is false until the user checks the skip errors. */
-    GtkWidget      **treeview_buttons;              /**< This array contains the header buttons in treeview */
     int              num_of_rows;                   /**< The number of rows in the store */
     int              longest_line;                  /**< The length of the longest row */
     int              fixed_context_col;             /**< The number of the column whose the user has clicked */
@@ -1408,14 +1406,7 @@ static void header_button_press_handler (GtkWidget* button, GdkEventButton* even
     gtk_widget_get_allocation (gtk_bin_get_child (GTK_BIN(button)), &alloc);
     offset = alloc.x - alloc.x;
     /* Find the column that was clicked. */
-    for (i = 0; i < ncols; i++)
-    {
-        if (info->treeview_buttons[i] == button)
-        {
-            col = i;
-            break;
-        }
-    }
+    col = GPOINTER_TO_INT(g_object_get_data (G_OBJECT(button), "col-num"));
 
     /* Don't let the user affect the last column if it has error messages. */
     if (col == ncols)
@@ -1661,70 +1652,50 @@ bool get_list_of_accounts (CsvImportTrans* info, GtkTreeModel *store)
  */
 static void gnc_csv_preview_update_assist (CsvImportTrans* info)
 {
-    /* store has the data from the file being imported.
-     * ctstore contains pointers the actual text that
-     * appears in info->ctreeview.
-     * combostore is a shared store for the header combo boxes. It
-     * holds the possible column types */
-    GtkListStore *store, *ctstore, *combostore;
-    GtkTreeIter iter;
-    GtkTreeSelection *selection;
     /* ncols is the number of columns in the file data. */
-    guint i, ncols = info->parse_data->column_types.size();
+    auto ncols = info->parse_data->column_types.size();
 
-    /* store contains only strings. */
-    GType* types = g_new (GType, 2 * ncols);
-    for (i = 0; i <  ncols + 1; i++)
-        types[i] = G_TYPE_STRING;
-    store = gtk_list_store_newv (ncols + 1, types);
+    // Set up the header liststore
 
-    /* ctstore is arranged so that every two
+    /* ctstore will be the liststore for the header row, which displays the user's
+     * column type selection
+     * its related treeview is info->ctreeview
+     * ctstore is arranged so that every two
      * columns form a pair of
      * - the column type as a user visible (translated) string
      * - the internal type for this column
      * So store looks like:
      * col_type_str 0, col_type, col_type_str 1, col_type 1, ..., col_type_str ncols, col_type ncols. */
-    for (i = 0; i < 2 * ncols; i += 2)
+    GType* types = g_new (GType, 2 * ncols);
+    for (guint i = 0; i < 2 * ncols; i += 2)
     {
         types[i] = G_TYPE_STRING;
         types[i+1] = G_TYPE_INT;
     }
-    ctstore = gtk_list_store_newv (2 * ncols, types);
+    auto ctstore = gtk_list_store_newv (2 * ncols, types);
 
-    g_free (types);
-
-    combostore = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
-    for (auto col_type : gnc_csv_col_type_strs)
+    /* Set all the column types to what's in the parse data. */
+    GtkTreeIter iter;
+    gtk_list_store_append (ctstore, &iter);
+    for (guint i = 0; i < ncols; i++)
     {
-        gtk_list_store_append (combostore, &iter);
-        gtk_list_store_set (combostore, &iter, 0, _(col_type.second),
-                                               1, static_cast<int>(col_type.first),
-                                               -1);
+        gtk_list_store_set (ctstore, &iter,
+                2 * i, _(gnc_csv_col_type_strs[info->parse_data->column_types[i]]),
+                2 * i + 1, static_cast<int>(info->parse_data->column_types[i]),
+                -1);
     }
+    gtk_tree_view_set_model (info->ctreeview, GTK_TREE_MODEL(ctstore));
 
-    if (info->not_empty)
-    {
-        GList *tv_columns, *tv_columns_begin, *ctv_columns, *ctv_columns_begin;
-        tv_columns = tv_columns_begin = gtk_tree_view_get_columns (info->treeview);
-        ctv_columns = ctv_columns_begin = gtk_tree_view_get_columns (info->ctreeview);
-        /* Clear out existing columns in info->treeview. */
-        while (tv_columns != NULL)
-        {
-            gtk_tree_view_remove_column (info->treeview, GTK_TREE_VIEW_COLUMN(tv_columns->data));
-            tv_columns = g_list_next (tv_columns);
-        }
-        /* Do the same in info->ctreeview. */
-        while (ctv_columns != NULL)
-        {
-            gtk_tree_view_remove_column (info->ctreeview, GTK_TREE_VIEW_COLUMN(ctv_columns->data));
-            ctv_columns = g_list_next (ctv_columns);
-        }
-        g_list_free (tv_columns_begin);
-        g_list_free (ctv_columns_begin);
-        g_free (info->treeview_buttons);
-    }
+    // Set up file data liststore
 
-    /* Fill the data treeview with data from the file. */
+    /* store is a liststore to hold the data from the file being imported.
+       it contains only strings. */
+    for (guint i = 0; i <  ncols + 1; i++)
+        types[i] = G_TYPE_STRING;
+    auto store = gtk_list_store_newv (ncols + 1, types);
+    g_free (types);
+
+    /* Fill the data liststore with data from the file. */
     info->num_of_rows = info->parse_data->orig_lines.size();
     for (auto parse_line : info->parse_data->orig_lines)
     {
@@ -1732,6 +1703,7 @@ static void gnc_csv_preview_update_assist (CsvImportTrans* info)
         if (info->previewing_errors && parse_line.second.empty())
             continue;
 
+        GtkTreeIter iter;
         gtk_list_store_append (store, &iter);
 
         /* Row Color column */
@@ -1744,69 +1716,79 @@ static void gnc_csv_preview_update_assist (CsvImportTrans* info)
             gtk_list_store_set (store, &iter, pos, cell_str_it->c_str(), -1);
         }
     }
+    gtk_tree_view_set_model (info->treeview, GTK_TREE_MODEL(store));
 
-    /* Set all the column types to what's in the parse data. */
-    gtk_list_store_append (ctstore, &iter);
-    for (i = 0; i < ncols; i++)
+    // Set up the two header and file data treeviews using the liststores created above
+
+    /* Clear any columns from a previous invocation */
+    auto column = gtk_tree_view_get_column (info->ctreeview, 0);
+    while (column)
     {
-        gtk_list_store_set (ctstore, &iter,
-                2 * i, _(gnc_csv_col_type_strs[info->parse_data->column_types[i]]),
-                2 * i + 1, static_cast<int>(info->parse_data->column_types[i]),
-                -1);
+        gtk_tree_view_remove_column (info->ctreeview, column);
+        column = gtk_tree_view_get_column (info->ctreeview, 0);
+    }
+    column = gtk_tree_view_get_column (info->treeview, 0);
+    while (column)
+    {
+        gtk_tree_view_remove_column (info->treeview, column);
+        column = gtk_tree_view_get_column (info->treeview, 0);
+    }
+
+    /* combostore is a shared store for the header combocells in the header row.
+     * It holds the possible column types */
+    auto combostore = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
+    for (auto col_type : gnc_csv_col_type_strs)
+    {
+        GtkTreeIter iter;
+        gtk_list_store_append (combostore, &iter);
+        gtk_list_store_set (combostore, &iter, 0, _(col_type.second),
+                                               1, static_cast<int>(col_type.first),
+                                               -1);
     }
 
-    info->treeview_buttons = g_new (GtkWidget*, ncols);
     /* Insert columns into the data and column type treeviews. */
-    for (i = 0; i < ncols ; i++)
+    for (guint i = 0; i < ncols ; i++)
     {
-        GtkTreeViewColumn* col; /* The column we add to info->treeview. */
-        /* Create renderers for the data treeview (renderer) and the
-         * column type treeview (crenderer). */
-        GtkCellRenderer* renderer = gtk_cell_renderer_text_new(),
-                         *crenderer = gtk_cell_renderer_combo_new();
-        /* We want a monospace font for the data in case of fixed-width data. */
-        g_object_set (G_OBJECT(renderer), "family", "monospace", NULL);
-        /* We are the common model for the combo box entries, and we don't
-         * want the user to be able to manually enter their own column
-         * types. */
+        /* The header cells are combobox entries. They all use the same
+         * common model for the dropdown list while their text value
+         * comes from the header liststore (ctstore). */
+        auto crenderer = gtk_cell_renderer_combo_new();
+        /* Set the properties for the dropdown list */
         g_object_set (G_OBJECT(crenderer), "model", combostore, "text-column", 0,
                      "editable", TRUE, "has-entry", FALSE, NULL);
         g_signal_connect (G_OBJECT(crenderer), "changed",
                          G_CALLBACK(column_type_changed), (gpointer)info);
+        /* Insert the column */
+        gtk_tree_view_insert_column_with_attributes (info->ctreeview,
+                -1, "", crenderer, "text", 2 * i, NULL);
 
-        /* Add a single column for the treeview. */
-        col = gtk_tree_view_column_new_with_attributes ("", renderer, "text", i + 1, NULL);
-
-        /* Add the Color column 0 to the renderer */
-        gtk_tree_view_column_add_attribute (col, renderer, "background", 0);
+        /* The file data treeview cells are simple text cells. */
+        auto renderer = gtk_cell_renderer_text_new();
+        /* We want a monospace font for the data in case of fixed-width data. */
+        g_object_set (G_OBJECT(renderer), "family", "monospace", NULL);
 
+        /* Add a single column for the treeview. */
+        auto col = gtk_tree_view_column_new_with_attributes ("", renderer, "background", 0,
+                                                             "text", i + 1, NULL);
         gtk_tree_view_insert_column (info->treeview, col, -1);
         /* Enable resizing of the columns. */
         gtk_tree_view_column_set_resizable (col, TRUE);
 
-        /* Use the common model and alternating text entries from ctstore in
-         * info->ctreeview. */
-        gtk_tree_view_insert_column_with_attributes (info->ctreeview,
-                -1, "", crenderer, "text", 2 * i, NULL);
-
-        /* We need to allow clicking on the column headers for fixed-width
-         * column splitting and merging. */
+        /* We need to allow clicking on the file data's column headers for
+         * fixed-width column splitting and merging. */
         g_object_set (G_OBJECT(col), "clickable", TRUE, NULL);
         g_signal_connect (G_OBJECT(col->button), "button_press_event",
                          G_CALLBACK(header_button_press_handler), (gpointer)info);
-        info->treeview_buttons[i] = col->button;
+        /* Store the column number in the button to know which one was clicked later on */
+        g_object_set_data (G_OBJECT(col->button), "col-num",GINT_TO_POINTER(i));
     }
 
-    /* Set the treeviews to use the models. */
-    gtk_tree_view_set_model (info->treeview, GTK_TREE_MODEL(store));
-    gtk_tree_view_set_model (info->ctreeview, GTK_TREE_MODEL(ctstore));
-
     /* Select the header row */
     gtk_tree_model_get_iter_first (GTK_TREE_MODEL(ctstore), &iter);
-    selection = gtk_tree_view_get_selection (info->ctreeview);
+    auto selection = gtk_tree_view_get_selection (info->ctreeview);
     gtk_tree_selection_select_iter (selection, &iter);
 
-    /* Free the memory for the stores. */
+    /* Release our reference for the stores to allow proper memory management. */
     g_object_unref (GTK_TREE_MODEL(store));
     g_object_unref (GTK_TREE_MODEL(ctstore));
     g_object_unref (GTK_TREE_MODEL(combostore));
@@ -1822,9 +1804,6 @@ static void gnc_csv_preview_update_assist (CsvImportTrans* info)
     /* Set the date format to what's in the combo box (since we don't
      * necessarily know if this will always be the same). */
     info->parse_data->date_format = gtk_combo_box_get_active (GTK_COMBO_BOX(info->date_format_combo));
-
-    /* It's now been filled with some stuff. */
-    info->not_empty = true;
 }
 
 
@@ -2918,9 +2897,6 @@ csv_import_trans_assistant_create (CsvImportTrans *info)
         /* This is true only after encoding_selected is called, so we must
          * set it initially to false. */
         info->encoding_selected_called = false;
-
-        /* It is empty at first. */
-        info->not_empty = false;
     }
 
     /* Account page */

commit 6adbab1d38feeb07be2c9d7e0f11830889bb1f1a
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Wed Sep 14 20:32:41 2016 +0200

    Use one common liststore for all the combocells in the preview header treeview
    
    They all show the same list anyway and this simplifies the code

diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index 039dba8..a474b7c 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -304,7 +304,7 @@ csv_import_trans_load_settings (CsvImportTrans *info)
         // This section deals with the column types
         if (info->settings_data->column_types)
         {
-            GtkTreeModel *store;
+            GtkTreeModel *ctstore;
             GtkTreeIter   iter;
             gchar       **columns;
             int           i;
@@ -312,11 +312,11 @@ csv_import_trans_load_settings (CsvImportTrans *info)
 
             columns = g_strsplit (info->settings_data->column_types, ",", -1);
 
-            // store contains the column types and their (translated) string representation appearing in the column types treeview.
-            store = gtk_tree_view_get_model (info->ctreeview);
+            // ctstore contains the column types and their (translated) string representation appearing in the column types treeview.
+            ctstore = gtk_tree_view_get_model (info->ctreeview);
 
             // Get an iterator for the first (and only) row.
-            gtk_tree_model_get_iter_first (store, &iter);
+            gtk_tree_model_get_iter_first (ctstore, &iter);
 
             for (i=0; columns[i] != NULL; i++)
             {
@@ -328,17 +328,16 @@ csv_import_trans_load_settings (CsvImportTrans *info)
                 {
                     col_type = static_cast<GncTransPropType>(saved_col_type);
                     info->parse_data->column_types.at(i) = col_type;
-                    /* Set the column type. Store is arranged so that every three
-                     * columns is a triplet of
-                     * - model used for the combobox
+                    /* Set the column type. Store is arranged so that every two
+                     * columns is a pair of
                      * - the column type as a user visible (translated) string
                      * - the internal type for this column
-                     * So store looks like:
-                     * model 0, col_type_str 0, col_type, model 1, col_type_str 1, col_type 1, ..., model ncols, col_type_str ncols, col_type ncols. */
-                    if ((3 * i + 2) < gtk_tree_model_get_n_columns (store))
-                        gtk_list_store_set (GTK_LIST_STORE(store), &iter,
-                                3 * i + 1, _(gnc_csv_col_type_strs[col_type]),
-                                3 * i + 2, col_type,
+                     * So ctstore looks like:
+                     * col_type_str 0, col_type, col_type_str 1, col_type 1, ..., col_type_str ncols, col_type ncols. */
+                    if ((2 * i + 1) < gtk_tree_model_get_n_columns (ctstore))
+                        gtk_list_store_set (GTK_LIST_STORE(ctstore), &iter,
+                                2 * i, _(gnc_csv_col_type_strs[col_type]),
+                                2 * i + 1, col_type,
                                 -1);
                 }
                 else
@@ -539,7 +538,7 @@ csv_import_trans_save_settings_cb (GtkWidget *button, CsvImportTrans *info)
         int           i;
         GList        *columns;
         GList        *column;
-        GtkTreeModel *store;
+        GtkTreeModel *ctstore;
         GtkTreeIter   iter;
         gchar        *details = NULL;
 
@@ -564,24 +563,23 @@ csv_import_trans_save_settings_cb (GtkWidget *button, CsvImportTrans *info)
 
         /* This section deals with the Treeview column names */
         columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(info->ctreeview));
-        // store contains the actual strings appearing in the column types treeview.
-        store = gtk_tree_view_get_model (info->ctreeview);
+        // ctstore contains the actual strings appearing in the column types treeview.
+        ctstore = gtk_tree_view_get_model (info->ctreeview);
         // Get an iterator for the first (and only) row.
-        gtk_tree_model_get_iter_first (store, &iter);
+        gtk_tree_model_get_iter_first (ctstore, &iter);
 
-        for (column = columns, i = 2; column; column = g_list_next (column), i += 3)
+        for (column = columns, i = 0; column; column = g_list_next (column), i++)
         {
             auto col_type = GncTransPropType::NONE;
             gchar *col_type_str = NULL;
 
-            /* Get the column type. Store is arranged so that every three
-             * columns is a triplet of
-             * - model used for the combobox
+            /* Get the column type. Store is arranged so that every two
+             * columns is a pair of
              * - the column type as a user visible (translated) string
              * - the internal type for this column
-             * So store looks like:
-             * model 0, col_type_str 0, col_type, model 1, col_type_str 1, col_type 1, ..., model ncols, col_type_str ncols, col_type ncols. */
-            gtk_tree_model_get (store, &iter, i, &col_type, -1);
+             * So ctstore looks like:
+             * col_type_str 0, col_type, col_type_str 1, col_type 1, ..., col_type_str ncols, col_type ncols. */
+            gtk_tree_model_get (ctstore, &iter, 2 * i + 1, &col_type, -1);
 
             col_type_str = g_strdup_printf ("%i", static_cast<int>(col_type));
             if (!details)
@@ -1306,7 +1304,7 @@ static void column_type_changed (GtkCellRenderer* renderer, gchar* path,
     /* ncols is the number of columns in the data. */
     int i, ncols = info->parse_data->column_types.size();
     /* store has the actual strings that appear in info->ctreeview. */
-    GtkTreeModel* store = gtk_tree_view_get_model (info->ctreeview);
+    GtkTreeModel* ctstore = gtk_tree_view_get_model (info->ctreeview);
     GtkTreeModel* model;
     gint textColumn;
     GtkTreeIter iter;
@@ -1321,7 +1319,7 @@ static void column_type_changed (GtkCellRenderer* renderer, gchar* path,
             -1);
 
     /* Get an iterator for the first (and only) row. */
-    gtk_tree_model_get_iter_first (store, &iter);
+    gtk_tree_model_get_iter_first (ctstore, &iter);
 
     /* Go through each column. */
     for (i = 0; i < ncols; i++)
@@ -1341,21 +1339,20 @@ static void column_type_changed (GtkCellRenderer* renderer, gchar* path,
         {
             /* The data type of this column */
             auto cur_col_type = GncTransPropType::NONE;
-            /* Get the column type. Store is arranged so that every three
-             * columns is a triplet of
-             * - model used for the combobox
+            /* Get the column type. Store is arranged so that every two
+             * columns is a pair of
              * - the column type as a user visible (translated) string
              * - the internal type for this column
              * So store looks like:
-             * model 0, col_type_str 0, col_type, model 1, col_type_str 1, col_type 1, ..., model ncols, col_type_str ncols, col_type ncols. */
-            gtk_tree_model_get(store, &iter, 3 * i + 2, &cur_col_type, -1);
+             * col_type_str 0, col_type, col_type_str 1, col_type 1, ..., col_type_str ncols, col_type ncols. */
+            gtk_tree_model_get(ctstore, &iter, 2 * i + 1, &cur_col_type, -1);
             /* If this column has the same type as the user selected ... */
             if (cur_col_type == new_col_type)
             {
                 /* ... set this column to the "None" type. (We can't allow duplicate types.) */
-                gtk_list_store_set (GTK_LIST_STORE(store), &iter,
-                        3 * i + 1, _(gnc_csv_col_type_strs[GncTransPropType::NONE]),
-                        3 * i + 2, GncTransPropType::NONE,
+                gtk_list_store_set (GTK_LIST_STORE(ctstore), &iter,
+                        2 * i, _(gnc_csv_col_type_strs[GncTransPropType::NONE]),
+                        2 * i + 1, GncTransPropType::NONE,
                         -1);
             }
         }
@@ -1363,10 +1360,10 @@ static void column_type_changed (GtkCellRenderer* renderer, gchar* path,
         {
             /* Set the type for this column to what the user selected. (See
              * comment above "Get the column type. ..." for why we set
-             * column 3*i+1 in store.) */
-            gtk_list_store_set (GTK_LIST_STORE(store), &iter,
-                    3 * i + 1, new_text,
-                    3 * i + 2, new_col_type,
+             * column 2*i+1 in store.) */
+            gtk_list_store_set (GTK_LIST_STORE(ctstore), &iter,
+                    2 * i, new_text,
+                    2 * i + 1, new_col_type,
                     -1);
         }
     }
@@ -1452,13 +1449,13 @@ bool preview_settings_valid (CsvImportTrans* info)
     int oweight = 0;
     bool valid = true;
     bool havebalance = false;
-    /* store contains the actual strings appearing in the column types treeview. */
-    GtkTreeModel* store = gtk_tree_view_get_model (info->ctreeview);
+    /* ctstore contains the actual strings appearing in the column types treeview. */
+    GtkTreeModel* ctstore = gtk_tree_view_get_model (info->ctreeview);
     /* datastore contains the actual strings appearing in the preview treeview. */
     GtkTreeModel* datastore = gtk_tree_view_get_model (info->treeview);
     GtkTreeIter iter1, iter2;
     /* Get an iterator for the first (and only) row. */
-    gtk_tree_model_get_iter_first (store, &iter1);
+    gtk_tree_model_get_iter_first (ctstore, &iter1);
 
     /* Get an iterator for the first required row in the data store. */
     gtk_tree_model_iter_nth_child (GTK_TREE_MODEL(datastore), &iter2, NULL, info->start_row);
@@ -1468,14 +1465,13 @@ bool preview_settings_valid (CsvImportTrans* info)
     {
         gchar* prevstr = NULL; /* The string in this column from datastore. */
         auto col_type = GncTransPropType::NONE;
-        /* Get the column type. Store is arranged so that every three
-         * columns is a triplet of
-         * - model used for the combobox
+        /* Get the column type. Store is arranged so that every two
+         * columns is a pair of
          * - the column type as a user visible (translated) string
          * - the internal type for this column
-         * So store looks like:
-         * model 0, col_type_str 0, col_type, model 1, col_type_str 1, col_type 1, ..., model ncols, col_type_str ncols, col_type ncols. */
-        gtk_tree_model_get (store, &iter1, 3 * i + 2, &col_type, -1);
+         * So ctstore looks like:
+         * col_type_str 0, col_type, col_type_str 1, col_type 1, ..., col_type_str ncols, col_type ncols. */
+        gtk_tree_model_get (ctstore, &iter1, 2 * i + 1, &col_type, -1);
 
         /* Set the column_types array appropriately*/
         info->parse_data->column_types[i] = col_type;
@@ -1599,15 +1595,15 @@ bool get_list_of_accounts (CsvImportTrans* info, GtkTreeModel *store)
     gint     home_account_number = 0;
     gint     other_account_number = 0;
 
-    /* store contains the actual strings appearing in the column types treeview. */
-    GtkTreeModel* columnstore = gtk_tree_view_get_model (info->ctreeview);
+    /* ctstore contains the actual strings appearing in the column types treeview. */
+    GtkTreeModel* ctstore = gtk_tree_view_get_model (info->ctreeview);
     /* datastore contains the actual strings appearing in the preview treeview. */
     GtkTreeModel* datastore = gtk_tree_view_get_model (info->treeview);
 
     GtkTreeIter iter1, iter2, iter3;
 
     /* Get an iterator for the first (and only) row of the column store. */
-    gtk_tree_model_get_iter_first (columnstore, &iter1);
+    gtk_tree_model_get_iter_first (ctstore, &iter1);
 
     for (j = info->start_row; j <= info->end_row; j++)
     {
@@ -1617,14 +1613,13 @@ bool get_list_of_accounts (CsvImportTrans* info, GtkTreeModel *store)
             gchar* accstr = NULL;   /* The string in this column from datastore. */
             auto col_type = GncTransPropType::NONE;
 
-            /* Get the column type. Store is arranged so that every three
-             * columns is a triplet of
-             * - model used for the combobox
+            /* Get the column type. Store is arranged so that every two
+             * columns is a pair of
              * - the column type as a user visible (translated) string
              * - the internal type for this column
              * So store looks like:
-             * model 0, col_type_str 0, col_type, model 1, col_type_str 1, col_type 1, ..., model ncols, col_type_str ncols, col_type ncols. */
-            gtk_tree_model_get (columnstore, &iter1, 3 * i + 2, &col_type, -1);
+             * col_type_str 0, col_type, col_type_str 1, col_type 1, ..., col_type_str ncols, col_type ncols. */
+            gtk_tree_model_get (ctstore, &iter1, 2 * i + 1, &col_type, -1);
 
             /* We're only interested in columns of type ACCOUNT and OACCOUNT. */
             if ((col_type == GncTransPropType::ACCOUNT) || (col_type == GncTransPropType::OACCOUNT))
@@ -1666,52 +1661,45 @@ bool get_list_of_accounts (CsvImportTrans* info, GtkTreeModel *store)
  */
 static void gnc_csv_preview_update_assist (CsvImportTrans* info)
 {
-    /* store has the data from the file being imported. cstores is an
-     * array of stores that hold the combo box entries for each
-     * column. ctstore contains both pointers to models in cstore and
-     * the actual text that appears in info->ctreeview. */
-    GtkListStore *store, **cstores, *ctstore;
+    /* store has the data from the file being imported.
+     * ctstore contains pointers the actual text that
+     * appears in info->ctreeview.
+     * combostore is a shared store for the header combo boxes. It
+     * holds the possible column types */
+    GtkListStore *store, *ctstore, *combostore;
     GtkTreeIter iter;
     GtkTreeSelection *selection;
     /* ncols is the number of columns in the file data. */
     guint i, ncols = info->parse_data->column_types.size();
 
     /* store contains only strings. */
-    GType* types = g_new (GType, 3 * ncols);
+    GType* types = g_new (GType, 2 * ncols);
     for (i = 0; i <  ncols + 1; i++)
         types[i] = G_TYPE_STRING;
     store = gtk_list_store_newv (ncols + 1, types);
 
-    /* ctstore is arranged so that every three
-     * columns is a triplet of
-     * - model used for the combobox
+    /* ctstore is arranged so that every two
+     * columns form a pair of
      * - the column type as a user visible (translated) string
      * - the internal type for this column
      * So store looks like:
-     * model 0, col_type_str 0, col_type, model 1, col_type_str 1, col_type 1, ..., model ncols, col_type_str ncols, col_type ncols. */
-    for (i = 0; i < 3 * ncols; i += 3)
+     * col_type_str 0, col_type, col_type_str 1, col_type 1, ..., col_type_str ncols, col_type ncols. */
+    for (i = 0; i < 2 * ncols; i += 2)
     {
-        types[i] = GTK_TYPE_TREE_MODEL;
-        types[i+1] = G_TYPE_STRING;
-        types[i+2] = G_TYPE_INT;
+        types[i] = G_TYPE_STRING;
+        types[i+1] = G_TYPE_INT;
     }
-    ctstore = gtk_list_store_newv (3 * ncols, types);
+    ctstore = gtk_list_store_newv (2 * ncols, types);
 
     g_free (types);
 
-    /* Each element in cstores is a single column model. */
-    cstores = g_new (GtkListStore*, ncols);
-    for (i = 0; i < ncols; i++)
+    combostore = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
+    for (auto col_type : gnc_csv_col_type_strs)
     {
-        cstores[i] = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
-        /* Add all of the possible entries to the combo box. */
-        for (auto col_type : gnc_csv_col_type_strs)
-        {
-            gtk_list_store_append (cstores[i], &iter);
-            gtk_list_store_set (cstores[i], &iter, 0, _(col_type.second),
-                                                   1, static_cast<int>(col_type.first),
-                                                   -1);
-        }
+        gtk_list_store_append (combostore, &iter);
+        gtk_list_store_set (combostore, &iter, 0, _(col_type.second),
+                                               1, static_cast<int>(col_type.first),
+                                               -1);
     }
 
     if (info->not_empty)
@@ -1737,8 +1725,7 @@ static void gnc_csv_preview_update_assist (CsvImportTrans* info)
     }
 
     /* Fill the data treeview with data from the file. */
-    /* Also, update the longest line value within the following loop (whichever is executed). */
-    info->num_of_rows = 0;
+    info->num_of_rows = info->parse_data->orig_lines.size();
     for (auto parse_line : info->parse_data->orig_lines)
     {
         // When previewing errors skip all lines that don't have errors
@@ -1752,13 +1739,10 @@ static void gnc_csv_preview_update_assist (CsvImportTrans* info)
 
         for (auto cell_str_it = parse_line.first.cbegin(); cell_str_it != parse_line.first.cend(); cell_str_it++)
         {
-            /* Add this cell's length to the row's length and set the value of the list store. */
+            /* Set the value of the proper column in the list store. */
             uint pos = cell_str_it - parse_line.first.cbegin() + 1;
             gtk_list_store_set (store, &iter, pos, cell_str_it->c_str(), -1);
         }
-
-        /* Set the number of rows in the store */
-        info->num_of_rows++;
     }
 
     /* Set all the column types to what's in the parse data. */
@@ -1766,9 +1750,8 @@ static void gnc_csv_preview_update_assist (CsvImportTrans* info)
     for (i = 0; i < ncols; i++)
     {
         gtk_list_store_set (ctstore, &iter,
-                3 * i, cstores[i],
-                3 * i + 1, _(gnc_csv_col_type_strs[info->parse_data->column_types[i]]),
-                3 * i + 2, static_cast<int>(info->parse_data->column_types[i]),
+                2 * i, _(gnc_csv_col_type_strs[info->parse_data->column_types[i]]),
+                2 * i + 1, static_cast<int>(info->parse_data->column_types[i]),
                 -1);
     }
 
@@ -1783,10 +1766,10 @@ static void gnc_csv_preview_update_assist (CsvImportTrans* info)
                          *crenderer = gtk_cell_renderer_combo_new();
         /* We want a monospace font for the data in case of fixed-width data. */
         g_object_set (G_OBJECT(renderer), "family", "monospace", NULL);
-        /* We are using cstores for the combo box entries, and we don't
+        /* We are the common model for the combo box entries, and we don't
          * want the user to be able to manually enter their own column
          * types. */
-        g_object_set (G_OBJECT(crenderer), "model", cstores[i], "text-column", 0,
+        g_object_set (G_OBJECT(crenderer), "model", combostore, "text-column", 0,
                      "editable", TRUE, "has-entry", FALSE, NULL);
         g_signal_connect (G_OBJECT(crenderer), "changed",
                          G_CALLBACK(column_type_changed), (gpointer)info);
@@ -1800,11 +1783,11 @@ static void gnc_csv_preview_update_assist (CsvImportTrans* info)
         gtk_tree_view_insert_column (info->treeview, col, -1);
         /* Enable resizing of the columns. */
         gtk_tree_view_column_set_resizable (col, TRUE);
-        /* Use the alternating model and text entries from ctstore in
+
+        /* Use the common model and alternating text entries from ctstore in
          * info->ctreeview. */
         gtk_tree_view_insert_column_with_attributes (info->ctreeview,
-                -1, "", crenderer, "model", 3 * i,
-                "text", 3 * i + 1, NULL);
+                -1, "", crenderer, "text", 2 * i, NULL);
 
         /* We need to allow clicking on the column headers for fixed-width
          * column splitting and merging. */
@@ -1826,8 +1809,7 @@ static void gnc_csv_preview_update_assist (CsvImportTrans* info)
     /* Free the memory for the stores. */
     g_object_unref (GTK_TREE_MODEL(store));
     g_object_unref (GTK_TREE_MODEL(ctstore));
-    for (i = 0; i < ncols; i++)
-        g_object_unref (GTK_TREE_MODEL(cstores[i]));
+    g_object_unref (GTK_TREE_MODEL(combostore));
 
     /* Make the things actually appear. */
     gtk_widget_show_all (GTK_WIDGET(info->treeview));
@@ -1911,19 +1893,6 @@ void gnc_csv_reset_preview_setting (CsvImportTrans *info, bool block)
     gtk_combo_box_set_active (GTK_COMBO_BOX(info->date_format_combo), 0);
     gtk_combo_box_set_active (GTK_COMBO_BOX(info->currency_format_combo), 0);
     go_charmap_sel_set_encoding (info->encselector, "UTF-8");
-
-// FIXME Should I explicitly do a reparse here, or did the above code already trigger it in some way ?
-//    GError* error = NULL;
-//    if (gnc_csv_parse (info->parse_data, false, &error))
-//    {
-//        gnc_error_dialog (NULL, "%s", error->message);
-//        return false;
-//    }
-//    gnc_csv_preview_update_assist (info);
-//
-//    /* Refresh the row highlighting */
-//    row_selection_update (info);
-
 }
 
 

commit bbeb351aa83ed5178345f242f43fec8aaf488ffa
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Wed Sep 14 18:49:06 2016 +0200

    Some changes to adhere better to the gnucash coding guidelines
    
    - use m_ prefix on member variables
    - prefer passing by reference over copying

diff --git a/src/import-export/csv-imp/gnc-csv-tokenizer.cpp b/src/import-export/csv-imp/gnc-csv-tokenizer.cpp
index b2d7063..8e7ebaf 100644
--- a/src/import-export/csv-imp/gnc-csv-tokenizer.cpp
+++ b/src/import-export/csv-imp/gnc-csv-tokenizer.cpp
@@ -13,15 +13,15 @@
 void
 GncCsvTokenizer::set_separators(const std::string& separators)
 {
-    sep_str = separators;
+    m_sep_str = separators;
 }
 
 
 int GncCsvTokenizer::tokenize()
 {
-    typedef boost::tokenizer< boost::escaped_list_separator<char> > Tokenizer;
+    using Tokenizer = boost::tokenizer< boost::escaped_list_separator<char>>;
 
-    boost::escaped_list_separator<char> sep("\\", sep_str, "\"");
+    boost::escaped_list_separator<char> sep("\\", m_sep_str, "\"");
 
     std::vector<std::string> vec;
     std::string line;
@@ -30,8 +30,8 @@ int GncCsvTokenizer::tokenize()
     bool inside_quotes(false);
     size_t last_quote(0);
 
-    tokenized_contents.clear();
-    std::istringstream in_stream(utf8_contents);
+    m_tokenized_contents.clear();
+    std::istringstream in_stream(m_utf8_contents);
 
     while (std::getline (in_stream, buffer))
     {
@@ -64,7 +64,7 @@ int GncCsvTokenizer::tokenize()
         // for correctly parsed 3 fields per record
         // if (vec.size() < 3) continue;
 
-        tokenized_contents.push_back(vec);
+        m_tokenized_contents.push_back(vec);
     }
 
     return 0;
diff --git a/src/import-export/csv-imp/gnc-csv-tokenizer.hpp b/src/import-export/csv-imp/gnc-csv-tokenizer.hpp
index 636ed7d..1c5b615 100644
--- a/src/import-export/csv-imp/gnc-csv-tokenizer.hpp
+++ b/src/import-export/csv-imp/gnc-csv-tokenizer.hpp
@@ -58,7 +58,7 @@ public:
     int  tokenize() override;
 
 private:
-    std::string sep_str = ",";
+    std::string m_sep_str = ",";
 };
 
 #endif
diff --git a/src/import-export/csv-imp/gnc-dummy-tokenizer.cpp b/src/import-export/csv-imp/gnc-dummy-tokenizer.cpp
index c6cb395..87902b7 100644
--- a/src/import-export/csv-imp/gnc-dummy-tokenizer.cpp
+++ b/src/import-export/csv-imp/gnc-dummy-tokenizer.cpp
@@ -15,13 +15,13 @@ int GncDummyTokenizer::tokenize()
     std::vector<std::string> vec;
     std::string line;
 
-    tokenized_contents.clear();
-    std::istringstream in_stream(utf8_contents);
+    m_tokenized_contents.clear();
+    std::istringstream in_stream(m_utf8_contents);
 
     while (std::getline (in_stream, line))
     {
         vec.push_back (line);
-        tokenized_contents.push_back(vec);
+        m_tokenized_contents.push_back(vec);
 
         line.clear();
         vec.clear();
diff --git a/src/import-export/csv-imp/gnc-fw-tokenizer.cpp b/src/import-export/csv-imp/gnc-fw-tokenizer.cpp
index 245830b..5d7227a 100644
--- a/src/import-export/csv-imp/gnc-fw-tokenizer.cpp
+++ b/src/import-export/csv-imp/gnc-fw-tokenizer.cpp
@@ -14,13 +14,13 @@
 void
 GncFwTokenizer::columns(const std::vector<uint>& cols)
 {
-    col_vec = cols;
+    m_col_vec = cols;
 }
 
 
 bool GncFwTokenizer::col_can_add (uint col_end)
 {
-    if (col_end < 0 || col_end > longest_line)
+    if (col_end < 0 || col_end > m_longest_line)
         return false;
     else
         return true;
@@ -30,23 +30,23 @@ void GncFwTokenizer::col_add (uint col_end)
 {
     if (col_can_add (col_end))
     {
-        for (auto col_it = col_vec.begin(); col_it != col_vec.end(); col_it++)
+        for (auto col_it = m_col_vec.begin(); col_it != m_col_vec.end(); col_it++)
         {
             if (*col_it == col_end)
                 return; // don't add same column end twice in the column list
             if (*col_it > col_end)
-                col_vec.insert (col_it, col_end);
+                m_col_vec.insert (col_it, col_end);
         }
 
         // If we got here that means the requested col_end is beyond the currently
         // inserted columns, so append it
-        col_vec.push_back (col_end);
+        m_col_vec.push_back (col_end);
     }
 }
 
 bool GncFwTokenizer::col_can_delete (uint col_num)
 {
-    auto last_col = col_vec.size() - 1;
+    auto last_col = m_col_vec.size() - 1;
     if (col_num < 0 || col_num > last_col)
         return false;
     else
@@ -56,19 +56,19 @@ bool GncFwTokenizer::col_can_delete (uint col_num)
 void GncFwTokenizer::col_delete (uint col_num)
 {
     if (col_can_delete (col_num))
-        col_vec.erase (col_vec.begin() + col_num);
+        m_col_vec.erase (m_col_vec.begin() + col_num);
 }
 
 bool GncFwTokenizer::col_can_narrow (uint col_num)
 {
-    auto last_col = col_vec.size() - 1;
+    auto last_col = m_col_vec.size() - 1;
     int col_start, next_col_start;
 
     if (col_num > last_col)
         return false;
 
-    col_start = (col_num == 0) ? 0 : col_vec[col_num - 1];
-    next_col_start = col_vec[col_num];
+    col_start = (col_num == 0) ? 0 : m_col_vec[col_num - 1];
+    next_col_start = m_col_vec[col_num];
 
     if (next_col_start - 1 <= col_start)
         return false;
@@ -79,21 +79,21 @@ bool GncFwTokenizer::col_can_narrow (uint col_num)
 void GncFwTokenizer::col_narrow (uint col_num)
 {
     if (col_can_narrow (col_num))
-        col_vec[col_num]--;
+        m_col_vec[col_num]--;
 }
 
 bool GncFwTokenizer::col_can_widen (uint col_num)
 {
-    auto last_col = col_vec.size() - 1;
+    auto last_col = m_col_vec.size() - 1;
     int col_end, next_col_end;
 
     if (col_num > last_col)
         return false;
 
-    col_end = col_vec[col_num];
+    col_end = m_col_vec[col_num];
     next_col_end = (col_num == last_col - 1)
-                    ? longest_line
-                    : col_vec[col_num + 1];
+                    ? m_longest_line
+                    : m_col_vec[col_num + 1];
 
     if (col_end + 1 >= next_col_end)
         return false;
@@ -104,17 +104,17 @@ bool GncFwTokenizer::col_can_widen (uint col_num)
 void GncFwTokenizer::col_widen (uint col_num)
 {
     if (col_can_widen (col_num))
-        col_vec[col_num]++;
+        m_col_vec[col_num]++;
 }
 
 bool GncFwTokenizer::col_can_split (uint col_num, uint position)
 {
-    auto last_col = col_vec.size() - 1;
+    auto last_col = m_col_vec.size() - 1;
     if (col_num > last_col)
         return false;
 
-    uint col_start = (col_num == 0) ? 0 : col_vec[col_num - 1];
-    uint col_end = col_vec[col_num];
+    uint col_start = (col_num == 0) ? 0 : m_col_vec[col_num - 1];
+    uint col_end = m_col_vec[col_num];
     if (position <= col_start || position >= col_end)
         return false;
     else
@@ -125,8 +125,8 @@ void GncFwTokenizer::col_split (uint col_num, uint position)
 {
     if (col_can_split (col_num, position))
     {
-        uint col_start = (col_num == 0) ? 0 : col_vec[col_num - 1];;
-        col_vec.insert (col_vec.begin() + col_num, col_start + position);
+        uint col_start = (col_num == 0) ? 0 : m_col_vec[col_num - 1];;
+        m_col_vec.insert (m_col_vec.begin() + col_num, col_start + position);
     }
 }
 
@@ -134,7 +134,7 @@ void GncFwTokenizer::col_split (uint col_num, uint position)
 std::string GncFwTokenizer::cols_to_string()
 {
     std::ostringstream colstream;
-    for (auto col_end : col_vec)
+    for (auto col_end : m_col_vec)
         colstream<<col_end<<",";
     std::string colstr = colstream.str();
     if (!colstr.empty())
@@ -165,12 +165,12 @@ void GncFwTokenizer::load_file(const std::string& path)
     GncTokenizer::load_file(path);
 
     std::string line;
-    longest_line = 0;
-    std::istringstream in_stream(utf8_contents);
+    m_longest_line = 0;
+    std::istringstream in_stream(m_utf8_contents);
     while (std::getline (in_stream, line))
     {
-        if (line.size() > longest_line)
-            longest_line = line.size();
+        if (line.size() > m_longest_line)
+            m_longest_line = line.size();
 
         line.clear();
     }
@@ -181,14 +181,14 @@ int GncFwTokenizer::tokenize()
 {
     typedef boost::tokenizer< boost::offset_separator > Tokenizer;
 
-    boost::offset_separator sep(col_vec.begin(), col_vec.end(), false);
+    boost::offset_separator sep(m_col_vec.begin(), m_col_vec.end(), false);
 
     std::vector<std::string> vec;
     std::string line;
     std::string buffer;
 
-    tokenized_contents.clear();
-    std::istringstream in_stream(utf8_contents);
+    m_tokenized_contents.clear();
+    std::istringstream in_stream(m_utf8_contents);
 
     while (std::getline (in_stream, line))
     {
@@ -208,7 +208,7 @@ int GncFwTokenizer::tokenize()
         // for correctly parsed 3 fields per record
         // if (vec.size() < 3) continue;
 
-        tokenized_contents.push_back(vec);
+        m_tokenized_contents.push_back(vec);
     }
 
     return 0;
diff --git a/src/import-export/csv-imp/gnc-fw-tokenizer.hpp b/src/import-export/csv-imp/gnc-fw-tokenizer.hpp
index 523f63b..fb06b15 100644
--- a/src/import-export/csv-imp/gnc-fw-tokenizer.hpp
+++ b/src/import-export/csv-imp/gnc-fw-tokenizer.hpp
@@ -80,8 +80,8 @@ public:
 
 
 private:
-    std::vector<uint> col_vec;
-    uint longest_line;
+    std::vector<uint> m_col_vec;
+    uint m_longest_line;
 };
 
 #endif
diff --git a/src/import-export/csv-imp/gnc-tokenizer.cpp b/src/import-export/csv-imp/gnc-tokenizer.cpp
index 73963cd..e1c42a5 100644
--- a/src/import-export/csv-imp/gnc-tokenizer.cpp
+++ b/src/import-export/csv-imp/gnc-tokenizer.cpp
@@ -42,49 +42,49 @@ GncTokenizer::load_file(const std::string& path)
     if (path.empty())
         return;
 
-    imp_file_str = path;
+    m_imp_file_str = path;
 
     std::ifstream in;
     in.exceptions ( std::ifstream::failbit | std::ifstream::badbit );
-    in.open (imp_file_str.c_str(), std::ios::in | std::ios::binary);
+    in.open (m_imp_file_str.c_str(), std::ios::in | std::ios::binary);
 
-    raw_contents.clear();
+    m_raw_contents.clear();
     in.seekg(0, std::ios::end);
-    raw_contents.resize(in.tellg());
+    m_raw_contents.resize(in.tellg());
     in.seekg(0, std::ios::beg);
-    in.read(&raw_contents[0], raw_contents.size());
+    in.read(&m_raw_contents[0], m_raw_contents.size());
     in.close();
 
     // Guess encoding, user can override if needed later on.
     const char *guessed_enc = NULL;
-    guessed_enc = go_guess_encoding (raw_contents.c_str(),
-                                     raw_contents.length(),
-                                     enc_str.empty() ? "UTF-8" : enc_str.c_str(),
+    guessed_enc = go_guess_encoding (m_raw_contents.c_str(),
+                                     m_raw_contents.length(),
+                                     m_enc_str.empty() ? "UTF-8" : m_enc_str.c_str(),
                                      NULL);
     if (guessed_enc)
         this->encoding(guessed_enc);
     else
-        enc_str.clear();
+        m_enc_str.clear();
 
 }
 
-std::string
+const std::string&
 GncTokenizer::current_file()
 {
-    return imp_file_str;
+    return m_imp_file_str;
 }
 
 void
 GncTokenizer::encoding(const std::string& encoding)
 {
-    enc_str = encoding;
-    utf8_contents = boost::locale::conv::to_utf<char>(raw_contents, enc_str);
+    m_enc_str = encoding;
+    m_utf8_contents = boost::locale::conv::to_utf<char>(m_raw_contents, m_enc_str);
 }
 
-std::string
+const std::string&
 GncTokenizer::encoding()
 {
-    return enc_str;
+    return m_enc_str;
 }
 
 
@@ -94,7 +94,8 @@ int GncTokenizer::tokenize()
 }
 
 
-std::vector<str_vec> GncTokenizer::get_tokens()
+const std::vector<str_vec>&
+GncTokenizer::get_tokens()
 {
-    return tokenized_contents;
+    return m_tokenized_contents;
 }
diff --git a/src/import-export/csv-imp/gnc-tokenizer.hpp b/src/import-export/csv-imp/gnc-tokenizer.hpp
index 0daba06..09e4445 100644
--- a/src/import-export/csv-imp/gnc-tokenizer.hpp
+++ b/src/import-export/csv-imp/gnc-tokenizer.hpp
@@ -68,20 +68,20 @@ public:
     virtual ~GncTokenizer() = default;                      // destructor
 
     void load_file(const std::string& path);
-    std::string current_file();
+    const std::string& current_file();
     void encoding(const std::string& encoding);
-    std::string encoding();
+    const std::string& encoding();
     virtual int  tokenize();
-    std::vector<str_vec> get_tokens();
+    const std::vector<str_vec>& get_tokens();
     
 protected:
-    std::string utf8_contents;
-    std::vector<str_vec> tokenized_contents;
+    std::string m_utf8_contents;
+    std::vector<str_vec> m_tokenized_contents;
 
 private:
-    std::string imp_file_str;
-    std::string raw_contents;
-    std::string enc_str;
+    std::string m_imp_file_str;
+    std::string m_raw_contents;
+    std::string m_enc_str;
 };
 
 
diff --git a/src/import-export/csv-imp/test/test-tokenizer.cpp b/src/import-export/csv-imp/test/test-tokenizer.cpp
index 99d1712..a434e33 100644
--- a/src/import-export/csv-imp/test/test-tokenizer.cpp
+++ b/src/import-export/csv-imp/test/test-tokenizer.cpp
@@ -64,9 +64,9 @@ public:
 
 protected:
     std::string& get_utf8_contents(std::unique_ptr<GncTokenizer> &tokenizer)
-    { return tokenizer->utf8_contents; }
+    { return tokenizer->m_utf8_contents; }
     void set_utf8_contents(std::unique_ptr<GncTokenizer> &tokenizer, const std::string& newcontents)
-    { tokenizer->utf8_contents = newcontents; }
+    { tokenizer->m_utf8_contents = newcontents; }
     void test_gnc_tokenize_helper (const std::string& separators, tokenize_csv_test_data* test_data); // for csv tokenizer
     void test_gnc_tokenize_helper (tokenize_fw_test_data* test_data); // for csv tokenizer
 

commit 737cbfb35bfda091d38d150cd4a8ca2965dc3e9f
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Tue Sep 13 23:50:19 2016 +0200

    Drop fake test in fw tokenizer

diff --git a/src/import-export/csv-imp/gnc-fw-tokenizer.cpp b/src/import-export/csv-imp/gnc-fw-tokenizer.cpp
index 1be2bea..245830b 100644
--- a/src/import-export/csv-imp/gnc-fw-tokenizer.cpp
+++ b/src/import-export/csv-imp/gnc-fw-tokenizer.cpp
@@ -206,7 +206,7 @@ int GncFwTokenizer::tokenize()
 
         // example checking
         // for correctly parsed 3 fields per record
-        if (vec.size() < 3) continue;
+        // if (vec.size() < 3) continue;
 
         tokenized_contents.push_back(vec);
     }

commit f47d12c373f11dfb9b1d5f5521748f0cd6cba365
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Tue Sep 13 23:49:22 2016 +0200

    Add test cases for tokenizer classes

diff --git a/src/import-export/csv-imp/gnc-tokenizer.hpp b/src/import-export/csv-imp/gnc-tokenizer.hpp
index 8562815..0daba06 100644
--- a/src/import-export/csv-imp/gnc-tokenizer.hpp
+++ b/src/import-export/csv-imp/gnc-tokenizer.hpp
@@ -54,9 +54,11 @@ enum class GncImpFileFormat {
     FIXED_WIDTH
 };
 
+class GncTokenizerTest;
 
 class GncTokenizer
 {
+friend GncTokenizerTest;
 public:
     GncTokenizer() = default;                               // default constructor
     GncTokenizer(const GncTokenizer&) = default;            // copy constructor
diff --git a/src/import-export/csv-imp/test/CMakeLists.txt b/src/import-export/csv-imp/test/CMakeLists.txt
index c33f23b..e43a6f5 100644
--- a/src/import-export/csv-imp/test/CMakeLists.txt
+++ b/src/import-export/csv-imp/test/CMakeLists.txt
@@ -21,5 +21,20 @@ IF (NOT WIN32)
     CSV_IMP_TEST_INCLUDE_DIRS CSV_IMP_TEST_LIBS
     SRCDIR=${CMAKE_CURRENT_SOURCE_DIR}
   )
+
+  SET(MODULEPATH ${CMAKE_SOURCE_DIR}/src/import-export/csv-imp)
+  SET(gtest_csv_imp_LIBS gncmod-csv-import ${GLIB2_LDFLAGS} ${GTEST_LIB})
+  SET(gtest_csv_imp_INCLUDES
+    ${MODULEPATH}
+    ${CSV_IMP_TEST_INCLUDE_DIRS}
+    ${GTEST_INCLUDE_DIR})
+
+  SET(test_tokenizer_SOURCES
+    test-tokenizer.cpp
+    ${GTEST_SRC})
+  GNC_ADD_TEST(test-tokenizer "${test_tokenizer_SOURCES}"
+    gtest_csv_imp_INCLUDES gtest_csv_imp_LIBS
+    SRCDIR=${CMAKE_SOURCE_DIR}/src/import-export/csv-imp/test)
+
 ENDIF()
 
diff --git a/src/import-export/csv-imp/test/Makefile.am b/src/import-export/csv-imp/test/Makefile.am
index f79e562..bf7205d 100644
--- a/src/import-export/csv-imp/test/Makefile.am
+++ b/src/import-export/csv-imp/test/Makefile.am
@@ -116,6 +116,73 @@ TESTS_ENVIRONMENT = \
   G_DEBUG= \
   $(shell ${abs_top_srcdir}/src/gnc-test-env.pl --noexports ${GNC_TEST_DEPS})
 
+test_tokenizer_SOURCES = \
+    test-tokenizer.cpp
+test_tokenizer_LDADD = \
+  -L${top_builddir}/${MODULEPATH} \
+  -L${top_builddir}/src/import-export \
+  -L${top_builddir}/src/gnome \
+  -L${top_builddir}/src/gnome-utils \
+  -L${top_builddir}/src/gnome-search \
+  -L${top_builddir}/src/register/ledger-core \
+  -L${top_builddir}/src/register/register-core \
+  -L${top_builddir}/src/register/register-gnome \
+  -L${top_builddir}/src/report/report-system \
+  -L${top_builddir}/src/report/report-gnome \
+  -L${top_builddir}/src/html \
+  -L${top_builddir}/src/app-utils \
+  -L${top_builddir}/src/backend/xml \
+  -L${top_builddir}/src/engine \
+  -L${top_builddir}/src/core-utils \
+  -L${top_builddir}/src/gnc-module \
+  -L${top_builddir}/src/libqof/qof \
+  -L${top_builddir}/lib/stf \
+  ${top_builddir}/${MODULEPATH}/libgncmod-csv-import.la \
+  ${top_builddir}/src/import-export/libgncmod-generic-import.la \
+  ${top_builddir}/src/gnome/libgnc-gnome.la \
+  ${top_builddir}/src/gnome-utils/libgncmod-gnome-utils.la \
+  ${top_builddir}/src/register/ledger-core/libgncmod-ledger-core.la \
+  ${top_builddir}/src/report/report-gnome/libgncmod-report-gnome.la \
+  ${top_builddir}/src/app-utils/libgncmod-app-utils.la \
+  ${top_builddir}/src/backend/xml/libgnc-backend-xml-utils.la \
+  ${top_builddir}/src/engine/libgncmod-engine.la \
+  ${top_builddir}/src/core-utils/libgnc-core-utils.la \
+  ${top_builddir}/src/gnc-module/libgnc-module.la \
+  ${top_builddir}/src/libqof/qof/libgnc-qof.la \
+  ${top_builddir}/lib/stf/libgnc-stf.la \
+  ${GLIB_LIBS} \
+  $(GTEST_LIBS) \
+  $(BOOST_LDFLAGS)
+
+if !GOOGLE_TEST_LIBS
+nodist_test_tokenizer_SOURCES = \
+        ${GTEST_SRC}/src/gtest_main.cc
+endif
+
+test_tokenizer_CPPFLAGS = \
+  -I$(GTEST_HEADERS) \
+  -I$(top_srcdir)/$(MODULEPATH) \
+  -I${top_srcdir}/src/test-core \
+  -I${top_srcdir}/src \
+  -I${top_srcdir}/src/import-export \
+  -I${top_srcdir}/src/gnome \
+  -I${top_srcdir}/src/register/ledger-core \
+  -I${top_srcdir}/src/register/register-gnome \
+  -I${top_srcdir}/src/register/register-core \
+  -I${top_srcdir}/src/gnome-utils \
+  -I${top_srcdir}/src/app-utils \
+  -I${top_srcdir}/src/engine \
+  -I${top_srcdir}/src/core-utils \
+  -I${top_srcdir}/src/gnc-module \
+  -I${top_srcdir}/src/libqof/qof \
+  -I${top_srcdir}/lib/libc \
+  -I${top_srcdir}/lib \
+  ${GTK_CFLAGS} \
+  ${GLIB_CFLAGS} \
+  $(BOOST_CPPFLAGS)
+
+check_PROGRAMS += test-tokenizer
+
 EXTRA_DIST= \
   sample1.csv
 
diff --git a/src/import-export/csv-imp/test/test-tokenizer.cpp b/src/import-export/csv-imp/test/test-tokenizer.cpp
new file mode 100644
index 0000000..99d1712
--- /dev/null
+++ b/src/import-export/csv-imp/test/test-tokenizer.cpp
@@ -0,0 +1,246 @@
+/********************************************************************
+ * test-tokenizer.cpp: test suite for the csv tokenizer class and   *
+ *                     its child classes.                           *
+ * Copyright 2016 Geert Janssens <geert.gnucash at kobaltwit.be>       *
+ *                                                                  *
+ * 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 2 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, you can retrieve it from        *
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html            *
+ * or contact:                                                      *
+ *                                                                  *
+ * Free Software Foundation           Voice:  +1-617-542-5942       *
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
+ ********************************************************************/
+
+
+#include <guid.hpp>
+#include "../gnc-tokenizer.hpp"
+#include "../gnc-csv-tokenizer.hpp"
+#include "../gnc-fw-tokenizer.hpp"
+#include <gtest/gtest.h>
+#include <iostream>
+#include <fstream>      // fstream
+
+#include <string>
+#include <stdlib.h>     /* getenv */
+
+
+typedef struct
+{
+    const char *csv_line;
+    uint        num_fields;
+    const char *fields [8];
+} tokenize_csv_test_data;
+
+typedef struct
+{
+    const char *fw_line;
+    uint        num_fields;
+    uint        field_widths[8];
+    const char *fields [8];
+} tokenize_fw_test_data;
+
+class GncTokenizerTest : public ::testing::Test
+{
+public:
+    GncTokenizerTest()
+    {
+        fw_tok = GncTokenizerFactory(GncImpFileFormat::FIXED_WIDTH);
+        csv_tok = GncTokenizerFactory(GncImpFileFormat::CSV);
+    }
+
+    std::string get_filepath(const std::string& filename);
+
+protected:
+    std::string& get_utf8_contents(std::unique_ptr<GncTokenizer> &tokenizer)
+    { return tokenizer->utf8_contents; }
+    void set_utf8_contents(std::unique_ptr<GncTokenizer> &tokenizer, const std::string& newcontents)
+    { tokenizer->utf8_contents = newcontents; }
+    void test_gnc_tokenize_helper (const std::string& separators, tokenize_csv_test_data* test_data); // for csv tokenizer
+    void test_gnc_tokenize_helper (tokenize_fw_test_data* test_data); // for csv tokenizer
+
+    std::unique_ptr<GncTokenizer> fw_tok;
+    std::unique_ptr<GncTokenizer> csv_tok;
+};
+
+std::string GncTokenizerTest::get_filepath(const std::string& filename)
+{
+
+    auto srcdir = getenv("SRCDIR");
+    if (!srcdir)
+        return filename;
+    else
+        return std::string(srcdir) + "/" + filename;
+}
+
+//TEST_F (GncTokenizerTest, load_file_nonexisting)
+//{
+//
+//    auto file1 = get_filepath ("notexist.csv");
+//
+//    /* Test loading of a non-existing file */
+//    // FIXME determine what is actually thrown as I can't find it by trial and error
+//    EXPECT_THROW (fw_tok->load_file (file1), std::ios_base::failure);
+//    EXPECT_THROW (csv_tok->load_file (file1), std::ios_base::failure);
+//}
+
+TEST_F (GncTokenizerTest, load_file_existing)
+{
+
+    auto file = get_filepath ("sample1.csv");
+    auto expected_contents = std::string(
+            "Date,Num,Description,Notes,Account,Deposit,Withdrawal,Balance\n"
+            "05/01/15,45,Acme Inc.,,Miscellaneous,,\"1,100.00\",\n");
+
+    ASSERT_NO_THROW (fw_tok->load_file (file))
+        << "File " << file << " not found. Perhaps you should set the SRCDIR environment variable to point to its containing directory ?";
+    ASSERT_NO_THROW (csv_tok->load_file (file))
+        << "File " << file << " not found. Perhaps you should set the SRCDIR environment variable to point to its containing directory ?";
+
+    EXPECT_EQ(expected_contents, get_utf8_contents (fw_tok));
+    EXPECT_EQ(expected_contents, get_utf8_contents (csv_tok));
+}
+
+TEST_F (GncTokenizerTest, tokenize_from_csv_file)
+{
+
+    auto file = get_filepath ("sample1.csv");
+    auto expected_contents = std::string(
+            "Date,Num,Description,Notes,Account,Deposit,Withdrawal,Balance\n"
+            "05/01/15,45,Acme Inc.,,Miscellaneous,,\"1,100.00\",\n");
+
+    csv_tok->load_file (file);
+    csv_tok->tokenize();
+    auto tokens = csv_tok->get_tokens();
+    EXPECT_EQ(2ul, tokens.size());
+    EXPECT_EQ(8ul, tokens[0].size());
+    EXPECT_EQ(8ul, tokens[1].size());
+    EXPECT_EQ(std::string("Date"), tokens.at(0).at(0));
+    EXPECT_EQ(std::string("1,100.00"), tokens.at(1).at(6));
+}
+
+/* Test parsing for several different prepared strings
+ * These tests bypass file loading, rather taking a
+ * prepared set of strings as input. This makes it
+ * easier to add test cases without having to create new test files
+ * each time to load from.
+ * Note this bypasses encoding configuration, which should be tested
+ * independently.
+ */
+
+/* This helper function will run the parse step on the given data
+ * with the parser as configured by the calling test function.
+ * This allows the same code to be used with different csv test strings
+ * and parser option combinations.
+ */
+void
+GncTokenizerTest::test_gnc_tokenize_helper (const std::string& separators, tokenize_csv_test_data* test_data)
+{
+
+    GncCsvTokenizer *csvtok = dynamic_cast<GncCsvTokenizer*>(csv_tok.get());
+    csvtok->set_separators (separators);
+
+    int i = 0;
+    while (test_data[i].csv_line)
+    {
+
+        tokenize_csv_test_data cur_line = test_data[i];
+        set_utf8_contents (csv_tok, std::string(cur_line.csv_line));
+        csv_tok->tokenize();
+
+        // The tests only come with one line, so get the first row only
+        auto line_tok = csv_tok->get_tokens().front();
+        EXPECT_EQ (cur_line.num_fields, line_tok.size());
+        for (auto j = 0ul; j < cur_line.num_fields; j++)
+        {
+            EXPECT_EQ (std::string (cur_line.fields[j]), line_tok[j]);
+        }
+
+        i++;
+    }
+}
+
+static tokenize_csv_test_data comma_separated [] = {
+        { "Date,Num,Description,Notes,Account,Deposit,Withdrawal,Balance", 8, { "Date","Num","Description","Notes","Account","Deposit","Withdrawal","Balance" } },
+        { "05/01/15,45,Acme Inc.,,Miscellaneous,,\"1,100.00\",", 8, { "05/01/15","45","Acme Inc.","","Miscellaneous","","1,100.00","" } },
+        { "05/01/15,45,Acme Inc.,,Miscellaneous,", 6, { "05/01/15","45","Acme Inc.","","Miscellaneous","",NULL,NULL } },
+        { NULL, 0, { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } },
+};
+
+TEST_F (GncTokenizerTest, tokenize_comma_sep)
+{
+    test_gnc_tokenize_helper (",", comma_separated);
+}
+
+static tokenize_csv_test_data semicolon_separated [] = {
+        { "Date;Num;Description;Notes;Account;Deposit;Withdrawal;Balance", 8, { "Date","Num","Description","Notes","Account","Deposit","Withdrawal","Balance" } },
+        { "05/01/15;45;Acme Inc.;;Miscellaneous;;\"1,100.00\";", 8, { "05/01/15","45","Acme Inc.","","Miscellaneous","","1,100.00","" } },
+        { "05/01/15;45;Acme Inc.;;Miscellaneous;", 6, { "05/01/15","45","Acme Inc.","","Miscellaneous","",NULL,NULL } },
+        { NULL, 0, { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } },
+};
+TEST_F (GncTokenizerTest, tokenize_semicolon_sep)
+{
+    test_gnc_tokenize_helper (";", semicolon_separated);
+}
+
+
+
+void
+GncTokenizerTest::test_gnc_tokenize_helper (tokenize_fw_test_data* test_data)
+{
+
+    GncFwTokenizer *fwtok = dynamic_cast<GncFwTokenizer*>(fw_tok.get());
+
+    int i = 0;
+    while (test_data[i].fw_line)
+    {
+        tokenize_fw_test_data cur_line = test_data[i];
+        set_utf8_contents (fw_tok, std::string(cur_line.fw_line));
+        auto columns = std::vector<uint>();
+        for (auto j = 0ul; j < cur_line.num_fields; j++)
+            columns.push_back (cur_line.field_widths[j]);
+        fwtok->columns (columns);
+        fw_tok->tokenize();
+
+        // The tests only come with one line, so get the first row only
+        auto line_tok = fw_tok->get_tokens().front();
+        EXPECT_EQ (cur_line.num_fields, line_tok.size());
+        for (auto j = 0ul; j < cur_line.num_fields; j++)
+        {
+            EXPECT_EQ (std::string (cur_line.fields[j]), line_tok[j]);
+        }
+
+        i++;
+    }
+}
+
+static tokenize_fw_test_data fixed_width [] = {
+        { "Date    NumDescriptionAccountDeposit", 5,
+                { 8,3,11,7,7,0,0,0 },
+                { "Date","Num","Description","Account","Deposit",NULL,NULL,NULL } },
+        { "05/01/1545Acme Inc.Miscellaneous1,100.00", 5,
+                { 8,2,9,13,8,0,0,0 },
+                { "05/01/15","45","Acme Inc.","Miscellaneous","1,100.00",NULL,NULL,NULL } },
+        { "05/01/15  45  Acme Inc.Miscellaneous        1,100.00", 6,
+                { 10,4,9,13,8,8,0,0 },
+                { "05/01/15","45","Acme Inc.","Miscellaneous","","1,100.00",NULL,NULL } },
+        { NULL, 0,
+                { 0,0,0,0,0,0,0,0 },
+                { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } },
+};
+
+TEST_F (GncTokenizerTest, tokenize_fw)
+{
+    test_gnc_tokenize_helper (fixed_width);
+}

commit b425a5e7ddade7f31c82819626344bb801ca9a75
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Tue Sep 13 23:48:57 2016 +0200

    Trim whitespace in case of fixed width input

diff --git a/src/import-export/csv-imp/gnc-fw-tokenizer.cpp b/src/import-export/csv-imp/gnc-fw-tokenizer.cpp
index 4a3ce42..1be2bea 100644
--- a/src/import-export/csv-imp/gnc-fw-tokenizer.cpp
+++ b/src/import-export/csv-imp/gnc-fw-tokenizer.cpp
@@ -9,6 +9,7 @@
 
 #include <boost/tokenizer.hpp>
 #include <boost/locale.hpp>
+#include <boost/algorithm/string.hpp>
 
 void
 GncFwTokenizer::columns(const std::vector<uint>& cols)
@@ -192,7 +193,14 @@ int GncFwTokenizer::tokenize()
     while (std::getline (in_stream, line))
     {
         Tokenizer tok(line, sep);
-        vec.assign(tok.begin(),tok.end());
+        vec.clear();
+        for (auto token : tok)
+            vec.push_back (boost::trim_copy(token));
+        //vec.assign(tok.begin(),tok.end());
+
+        // Trim all leading and trailing whitespace
+        //for (auto token : vec)
+        //    boost::trim(token);
 
         line.clear(); // clear here, next check could fail
 

commit 1b44310b04adab2aebb151679f31462a4ec6988d
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sat Sep 10 18:03:16 2016 +0200

    Replace transactions GList with a std::multimap

diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index b2d186a..039dba8 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -2467,16 +2467,12 @@ csv_import_trans_assistant_match_page_prepare (GtkAssistant *assistant,
                              G_CALLBACK(on_matcher_help_clicked), info->gnc_csv_importer_gui);
             gtk_widget_show (GTK_WIDGET(info->help_button));
 
-            /* Get the list of the transactions that were created. */
-            GList *transactions = info->parse_data->transactions;
             /* Copy all of the transactions to the importer GUI. */
-            while (transactions != NULL)
+            for (auto trans_it : info->parse_data->transactions)
             {
-                GncCsvTransLine* trans_line = (GncCsvTransLine*) transactions->data;
+                auto trans_line = trans_it.second;
                 gnc_gen_trans_list_add_trans (info->gnc_csv_importer_gui, trans_line->trans);
-                transactions = g_list_next (transactions);
             }
-            g_list_free (transactions);
         }
     }
     /* Enable the Forward Assistant Button */
@@ -2714,7 +2710,7 @@ csv_import_trans_assistant_finish (GtkAssistant *assistant, gpointer user_data)
     CsvImportTrans *info = (CsvImportTrans*) user_data;
 
     /* Start the import */
-    if (info->parse_data->transactions != NULL)
+    if (!info->parse_data->transactions.empty())
         gnc_gen_trans_assist_start (info->gnc_csv_importer_gui);
     else
         gnc_gen_trans_list_delete (info->gnc_csv_importer_gui);
diff --git a/src/import-export/csv-imp/gnc-csv-imp-trans.cpp b/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
index 610b0cd..469da05 100644
--- a/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
+++ b/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
@@ -226,7 +226,6 @@ GncCsvParseData::GncCsvParseData(GncImpFileFormat format)
     /* All of the data pointers are initially NULL. This is so that, if
      * gnc_csv_parse_data_free is called before all of the data is
      * initialized, only the data that needs to be freed is freed. */
-    transactions = NULL;
     date_format = -1;
     currency_format = 0;
     start_row = 0;
@@ -242,10 +241,6 @@ GncCsvParseData::GncCsvParseData(GncImpFileFormat format)
  */
 GncCsvParseData::~GncCsvParseData()
 {
-    /* All non-NULL pointers have been initialized and must be freed. */
-
-    if (transactions != NULL)
-        g_list_free_full (transactions, g_free);
 }
 
 int GncCsvParseData::file_format(GncImpFileFormat format,
@@ -762,30 +757,11 @@ int GncCsvParseData::parse_to_trans (Account* account,
         for (auto orig_line : orig_lines)
             orig_line.second.clear();
 
-        if (transactions != NULL)
-            g_list_free (transactions);
-    }
-
-    /* last_transaction points to the last element in
-     * transactions, or NULL if it's empty. */
-    GList* last_transaction = NULL;
-    if (redo_errors) /* If we're looking only at error data ... */
-    {
-        if (transactions == NULL)
-            last_transaction = NULL;
-        else
-        {
-            /* Move last_transaction to the end. */
-            last_transaction = transactions;
-            while (g_list_next (last_transaction) != NULL)
-            {
-                last_transaction = g_list_next (last_transaction);
-            }
-        }
-    }
-    else /* Otherwise, we look at all the data. */
-    {
-        last_transaction = NULL;
+        /* FIXME handle memory leak here!
+         * Existing transactions in the map should probably removed before emptying the map
+         */
+        if (!transactions.empty())
+            transactions.clear();
     }
 
     /* compute start and end iterators based on user-set restrictions */
@@ -911,48 +887,17 @@ int GncCsvParseData::parse_to_trans (Account* account,
         }
 
         /* If all went well, add this transaction to the list. */
-        /* We keep the transactions sorted by date. We start at the end
-         * of the list and go backward, simply because the file itself
-         * is probably also sorted by date (but we need to handle the
-         * exception anyway). */
-
-        /* If we can just put it at the end, do so and increment last_transaction. */
-        if (last_transaction == NULL ||
-                xaccTransGetDate (((GncCsvTransLine*)(last_transaction->data))->trans) <= xaccTransGetDate (trans_line->trans))
-        {
-            transactions = g_list_append (transactions, trans_line);
-            /* If this is the first transaction, we need to get last_transaction on track. */
-            if (last_transaction == NULL)
-                last_transaction = transactions;
-            else /* Otherwise, we can just continue. */
-                last_transaction = g_list_next (last_transaction);
-        }
-        /* Otherwise, search backward for the correct spot. */
-        else
-        {
-            GList* insertion_spot = last_transaction;
-            while (insertion_spot != NULL &&
-                    xaccTransGetDate (((GncCsvTransLine*)(insertion_spot->data))->trans) > xaccTransGetDate (trans_line->trans))
-            {
-                insertion_spot = g_list_previous (insertion_spot);
-            }
-            /* Move insertion_spot one location forward since we have to
-             * use the g_list_insert_before function. */
-            if (insertion_spot == NULL) /* We need to handle the case of inserting at the beginning of the list. */
-                insertion_spot = transactions;
-            else
-                insertion_spot = g_list_next (insertion_spot);
-
-            transactions = g_list_insert_before (transactions, insertion_spot, trans_line);
-        }
+        /* We want to keep the transactions sorted by date in case we have
+         * to calculate the transaction's amount based on the user provided balances.
+         * The multimap should deal with this for us. */
+        auto trans_date = xaccTransGetDate (trans_line->trans);
+        transactions.insert (std::pair<time64, GncCsvTransLine*>(trans_date,trans_line));
     }
 
     if (std::find(column_types.begin(),column_types.end(), GncTransPropType::BALANCE) !=
         column_types.end()) // This is only used if we have one home account
     {
         Split      *split, *osplit;
-        gnc_numeric balance_offset;
-        GList* tx_iter = transactions;
 
         if (account != NULL)
             home_account = account;
@@ -961,11 +906,11 @@ int GncCsvParseData::parse_to_trans (Account* account,
          * differs from what it will be after the transactions are
          * imported. This will be sum of all the previous transactions for
          * any given transaction. */
-        balance_offset = double_to_gnc_numeric (0.0, xaccAccountGetCommoditySCU (home_account),
+        auto balance_offset = double_to_gnc_numeric (0.0, xaccAccountGetCommoditySCU (home_account),
                                      GNC_HOW_RND_ROUND_HALF_UP);
-        while (tx_iter != NULL)
+        for (auto trans_iter : transactions)
         {
-            auto trans_line = static_cast<GncCsvTransLine*> (tx_iter->data);
+            auto trans_line = trans_iter.second;
             if (trans_line->balance_set)
             {
                 time64 date = xaccTransGetDate (trans_line->trans);
@@ -1000,7 +945,6 @@ int GncCsvParseData::parse_to_trans (Account* account,
                                                  xaccAccountGetCommoditySCU (home_account),
                                                  GNC_HOW_RND_ROUND_HALF_UP);
             }
-            tx_iter = g_list_next (tx_iter);
         }
     }
 
diff --git a/src/import-export/csv-imp/gnc-csv-imp-trans.hpp b/src/import-export/csv-imp/gnc-csv-imp-trans.hpp
index c8a4c87..6aee870 100644
--- a/src/import-export/csv-imp/gnc-csv-imp-trans.hpp
+++ b/src/import-export/csv-imp/gnc-csv-imp-trans.hpp
@@ -131,7 +131,7 @@ public:
     std::unique_ptr<GncTokenizer> tokenizer;    /**< Will handle file loading/encoding conversion/splitting into fields */
     std::vector<parse_line_t> orig_lines;      /**< file_str parsed into a two-dimensional array of strings */
     std::vector<GncTransPropType> column_types;       /**< Vector of values from the GncCsvColumnType enumeration */
-    GList* transactions;        /**< List of GncCsvTransLine*s created using orig_lines and column_types */
+    std::multimap <time64, GncCsvTransLine*> transactions;        /**< List of GncCsvTransLine*s created using orig_lines and column_types */
     int date_format;            /**< The format of the text in the date columns from date_format_internal. */
     guint start_row;            /**< The start row to generate transactions from. */
     guint end_row;              /**< The end row to generate transactions from. */

commit d557c01c88cc4cd293fd4a238e257babd6c88cde
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sat Sep 10 17:35:12 2016 +0200

    Another round of small c to c++(11) conversions
    
    - prefer c++ data types (auto, bool, int, std::string)
    - iterate over vectors instead of accessing via the [] operator
    - use boost::{u32}regex instead of directly manipulating a c-string

diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index 33bb231..b2d186a 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -77,9 +77,9 @@ typedef struct
 
     GtkWidget       *file_page;                     /**< Assistant file page widget */
     GtkWidget       *file_chooser;                  /**< The widget for the file chooser */
-    gchar           *starting_dir;                  /**< The starting directory for import file */
-    gchar           *file_name;                     /**< The import file name */
-    gchar           *error_text;                    /**< Error Text */
+    std::string      starting_dir;                  /**< The starting directory for import file */
+    std::string      file_name;                     /**< The import file name */
+    std::string      error_text;                    /**< Error Text */
 
     GtkWidget       *preview_page;                  /**< Assistant preview page widget */
     GtkWidget       *settings_combo;                /**< The Settings Combo */
@@ -107,16 +107,16 @@ typedef struct
     GtkTreeView     *ctreeview;                     /**< The treeview containing the column types */
     GtkLabel        *instructions_label;            /**< The instructions label */
     GtkImage        *instructions_image;            /**< The instructions image */
-    gboolean         encoding_selected_called;      /**< Before encoding_selected is first called, this is FALSE.
+    bool             encoding_selected_called;      /**< Before encoding_selected is first called, this is false.
                                                        * (See description of encoding_selected.) */
-    gboolean         not_empty;                     /**< FALSE initially, true after the first type gnc_csv_preview_update_assist is called. */
-    gboolean         previewing_errors;             /**< TRUE if the dialog is displaying
+    bool             not_empty;                     /**< false initially, true after the first type gnc_csv_preview_update_assist is called. */
+    bool             previewing_errors;             /**< true if the dialog is displaying
                                                        * error lines, instead of all the file data. */
     int              code_encoding_calls;           /**< Normally this is 0. If the computer
                                                        * changes encselector, this is set to
                                                        * 2. encoding_selected is called twice,
                                                        * each time decrementing this by 1. */
-    gboolean         skip_errors;                   /**< This is FALSE until the user checks the skip errors. */
+    bool             skip_errors;                   /**< This is false until the user checks the skip errors. */
     GtkWidget      **treeview_buttons;              /**< This array contains the header buttons in treeview */
     int              num_of_rows;                   /**< The number of rows in the store */
     int              longest_line;                  /**< The length of the longest row */
@@ -141,15 +141,15 @@ typedef struct
     GtkWidget            *match_label;              /**< The match label at the bottom of the page */
     GtkWidget            *help_button;              /**< The widget for the help button on the matcher page */
     GtkWidget            *cancel_button;            /**< The widget for the new cancel button when going back is blocked */
-    gboolean              match_parse_run;          /**< This is set after the first run */
+    bool                  match_parse_run;          /**< This is set after the first run */
 
     GtkWidget            *summary_page;             /**< Assistant summary page widget */
     GtkWidget            *summary_label;            /**< The summary text */
 
-    gboolean              new_book;                 /**< Are we importing into a new book?; if yes, call book options */
-    gint                  callcount;                /**< Number of times the assistant page forward function called */
-    gint                  next_page;                /**< The saved assistant next page number */
-    gboolean              settings_valid;           /**< Are the settings valid */
+    bool                  new_book;                 /**< Are we importing into a new book?; if yes, call book options */
+    int                   callcount;                /**< Number of times the assistant page forward function called */
+    int                   next_page;                /**< The saved assistant next page number */
+    bool                  settings_valid;           /**< Are the settings valid */
 
 } CsvImportTrans;
 
@@ -187,9 +187,9 @@ void csv_import_trans_assistant_summary_page_prepare (GtkAssistant *assistant, g
 void csv_import_trans_load_settings (CsvImportTrans *info);
 
 static void gnc_csv_preview_update_assist (CsvImportTrans* info);
-void gnc_csv_reset_preview_setting (CsvImportTrans* info, gboolean block);
-gboolean preview_settings_valid (CsvImportTrans *info);
-gboolean get_list_of_accounts (CsvImportTrans* info, GtkTreeModel *store);
+void gnc_csv_reset_preview_setting (CsvImportTrans* info, bool block);
+bool preview_settings_valid (CsvImportTrans *info);
+bool get_list_of_accounts (CsvImportTrans* info, GtkTreeModel *store);
 
 /*************************************************************************/
 
@@ -290,7 +290,7 @@ csv_import_trans_load_settings (CsvImportTrans *info)
                 fwtok->cols_from_string (std::string(info->settings_data->column_widths));
 
             GError  *error = NULL;
-            if (info->parse_data->parse (FALSE, &error))
+            if (info->parse_data->parse (false, &error))
             {
                 gnc_error_dialog (NULL, "%s", _("There was a problem with the column widths, please review."));
                 g_error_free (error);
@@ -308,7 +308,7 @@ csv_import_trans_load_settings (CsvImportTrans *info)
             GtkTreeIter   iter;
             gchar       **columns;
             int           i;
-            gboolean      error = FALSE;
+            bool          error = false;
 
             columns = g_strsplit (info->settings_data->column_types, ",", -1);
 
@@ -342,7 +342,7 @@ csv_import_trans_load_settings (CsvImportTrans *info)
                                 -1);
                 }
                 else
-                    error = TRUE;
+                    error = true;
             }
             if (error)
                 gnc_error_dialog (NULL, "%s", _("There was a problem with the column types, please review."));
@@ -409,7 +409,7 @@ csv_import_trans_delete_settings_cb (GtkWidget *button, CsvImportTrans *info)
                 g_key_file_remove_group (keyfile, group, NULL);
                 gnc_csv_trans_find_settings (model);
                 gtk_combo_box_set_active (GTK_COMBO_BOX(info->settings_combo), 0); // Default
-                gnc_csv_reset_preview_setting (info, FALSE); // Reset the widgets
+                gnc_csv_reset_preview_setting (info, false); // Reset the widgets
             }
         }
         g_free (group);
@@ -443,7 +443,7 @@ csv_import_trans_save_settings_cb (GtkWidget *button, CsvImportTrans *info)
     GtkWidget     *entry;
     gchar         *group = NULL, *name = NULL;
     const gchar   *title = _("Save the Import Settings.");
-    gboolean       error = FALSE;
+    bool           error = false;
     const gchar   *entry_text;
 
     // Get the Entry Text
@@ -468,7 +468,7 @@ csv_import_trans_save_settings_cb (GtkWidget *button, CsvImportTrans *info)
                 "%s", _("You can not save to 'No Settings'."));
             gtk_dialog_run (GTK_DIALOG (dialog));
             gtk_widget_destroy (dialog);
-            error = TRUE;
+            error = true;
         }
         g_free (group);
         g_free (name);
@@ -486,15 +486,15 @@ csv_import_trans_save_settings_cb (GtkWidget *button, CsvImportTrans *info)
                 "%s", _("The settings name is blank."));
             gtk_dialog_run (GTK_DIALOG (dialog));
             gtk_widget_destroy (dialog);
-            error = TRUE;
+            error = true;
         }
         else // Check for entry_text in settings list
         {
             GtkTreeModel *model;
             GtkTreeIter   iter;
-            gboolean      valid = FALSE;
-            gboolean      found = FALSE;
-            gint          response;
+            bool          valid = false;
+            bool          found = false;
+            int           response;
 
             model = gtk_combo_box_get_model (GTK_COMBO_BOX(info->settings_combo));
 
@@ -508,7 +508,7 @@ csv_import_trans_save_settings_cb (GtkWidget *button, CsvImportTrans *info)
                 gtk_tree_model_get (model, &iter, SET_NAME, &name, -1);
 
                 if (g_strcmp0 (name, entry_text) == 0)
-                    found = TRUE;
+                    found = true;
 
                 g_free (name);
 
@@ -528,13 +528,13 @@ csv_import_trans_save_settings_cb (GtkWidget *button, CsvImportTrans *info)
                 gtk_widget_destroy (dialog);
 
                 if (response != GTK_RESPONSE_OK)
-                    error = TRUE;
+                    error = true;
             }
         }
     }
 
     // Call save settings if we have no errors
-    if (error == FALSE)
+    if (!error)
     {
         int           i;
         GList        *columns;
@@ -613,7 +613,7 @@ csv_import_trans_save_settings_cb (GtkWidget *button, CsvImportTrans *info)
         {
             GtkTreeModel *model;
             GtkTreeIter   iter;
-            gboolean      valid = FALSE;
+            bool          valid = false;
 
             dialog = gtk_message_dialog_new (GTK_WINDOW(info->window),
                                         (GtkDialogFlags) 0,
@@ -677,28 +677,28 @@ csv_import_trans_file_chooser_confirm_cb (GtkWidget *button, CsvImportTrans *inf
 
     gtk_assistant_set_page_complete (assistant, page, FALSE);
 
-    gchar *file_name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(info->file_chooser));
+    auto file_name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(info->file_chooser));
     if (!file_name)
         return;
 
     gchar *filepath = gnc_uri_get_path (file_name);
-    if (info->starting_dir)
-        g_free (info->starting_dir);
-    info->starting_dir = g_path_get_dirname (filepath);
-    g_free (filepath);
+    auto starting_dir = g_path_get_dirname (filepath);
 
-    if (info->file_name)
-        g_free (info->file_name);
     info->file_name = file_name;
+    info->starting_dir = starting_dir;
+
+    g_free (filepath);
+    g_free (file_name);
+    g_free (starting_dir);
 
-    DEBUG("file_name selected is %s", info->file_name);
-    DEBUG("starting directory is %s", info->starting_dir);
+    DEBUG("file_name selected is %s", info->file_name.c_str());
+    DEBUG("starting directory is %s", info->starting_dir.c_str());
 
     /* Load the file into parse_data. */
     auto parse_data = new GncCsvParseData;
     /* Assume data is CSV. User can later override to Fixed Width if needed */
     parse_data->file_format (GncImpFileFormat::CSV, &error);
-    if (parse_data->load_file (file_name, &error))
+    if (parse_data->load_file (info->file_name, &error))
     {
         /* If we couldn't load the file ... */
         gnc_error_dialog (NULL, "%s", error->message);
@@ -713,7 +713,7 @@ csv_import_trans_file_chooser_confirm_cb (GtkWidget *button, CsvImportTrans *inf
     }
 
     /* Parse the data. */
-    if (parse_data->parse (TRUE, &error))
+    if (parse_data->parse (true, &error))
     {
         /* If we couldn't parse the data ... */
         gnc_error_dialog (NULL, "%s", error->message);
@@ -724,11 +724,11 @@ csv_import_trans_file_chooser_confirm_cb (GtkWidget *button, CsvImportTrans *inf
     if (info->parse_data) // Free parse_data if we have come back here
     {
         delete info->parse_data;
-        gnc_csv_reset_preview_setting (info, TRUE);
+        gnc_csv_reset_preview_setting (info, true);
     }
     info->parse_data = parse_data;
-    info->previewing_errors = FALSE; /* We're looking at all the data. */
-    info->skip_errors = FALSE; // Set skip_errors to False
+    info->previewing_errors = false; /* We're looking at all the data. */
+    info->skip_errors = false; // Set skip_errors to False
     gtk_assistant_set_page_complete (assistant, page, TRUE);
     gtk_assistant_set_current_page (assistant, num + 1);
 }
@@ -744,7 +744,7 @@ void row_selection_update (CsvImportTrans* info)
 {
     GtkListStore *store;
     GtkTreeIter iter;
-    gboolean valid;
+    bool valid;
     int i = 0;
 
     store = GTK_LIST_STORE(gtk_tree_view_get_model (info->treeview));
@@ -800,7 +800,7 @@ void row_selection_update (CsvImportTrans* info)
     }
 
     /* Skip rows */
-    if (info->parse_data->skip_rows == TRUE)
+    if (info->parse_data->skip_rows)
     {
         for (i = info->start_row + 1; i <= info->end_row; i = i + 2)
         {
@@ -869,9 +869,9 @@ void csv_import_trans_skip_errors_cb (GtkWidget *cb, gpointer user_data)
     CsvImportTrans *info = (CsvImportTrans*) user_data;
 
     if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(cb)))
-        info->skip_errors = TRUE;
+        info->skip_errors = true;
     else
-        info->skip_errors = FALSE;
+        info->skip_errors = false;
 }
 
 
@@ -949,7 +949,7 @@ void sep_button_clicked (GtkWidget* widget, CsvImportTrans* info)
      * the column types because we want to leave the user's
      * configurations intact. */
     GError* error;
-    if (info->parse_data->parse (FALSE, &error))
+    if (info->parse_data->parse (false, &error))
     {
         /* Warn the user there was a problem and try to undo what caused
          * the error. (This will cause a reparsing and ideally a usable
@@ -998,7 +998,7 @@ static void separated_or_fixed_selected (GtkToggleButton* csv_button, CsvImportT
 
     /* Reparse the data. */
     GError* error = NULL;
-    if (info->parse_data->parse (FALSE, &error))
+    if (info->parse_data->parse (false, &error))
     {
         /* Show an error dialog explaining the problem. */
         gnc_error_dialog (NULL, "%s", error->message);
@@ -1041,11 +1041,11 @@ static void encoding_selected (GOCharmapSel* selector, const char* encoding,
         GError* error = NULL;
         /* Try converting the new encoding and reparsing. */
         info->parse_data->convert_encoding (encoding);
-        if (info->parse_data->parse (FALSE, &error))
+        if (info->parse_data->parse (false, &error))
         {
             /* If it fails, change back to the old encoding. */
             gnc_error_dialog (NULL, "%s", _("Invalid encoding selected"));
-            info->encoding_selected_called = FALSE;
+            info->encoding_selected_called = false;
             go_charmap_sel_set_encoding (selector, previous_encoding.c_str());
             return;
         }
@@ -1055,11 +1055,11 @@ static void encoding_selected (GOCharmapSel* selector, const char* encoding,
         /* Refresh the row highlighting */
         row_selection_update (info);
 
-        info->encoding_selected_called = FALSE;
+        info->encoding_selected_called = false;
     }
     else /* If this is the first call of the function ... */
     {
-        info->encoding_selected_called = TRUE; /* ... set the flag and wait for the next call. */
+        info->encoding_selected_called = true; /* ... set the flag and wait for the next call. */
     }
 }
 
@@ -1197,7 +1197,7 @@ fixed_context_menu_handler (GnumericPopupMenuElement const *element,
     }
 
     GError* error = NULL;
-    if (info->parse_data->parse (FALSE, &error))
+    if (info->parse_data->parse (false, &error))
     {
         gnc_error_dialog (NULL, "%s", error->message);
         return FALSE;
@@ -1380,7 +1380,7 @@ split_column (CsvImportTrans* info, int col, int dx)
     GncFwTokenizer *fwtok = dynamic_cast<GncFwTokenizer*>(info->parse_data->tokenizer.get());
     fwtok->col_split (col, rel_pos);
     GError* error = NULL;
-    if (info->parse_data->parse (FALSE, &error))
+    if (info->parse_data->parse (false, &error))
     {
         gnc_error_dialog (NULL, "%s", error->message);
         return;
@@ -1441,17 +1441,17 @@ static void header_button_press_handler (GtkWidget* button, GdkEventButton* even
 
 /* Test for the required minimum number of columns selected and
  * a valid date format.
- * Returns TRUE if we do or FALSE if we don't.
+ * Returns true if we do or false if we don't.
  *
  * @param info The data being previewed
  */
-gboolean preview_settings_valid (CsvImportTrans* info)
+bool preview_settings_valid (CsvImportTrans* info)
 {
     int i, ncols = info->parse_data->column_types.size(); /* ncols is the number of columns in the data. */
     int weight = 0;
     int oweight = 0;
-    gboolean valid = TRUE;
-    gboolean havebalance = FALSE;
+    bool valid = true;
+    bool havebalance = false;
     /* store contains the actual strings appearing in the column types treeview. */
     GtkTreeModel* store = gtk_tree_view_get_model (info->ctreeview);
     /* datastore contains the actual strings appearing in the preview treeview. */
@@ -1487,7 +1487,7 @@ gboolean preview_settings_valid (CsvImportTrans* info)
             gtk_tree_model_get (datastore, &iter2, i + 1, &prevstr, -1);
 
             if (parse_date (prevstr, info->parse_data->date_format) == -1)
-                valid = FALSE;
+                valid = false;
             break;
 
         case GncTransPropType::DESCRIPTION:
@@ -1495,7 +1495,7 @@ gboolean preview_settings_valid (CsvImportTrans* info)
             break;
 
         case GncTransPropType::BALANCE:
-            havebalance = TRUE;
+            havebalance = true;
             /* No break */
         case GncTransPropType::DEPOSIT:
         case GncTransPropType::WITHDRAWAL:
@@ -1527,46 +1527,43 @@ gboolean preview_settings_valid (CsvImportTrans* info)
         g_free (prevstr);
     }
 
-    if ((havebalance == TRUE) && (info->home_account_number > 1))
+    if (havebalance && (info->home_account_number > 1))
     {
-        g_free (info->error_text);
-        info->error_text = g_strdup_printf (gettext ("There are problems with the import settings!\nIf you have a Balance column "
-                                        "and an Account column there must be only one account listed..."));
-        return FALSE;
+        info->error_text = _("There are problems with the import settings!\nIf you have a Balance column "
+                             "and an Account column there must be only one account listed...");
+        return false;
     }
 
     if ((oweight > 0) && (oweight < 99))
     {
-        g_free (info->error_text);
-        info->error_text = g_strdup_printf (gettext ("There are problems with the import settings!\nIf you have an Other Memo column "
-                                        "you must have an Other Account column..."));
-        return FALSE;
+        info->error_text = _("There are problems with the import settings!\nIf you have an Other Memo column "
+                             "you must have an Other Account column...");
+        return false;
     }
 
-    if (weight < 1109 || valid == FALSE)
+    if (weight < 1109 || !valid)
     {
-        g_free (info->error_text);
-        info->error_text = g_strdup_printf (gettext ("There are problems with the import settings!\nThe date format could be wrong "
-                                        "or there are not enough columns set..."));
-        return FALSE;
+        info->error_text = _("There are problems with the import settings!\nThe date format could be wrong "
+                             "or there are not enough columns set...");
+        return false;
     }
     else
-        return TRUE;
+        return true;
 }
 
 
 /* Test for the string being in the liststore
- * Returns TRUE if it is or FALSE if not.
+ * Returns true if it is or false if not.
  *
  * @param liststore The data being reviewed
  *
  * @param string to check for
  */
-static gboolean
+static bool
 check_for_duplicates (GtkListStore *liststore, const gchar *string)
 {
     GtkTreeIter iter;
-    gboolean valid;
+    bool valid;
 
     valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL(liststore), &iter);
     while (valid)
@@ -1578,27 +1575,27 @@ check_for_duplicates (GtkListStore *liststore, const gchar *string)
         if(!(g_strcmp0 (text, string)))
         {
             g_free (text);
-            return TRUE;
+            return true;
         }
         g_free (text);
 
         valid = gtk_tree_model_iter_next (GTK_TREE_MODEL(liststore), &iter);
     }
-    return FALSE;
+    return false;
 }
 
 
 /* Get the list of accounts
- * Returns TRUE if we have any accounts
+ * Returns true if we have any accounts
  *
  * @param info The data being previewed
  *
  * @param store for the account match page
  */
-gboolean get_list_of_accounts (CsvImportTrans* info, GtkTreeModel *store)
+bool get_list_of_accounts (CsvImportTrans* info, GtkTreeModel *store)
 {
     int      i, j, ncols = info->parse_data->column_types.size(); /* ncols is the number of columns in the data. */
-    gboolean have_accounts = FALSE;
+    bool     have_accounts = false;
     gint     home_account_number = 0;
     gint     other_account_number = 0;
 
@@ -1648,7 +1645,7 @@ gboolean get_list_of_accounts (CsvImportTrans* info, GtkTreeModel *store)
                         gtk_list_store_append (GTK_LIST_STORE(store), &iter3);
                         gtk_list_store_set (GTK_LIST_STORE(store), &iter3, MAPPING_STRING, accstr,
                                             MAPPING_FULLPATH, _("No Linked Account"), MAPPING_ACCOUNT, NULL, -1);
-                        have_accounts = TRUE;
+                        have_accounts = true;
                     }
                     g_free (accstr);
                 }
@@ -1661,8 +1658,8 @@ gboolean get_list_of_accounts (CsvImportTrans* info, GtkTreeModel *store)
 }
 
 
-/* Loads the preview's data into its data treeview. not_empty is TRUE
- * when the data treeview already contains data, FALSE otherwise
+/* Loads the preview's data into its data treeview. not_empty is true
+ * when the data treeview already contains data, false otherwise
  * (e.g. the first time this function is called on a preview).
  *
  * @param info The data being previewed
@@ -1845,7 +1842,7 @@ static void gnc_csv_preview_update_assist (CsvImportTrans* info)
     info->parse_data->date_format = gtk_combo_box_get_active (GTK_COMBO_BOX(info->date_format_combo));
 
     /* It's now been filled with some stuff. */
-    info->not_empty = TRUE;
+    info->not_empty = true;
 }
 
 
@@ -1854,7 +1851,7 @@ static void gnc_csv_preview_update_assist (CsvImportTrans* info)
  *
  * Reset the widgets on the preview settings page
  *******************************************************/
-void gnc_csv_reset_preview_setting (CsvImportTrans *info, gboolean block)
+void gnc_csv_reset_preview_setting (CsvImportTrans *info, bool block)
 {
     int i;
     GtkAdjustment  *adj;
@@ -1917,10 +1914,10 @@ void gnc_csv_reset_preview_setting (CsvImportTrans *info, gboolean block)
 
 // FIXME Should I explicitly do a reparse here, or did the above code already trigger it in some way ?
 //    GError* error = NULL;
-//    if (gnc_csv_parse (info->parse_data, FALSE, &error))
+//    if (gnc_csv_parse (info->parse_data, false, &error))
 //    {
 //        gnc_error_dialog (NULL, "%s", error->message);
-//        return FALSE;
+//        return false;
 //    }
 //    gnc_csv_preview_update_assist (info);
 //
@@ -1939,10 +1936,11 @@ static
 void load_settings (CsvImportTrans *info)
 {
     info->start_row = 0;
-    info->match_parse_run = FALSE;
-    info->file_name = NULL;
-    info->starting_dir = NULL;
-    info->error_text = NULL;
+    info->match_parse_run = false;
+    if (!info->file_name.empty())
+        info->file_name.clear();
+    if (!info->error_text.empty())
+        info->error_text.clear();
 
     /* Init Settings data. */
     info->settings_data = gnc_csv_trans_new_settings_data();
@@ -1986,12 +1984,12 @@ csv_import_trans_assistant_file_page_prepare (GtkAssistant *assistant,
     gint            num = gtk_assistant_get_current_page (assistant);
     GtkWidget      *page = gtk_assistant_get_nth_page (assistant, num);
 
-    info->previewing_errors = FALSE; // We're looking at all the data.
-    info->skip_errors = FALSE; // Set skip_errors to False to start with.
+    info->previewing_errors = false; // We're looking at all the data.
+    info->skip_errors = false; // Set skip_errors to False to start with.
 
     /* Set the default directory */
-    if (info->starting_dir)
-        gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER(info->file_chooser), info->starting_dir);
+    if (info->starting_dir.size())
+        gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER(info->file_chooser), info->starting_dir.c_str());
 
     /* Reset start row to first row 1 */
     adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(info->start_row_spin));
@@ -2025,7 +2023,7 @@ csv_import_trans_assistant_preview_page_prepare (GtkAssistant *assistant,
     gtk_widget_hide (GTK_WIDGET(info->check_label));
     gtk_widget_hide (GTK_WIDGET(info->check_butt));
 
-    if (info->previewing_errors == TRUE) // We are looking at errors to display
+    if (info->previewing_errors) // We are looking at errors to display
     {
         gchar* name;
         GtkIconSize size;
@@ -2093,9 +2091,9 @@ csv_import_trans_assistant_account_page_prepare (GtkAssistant *assistant,
 
     info->settings_valid = preview_settings_valid (info);
 
-    if (!info->settings_valid && (info->skip_errors == FALSE))
+    if (!info->settings_valid && !info->skip_errors)
     {
-        mtext = g_strdup_printf ("<span size=\"medium\" color=\"red\"><b>%s</b></span>", info->error_text);
+        mtext = g_strdup_printf ("<span size=\"medium\" color=\"red\"><b>%s</b></span>", info->error_text.c_str());
         gtk_label_set_markup (GTK_LABEL(info->account_label), mtext);
         g_free (mtext);
 
@@ -2133,11 +2131,11 @@ csv_import_trans_assistant_account_page_prepare (GtkAssistant *assistant,
 }
 
 
-static gboolean
+static bool
 import_account_check_all (GtkTreeModel *model)
 {
     GtkTreeIter iter;
-    gboolean valid, ret = TRUE;
+    bool valid, ret = true;
 
     // Set iter to first entry of store
     valid = gtk_tree_model_get_iter_first (model, &iter);
@@ -2151,7 +2149,7 @@ import_account_check_all (GtkTreeModel *model)
         gtk_tree_model_get (model, &iter, MAPPING_ACCOUNT, &account, -1);
 
         if (account == NULL)
-            ret = FALSE;
+            ret = false;
 
         valid = gtk_tree_model_iter_next (model, &iter);
     }
@@ -2294,7 +2292,7 @@ import_account_select_cb (GtkWidget *widget, gpointer user_data)
 
 
 /* This is the callback for the mouse click */
-static gboolean
+static bool
 import_account_button_cb (GtkWidget *widget, GdkEventButton *event, gpointer user_data)
 {
     CsvImportTrans     *info = (CsvImportTrans*) user_data;
@@ -2311,7 +2309,7 @@ import_account_button_cb (GtkWidget *widget, GdkEventButton *event, gpointer use
         GdkWindow *window = gtk_tree_view_get_bin_window (GTK_TREE_VIEW (info->account_match_view));
 
         if (event->window != window)
-            return FALSE;
+            return false;
 
         /* Get tree path for row that was clicked, true if row exists */
         if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (info->account_match_view), (gint) event->x, (gint) event->y,
@@ -2357,9 +2355,9 @@ import_account_button_cb (GtkWidget *widget, GdkEventButton *event, gpointer use
         if (import_account_check_all (model))
             gtk_assistant_set_page_complete (GTK_ASSISTANT(info->window), info->account_match_page, TRUE);
 
-        return TRUE;
+        return true;
     }
-    return FALSE;
+    return false;
 }
 
 
@@ -2373,9 +2371,9 @@ csv_import_trans_assistant_account_match_page_prepare (GtkAssistant *assistant,
 
     info->settings_valid = preview_settings_valid (info);
 
-    if (!info->settings_valid && (info->skip_errors == FALSE))
+    if (!info->settings_valid && !info->skip_errors)
     {
-        mtext = g_strdup_printf ("<span size=\"medium\" color=\"red\"><b>%s</b></span>", info->error_text);
+        mtext = g_strdup_printf ("<span size=\"medium\" color=\"red\"><b>%s</b></span>", info->error_text.c_str());
         gtk_label_set_markup (GTK_LABEL(info->account_match_label), mtext);
         g_free (mtext);
 
@@ -2425,7 +2423,7 @@ csv_import_trans_assistant_doc_page_prepare (GtkAssistant *assistant,
     if (info->new_book)
         info->new_book = gnc_new_book_option_display (info->window);
 
-    if (info->match_parse_run == FALSE)
+    if (!info->match_parse_run)
     {
         /* Add the Cancel button for the matcher */
         info->cancel_button = gtk_button_new_with_mnemonic (_("_Cancel"));
@@ -2434,7 +2432,7 @@ csv_import_trans_assistant_doc_page_prepare (GtkAssistant *assistant,
                          G_CALLBACK(csv_import_trans_assistant_cancel), info);
         gtk_widget_show (GTK_WIDGET(info->cancel_button));
     }
-    info->match_parse_run = TRUE;
+    info->match_parse_run = true;
 }
 
 
@@ -2450,10 +2448,8 @@ csv_import_trans_assistant_match_page_prepare (GtkAssistant *assistant,
     /* Block going back */
     gtk_assistant_commit (GTK_ASSISTANT(info->window));
 
-    if (!info->parse_data->parse_errors || (info->skip_errors == TRUE))
+    if (!info->parse_data->parse_errors || info->skip_errors)
     {
-        GList* transactions; /* A list of the transactions we create */
-
         text =  _("Double click on rows to change, then click on Apply to Import");
         mtext = g_strdup_printf ("<span size=\"medium\" color=\"red\"><b>%s</b></span>", text);
         gtk_label_set_markup (GTK_LABEL(info->match_label), mtext);
@@ -2472,7 +2468,7 @@ csv_import_trans_assistant_match_page_prepare (GtkAssistant *assistant,
             gtk_widget_show (GTK_WIDGET(info->help_button));
 
             /* Get the list of the transactions that were created. */
-            transactions = info->parse_data->transactions;
+            GList *transactions = info->parse_data->transactions;
             /* Copy all of the transactions to the importer GUI. */
             while (transactions != NULL)
             {
@@ -2496,13 +2492,13 @@ csv_import_trans_assistant_summary_page_prepare (GtkAssistant *assistant,
     gchar *text, *mtext;
 
     /* Save the Window size and directory */
-    gnc_set_default_directory (GNC_PREFS_GROUP, info->starting_dir);
+    gnc_set_default_directory (GNC_PREFS_GROUP, info->starting_dir.c_str());
 
     /* Remove the added button */
     gtk_assistant_remove_action_widget (assistant, info->help_button);
     gtk_assistant_remove_action_widget (assistant, info->cancel_button);
 
-    text = g_strdup_printf (gettext ("The transactions were imported from the file '%s'."), info->file_name);
+    text = g_strdup_printf (gettext ("The transactions were imported from the file '%s'."), info->file_name.c_str());
     mtext = g_strdup_printf ("<span size=\"medium\"><b>%s</b></span>", text);
     gtk_label_set_markup (GTK_LABEL(info->summary_label), mtext);
     g_free (text);
@@ -2548,7 +2544,7 @@ csv_import_trans_forward_page_func (gint current_page, gpointer user_data)
             if (info->callcount == 1)
             {
                 GtkTreeModel *store;
-                gboolean      valid;
+                bool          valid;
 
                 // Load the account strings into the store
                 store = gtk_tree_view_get_model (GTK_TREE_VIEW(info->account_match_view));
@@ -2563,7 +2559,7 @@ csv_import_trans_forward_page_func (gint current_page, gpointer user_data)
                     next_page = 3;
 
                 // Skip Errors set, goto to doc page
-                if (info->skip_errors == TRUE)
+                if (info->skip_errors)
                     next_page = 5;
 
                 info->next_page = next_page;
@@ -2585,7 +2581,7 @@ csv_import_trans_forward_page_func (gint current_page, gpointer user_data)
                     next_page = 5;
 
                 // Skip Errors set, goto to doc page
-                if (info->skip_errors == TRUE)
+                if (info->skip_errors)
                     next_page = 5;
 
                 info->next_page = next_page;
@@ -2607,14 +2603,14 @@ csv_import_trans_forward_page_func (gint current_page, gpointer user_data)
         case 5: //from doc page
             if (info->callcount == 1)
             {
-                /* Create transactions from the parsed data, first time with FALSE
-                   Subsequent times with TRUE */
+                /* Create transactions from the parsed data, first time with false
+                   Subsequent times with true */
                     info->parse_data->parse_to_trans (info->account, info->match_parse_run);
 
                 /* if there are errors, we jump back to preview to correct */
-                if (info->parse_data->parse_errors && (info->skip_errors == FALSE) )
+                if (info->parse_data->parse_errors && !info->skip_errors)
                 {
-                    info->previewing_errors = TRUE; /* We're looking at errors. */
+                    info->previewing_errors = true; /* We're looking at errors. */
                     next_page = 2;
                 }
                 else
@@ -2729,10 +2725,6 @@ csv_import_trans_close_handler (gpointer user_data)
 {
     CsvImportTrans *info = (CsvImportTrans*) user_data;
 
-    g_free(info->file_name);
-    g_free(info->starting_dir);
-    g_free(info->error_text);
-
     /* Free the memory we allocated. */
     if (!(info->parse_data == NULL))
         delete info->parse_data;
@@ -2958,12 +2950,12 @@ csv_import_trans_assistant_create (CsvImportTrans *info)
         /* Load the column type treeview. */
         info->ctreeview = (GtkTreeView*)GTK_WIDGET(gtk_builder_get_object (builder, "ctreeview"));
 
-        /* This is TRUE only after encoding_selected is called, so we must
-         * set it initially to FALSE. */
-        info->encoding_selected_called = FALSE;
+        /* This is true only after encoding_selected is called, so we must
+         * set it initially to false. */
+        info->encoding_selected_called = false;
 
         /* It is empty at first. */
-        info->not_empty = FALSE;
+        info->not_empty = false;
     }
 
     /* Account page */
diff --git a/src/import-export/csv-imp/gnc-csv-imp-trans.cpp b/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
index 4051c00..610b0cd 100644
--- a/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
+++ b/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
@@ -38,7 +38,6 @@ extern "C" {
 
 #include <sys/types.h>
 #include <sys/stat.h>
-#include <regex.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <stdlib.h>
@@ -47,6 +46,7 @@ extern "C" {
 
 #include <algorithm>
 #include <boost/regex.hpp>
+#include <boost/regex/icu.hpp>
 
 #include "gnc-csv-imp-trans.hpp"
 #include "gnc-csv-tokenizer.hpp"
@@ -152,16 +152,15 @@ std::map<GncTransPropType, const char*> gnc_csv_col_type_strs = {
  */
 time64 parse_date (const std::string &date_str, int format)
 {
-    time64 rawtime; /* The integer time */
     struct tm retvalue, test_retvalue; /* The time in a broken-down structure */
     int orig_year = -1, orig_month = -1, orig_day = -1;
 
     boost::regex r(date_regex[format]);
     boost::smatch what;
-    if(!boost::regex_search(date_str.cbegin(), date_str.cend(), what, r))
+    if(!boost::regex_search(date_str, what, r))
         return -1;  // regex didn't find a match
 
-    // xxx Different behavior from 2.6.x series !
+    // Attention: different behavior from 2.6.x series !
     // If date format without year was selected, the match
     // should NOT have found a year.
     if ((format >= 3) && (what.length("YEAR") != 0))
@@ -170,6 +169,7 @@ time64 parse_date (const std::string &date_str, int format)
 
     /* Put some sane values in retvalue by using a fixed time for
      * the non-year-month-day parts of the date. */
+    time64 rawtime; /* The integer time */
     gnc_time (&rawtime);
     gnc_localtime_r (&rawtime, &retvalue);
     retvalue.tm_hour = 11;
@@ -254,8 +254,8 @@ int GncCsvParseData::file_format(GncImpFileFormat format,
     if (file_fmt == format)
         return 0;
 
-    std::string new_encoding = "UTF-8";
-    std::string new_imp_file;
+    auto new_encoding = std::string("UTF-8");
+    auto new_imp_file = std::string();
 
     // Recover common settings from old tokenizer
     if (tokenizer)
@@ -270,7 +270,7 @@ int GncCsvParseData::file_format(GncImpFileFormat format,
     // Set up new tokenizer with common settings
     // recovered from old tokenizer
     tokenizer->encoding(new_encoding);
-    return load_file(new_imp_file.c_str(), error);
+    return load_file(new_imp_file, error);
 }
 GncImpFileFormat GncCsvParseData::file_format()
 {
@@ -278,7 +278,7 @@ GncImpFileFormat GncCsvParseData::file_format()
 }
 
 /** Converts raw file data using a new encoding. This function must be
- * called after gnc_csv_load_file only if gnc_csv_load_file guessed
+ * called after load_file only if load_file guessed
  * the wrong encoding.
  * @param parse_data Data that is being parsed
  * @param encoding Encoding that data should be translated using
@@ -304,7 +304,7 @@ void GncCsvParseData::convert_encoding (const std::string& encoding)
  * @param error Will contain an error if there is a failure
  * @return 0 on success, 1 on failure
  */
-int GncCsvParseData::load_file (const char* filename,
+int GncCsvParseData::load_file (const std::string& filename,
                                 GError** error)
 {
 
@@ -336,7 +336,7 @@ int GncCsvParseData::load_file (const char* filename,
  * @param error Will contain an error if there is a failure
  * @return 0 on success, 1 on failure
  */
-int GncCsvParseData::parse (gboolean guessColTypes, GError** error)
+int GncCsvParseData::parse (bool guessColTypes, GError** error)
 {
     uint max_cols = 0;
     tokenizer->tokenize();
@@ -378,9 +378,9 @@ int GncCsvParseData::parse (gboolean guessColTypes, GError** error)
  * @param date_format The date format to use.
  * @return a pointer to a time64 on success, nullptr on failure
  */
-static time64* convert_date_col_str (const char* str, int date_format)
+static time64* convert_date_col_str (const std::string &str, int date_format)
 {
-    auto parsed_date = parse_date(str, date_format);
+    auto parsed_date = parse_date (str.c_str(), date_format);
     if (parsed_date == -1)
         return nullptr;
     else
@@ -397,72 +397,37 @@ static time64* convert_date_col_str (const char* str, int date_format)
  * @param currency_format The currency format to use.
  * @return a pointer to a gnc_numeric on success, nullptr on failure
  */
-static gnc_numeric* convert_amount_col_str (const char* str, int currency_format)
+static gnc_numeric* convert_amount_col_str (const std::string &str, int currency_format)
 {
-    auto str_dupe = g_strdup (str); /* First, we make a copy so we can't mess up real data. */
-    /* If a cell is empty or just spaces make its value = "0" */
-    regex_t regex;
-    int reti = regcomp(&regex, "[0-9]", 0);
-    reti = regexec(&regex, str_dupe, 0, NULL, 0);
-    if (reti == REG_NOMATCH)
-    {
-        g_free (str_dupe);
-        str_dupe = g_strdup ("0");
-    }
-    /* Go through str_dupe looking for currency symbols. */
-    for (auto possible_currency_symbol = str_dupe; *possible_currency_symbol;
-            possible_currency_symbol = g_utf8_next_char (possible_currency_symbol))
-    {
-        if (g_unichar_type (g_utf8_get_char (possible_currency_symbol)) == G_UNICODE_CURRENCY_SYMBOL)
-        {
-            /* If we find a currency symbol, save the position just ahead
-             * of the currency symbol (next_symbol), and find the null
-             * terminator of the string (last_symbol). */
-            char *next_symbol = g_utf8_next_char (possible_currency_symbol), *last_symbol = next_symbol;
-            while (*last_symbol)
-                last_symbol = g_utf8_next_char (last_symbol);
-
-            /* Move all of the string (including the null byte, which is
-             * why we have +1 in the size parameter) following the
-             * currency symbol back one character, thereby overwriting the
-             * currency symbol. */
-            memmove (possible_currency_symbol, next_symbol, last_symbol - next_symbol + 1);
-            break;
-        }
-    }
+    /* If a cell is empty or just spaces return 0 as amount */
+    if(!boost::regex_search(str, boost::regex("[0-9]")))
+        return new gnc_numeric({0, 0});
+
+    auto expr = boost::make_u32regex("[[:Sc:]]");
+    std::string str_no_symbols = boost::u32regex_replace(str, expr, "");
 
-    /* Currency format */
+    /* Convert based on user chosen currency format */
     gnc_numeric val;
     char *endptr;
     switch (currency_format)
     {
     case 0:
         /* Currency locale */
-        if (!(xaccParseAmount (str_dupe, TRUE, &val, &endptr)))
-        {
-            g_free (str_dupe);
+        if (!(xaccParseAmount (str_no_symbols.c_str(), TRUE, &val, &endptr)))
             return nullptr;
-        }
         break;
     case 1:
         /* Currency decimal period */
-        if (!(xaccParseAmountExtended (str_dupe, TRUE, '-', '.', ',', "\003\003", "$+", &val, &endptr)))
-        {
-            g_free (str_dupe);
+        if (!(xaccParseAmountExtended (str_no_symbols.c_str(), TRUE, '-', '.', ',', "\003\003", "$+", &val, &endptr)))
             return nullptr;
-        }
         break;
     case 2:
         /* Currency decimal comma */
-        if (!(xaccParseAmountExtended (str_dupe, TRUE, '-', ',', '.', "\003\003", "$+", &val, &endptr)))
-        {
-            g_free (str_dupe);
+        if (!(xaccParseAmountExtended (str_no_symbols.c_str(), TRUE, '-', ',', '.', "\003\003", "$+", &val, &endptr)))
             return nullptr;
-        }
         break;
     }
 
-    g_free (str_dupe);
     auto amount = new gnc_numeric;
     *amount = val;
     return amount;
@@ -487,12 +452,12 @@ struct GncTransPropImpl
 {
 public:
     ~GncTransPropImpl(){};
-    GncTransPropImpl(std::string& val, int fmt)
+    GncTransPropImpl(const std::string& val, int fmt)
     {
         m_valid = false;
     };
 
-    static GncTransProperty* make_new(std::string& val,int fmt = 0)
+    static GncTransProperty* make_new(const std::string& val,int fmt = 0)
         { return nullptr; }
 
     T value;
@@ -502,15 +467,15 @@ template<>
 struct GncTransPropImpl<time64*>
 : public GncTransProperty
 {
-    GncTransPropImpl(std::string& val, int fmt)
+    GncTransPropImpl(const std::string& val, int fmt)
     {
-        value = convert_date_col_str (val.c_str(), fmt);
+        value = convert_date_col_str (val, fmt);
         m_valid = (value != nullptr);
     }
     ~GncTransPropImpl()
         { if (value) delete value; }
 
-    static std::shared_ptr<GncTransProperty> make_new(std::string& val,int fmt)
+    static std::shared_ptr<GncTransProperty> make_new(const std::string& val,int fmt)
     { return std::shared_ptr<GncTransProperty>(new GncTransPropImpl<time64*>(val, fmt)); }
 
     time64* value;
@@ -521,7 +486,7 @@ template<>
 struct GncTransPropImpl<std::string*>
 : public GncTransProperty
 {
-    GncTransPropImpl(std::string& val, int fmt = 0)
+    GncTransPropImpl(const std::string& val, int fmt = 0)
     {
         value = new std::string(val);
         m_valid = (value != nullptr);
@@ -529,7 +494,7 @@ struct GncTransPropImpl<std::string*>
     ~GncTransPropImpl()
         { if (value) delete value; }
 
-    static std::shared_ptr<GncTransProperty> make_new(std::string& val,int fmt = 0)
+    static std::shared_ptr<GncTransProperty> make_new(const std::string& val,int fmt = 0)
         { return std::shared_ptr<GncTransProperty>(new GncTransPropImpl<std::string*>(val)); } /* Note fmt is not used for strings */
 
     std::string* value;
@@ -539,7 +504,7 @@ template<>
 struct GncTransPropImpl<Account *>
 : public GncTransProperty
 {
-    GncTransPropImpl(std::string& val, int fmt = 0)
+    GncTransPropImpl(const std::string& val, int fmt = 0)
     {
         value = gnc_csv_account_map_search (val.c_str());
         m_valid = (value != nullptr);
@@ -548,7 +513,7 @@ struct GncTransPropImpl<Account *>
         { value = val; }
     ~GncTransPropImpl(){};
 
-    static std::shared_ptr<GncTransProperty> make_new(std::string& val,int fmt = 0)
+    static std::shared_ptr<GncTransProperty> make_new(const std::string& val,int fmt = 0)
         { return std::shared_ptr<GncTransProperty>(new GncTransPropImpl<Account*>(val)); } /* Note fmt is not used in for accounts */
 
     Account* value;
@@ -558,15 +523,15 @@ template<>
 struct GncTransPropImpl<gnc_numeric *>
 : public GncTransProperty
 {
-    GncTransPropImpl(std::string& val, int fmt)
+    GncTransPropImpl(const std::string& val, int fmt)
     {
-        value = convert_amount_col_str (val.c_str(), fmt);
+        value = convert_amount_col_str (val, fmt);
         m_valid = (value != nullptr);
     }
     ~GncTransPropImpl()
         { if (value) delete value; }
 
-    static std::shared_ptr<GncTransProperty> make_new(std::string& val,int fmt)
+    static std::shared_ptr<GncTransProperty> make_new(const std::string& val,int fmt)
     { return std::shared_ptr<GncTransProperty>(new GncTransPropImpl<gnc_numeric*>(val, fmt)); }
 
     gnc_numeric* value;
@@ -581,7 +546,7 @@ struct GncTransPropImpl<gnc_numeric *>
 static void trans_add_split (Transaction* trans, Account* account, QofBook* book,
                             gnc_numeric amount, const std::string& num, const std::string& memo)
 {
-    Split* split = xaccMallocSplit (book);
+    auto split = xaccMallocSplit (book);
     xaccSplitSetAccount (split, account);
     xaccSplitSetParent (split, trans);
     xaccSplitSetAmount (split, amount);
@@ -788,7 +753,7 @@ static GncCsvTransLine* trans_properties_to_trans (prop_map_t& trans_props, gcha
  * @return 0 on success, 1 on failure
  */
 int GncCsvParseData::parse_to_trans (Account* account,
-                                     gboolean redo_errors)
+                                     bool redo_errors)
 {
     /* Free error_lines and transactions if they
      * already exist. */
@@ -834,7 +799,7 @@ int GncCsvParseData::parse_to_trans (Account* account,
         std::advance(orig_lines_max, end_row);
 
     Account *home_account = NULL;
-    bool odd_line = false;
+    auto odd_line = false;
     parse_errors = false;
     for (orig_lines_it, odd_line;
             orig_lines_it != orig_lines_max;
@@ -855,15 +820,20 @@ int GncCsvParseData::parse_to_trans (Account* account,
         auto line = orig_lines_it->first;
         GncCsvTransLine* trans_line = NULL;
 
-        /* Affect the transaction appropriately. */
-        bool loop_err = false;
-        for (uint j = 0; j < line.size(); j++)
+        /* Convert this import line into a map of transaction/split properties. */
+        auto loop_err = false;
+        auto col_types_it = column_types.cbegin();
+        auto line_it = line.cbegin();
+        for (col_types_it, line_it;
+                col_types_it != column_types.cend(),
+                line_it != line.cend();
+                ++col_types_it, ++line_it)
         {
             std::shared_ptr<GncTransProperty> property;
-            switch (column_types[j])
+            switch (*col_types_it)
             {
                 case GncTransPropType::DATE:
-                    property = GncTransPropImpl<time64*>::make_new (line[j], date_format);
+                    property = GncTransPropImpl<time64*>::make_new (*line_it, date_format);
                 break;
 
                 case GncTransPropType::DESCRIPTION:
@@ -871,12 +841,12 @@ int GncCsvParseData::parse_to_trans (Account* account,
                 case GncTransPropType::MEMO:
                 case GncTransPropType::OMEMO:
                 case GncTransPropType::NUM:
-                    property = GncTransPropImpl<std::string*>::make_new (line[j]);
+                    property = GncTransPropImpl<std::string*>::make_new (*line_it);
                     break;
 
                 case GncTransPropType::ACCOUNT:
                 case GncTransPropType::OACCOUNT:
-                    property = GncTransPropImpl<Account*>::make_new (line[j]);
+                    property = GncTransPropImpl<Account*>::make_new (*line_it);
                     if (*col_types_it == GncTransPropType::ACCOUNT)
                         home_account = dynamic_cast<GncTransPropImpl<Account*>*>(property.get())->value;
                     break;
@@ -884,7 +854,7 @@ int GncCsvParseData::parse_to_trans (Account* account,
                 case GncTransPropType::BALANCE:
                 case GncTransPropType::DEPOSIT:
                 case GncTransPropType::WITHDRAWAL:
-                    property = GncTransPropImpl<gnc_numeric*>::make_new (line[j], currency_format);
+                    property = GncTransPropImpl<gnc_numeric*>::make_new (*line_it, currency_format);
                     break;
 
                 default:
@@ -893,12 +863,12 @@ int GncCsvParseData::parse_to_trans (Account* account,
             }
 
             if (property->m_valid)
-                trans_props.insert(prop_pair_t(column_types[j], property));
+                trans_props.insert(prop_pair_t(*col_types_it, property));
             else
             {
                 parse_errors = loop_err = true;
                 gchar *error_message = g_strdup_printf (_("%s column could not be understood."),
-                                                _(gnc_csv_col_type_strs[column_types[j]]));
+                                                _(gnc_csv_col_type_strs[*col_types_it]));
                 orig_lines_it->second = error_message;
 
                 g_free (error_message);
@@ -995,7 +965,7 @@ int GncCsvParseData::parse_to_trans (Account* account,
                                      GNC_HOW_RND_ROUND_HALF_UP);
         while (tx_iter != NULL)
         {
-            GncCsvTransLine* trans_line = (GncCsvTransLine*)tx_iter->data;
+            auto trans_line = static_cast<GncCsvTransLine*> (tx_iter->data);
             if (trans_line->balance_set)
             {
                 time64 date = xaccTransGetDate (trans_line->trans);
diff --git a/src/import-export/csv-imp/gnc-csv-imp-trans.hpp b/src/import-export/csv-imp/gnc-csv-imp-trans.hpp
index a432f20..c8a4c87 100644
--- a/src/import-export/csv-imp/gnc-csv-imp-trans.hpp
+++ b/src/import-export/csv-imp/gnc-csv-imp-trans.hpp
@@ -121,11 +121,11 @@ public:
     int file_format(GncImpFileFormat format, GError** error);
     GncImpFileFormat file_format();
 
-    int load_file (const char* filename, GError** error);
+    int load_file (const std::string& filename, GError** error);
     void convert_encoding (const std::string& encoding);
 
-    int parse (gboolean guessColTypes, GError** error);
-    int parse_to_trans (Account* account, gboolean redo_errors);
+    int parse (bool guessColTypes, GError** error);
+    int parse_to_trans (Account* account, bool redo_errors);
     bool check_for_column_type (GncTransPropType type);
 
     std::unique_ptr<GncTokenizer> tokenizer;    /**< Will handle file loading/encoding conversion/splitting into fields */

commit 05c187960a1eeaacf5cf6eee81e8b9e266afebf7
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sat Sep 10 14:04:11 2016 +0200

    Delay the test for a default account until it's needed

diff --git a/src/import-export/csv-imp/gnc-csv-imp-trans.cpp b/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
index e347b47..4051c00 100644
--- a/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
+++ b/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
@@ -855,25 +855,6 @@ int GncCsvParseData::parse_to_trans (Account* account,
         auto line = orig_lines_it->first;
         GncCsvTransLine* trans_line = NULL;
 
-        // If account = NULL, we should have an Account column
-        home_account = account;
-        if (home_account == NULL)
-        {
-            for (uint j = 0; j < line.size(); j++)
-            {
-                /* Look for "Account" columns. */
-                if (column_types[j] == GncTransPropType::ACCOUNT)
-                    home_account = gnc_csv_account_map_search (line[j].c_str());
-            }
-        }
-
-        if (home_account == NULL)
-        {
-            parse_errors = true;
-            orig_lines_it->second = _("Account column could not be understood.");
-            continue;
-        }
-
         /* Affect the transaction appropriately. */
         bool loop_err = false;
         for (uint j = 0; j < line.size(); j++)
@@ -896,6 +877,8 @@ int GncCsvParseData::parse_to_trans (Account* account,
                 case GncTransPropType::ACCOUNT:
                 case GncTransPropType::OACCOUNT:
                     property = GncTransPropImpl<Account*>::make_new (line[j]);
+                    if (*col_types_it == GncTransPropType::ACCOUNT)
+                        home_account = dynamic_cast<GncTransPropImpl<Account*>*>(property.get())->value;
                     break;
 
                 case GncTransPropType::BALANCE:
@@ -922,16 +905,30 @@ int GncCsvParseData::parse_to_trans (Account* account,
                 break;
             }
         }
-        // Add an ACCOUNT property with the home_account if no account column was set by the user
-        if (std::find (column_types.begin(), column_types.end(), GncTransPropType::ACCOUNT) == column_types.end())
-        {
-            auto property = std::shared_ptr<GncTransProperty>(new GncTransPropImpl<Account*>(home_account));
-            trans_props.insert(prop_pair_t(GncTransPropType::ACCOUNT, property));
-        }
 
         if (loop_err)
             continue;
 
+        // Add an ACCOUNT property with the default account if no account column was set by the user
+        if (std::find (column_types.begin(), column_types.end(), GncTransPropType::ACCOUNT) == column_types.end())
+        {
+            // If there is no ACCOUNT property by now, try to use the default account passed in
+            if (account)
+            {
+                auto property = std::shared_ptr<GncTransProperty>(new GncTransPropImpl<Account*>(account));
+                trans_props.insert(prop_pair_t(GncTransPropType::ACCOUNT, property));
+                home_account = account;
+            }
+            else
+            {
+                // Oops - the user didn't select an Account column *and* we didn't get a default value either!
+                // Note if you get here this suggests a bug in the code!
+                parse_errors = true;
+                orig_lines_it->second = _("No account column selected and no default account specified either.");
+                continue;
+            }
+        }
+
         /* If column parsing was successful, convert trans properties into a trans line. */
         gchar *error_message = NULL;
         trans_line = trans_properties_to_trans (trans_props, &error_message);

commit 11ff8273e549357d4e03ef40722b5d762a606f60
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Fri Sep 9 18:58:21 2016 +0200

    Fix memory management for the temporary trans properties
    
    This is done by wrapping each property in a minimal template class hierarchy
    and keep track of each using a std::shared_pointer

diff --git a/src/import-export/csv-imp/gnc-csv-imp-trans.cpp b/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
index 2015482..e347b47 100644
--- a/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
+++ b/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
@@ -372,6 +372,7 @@ int GncCsvParseData::parse (gboolean guessColTypes, GError** error)
     return 0;
 }
 
+
 /** Convert str into a time64 using the user-specified (import) date format.
  * @param str The string to be parsed
  * @param date_format The date format to use.
@@ -467,6 +468,110 @@ static gnc_numeric* convert_amount_col_str (const char* str, int currency_format
     return amount;
 }
 
+/* Define a class hierarchy to temporarily store transaction/split properties
+ * found in one import line. There is a generic parent class and an implementation
+ * template class. This template class is further specialized for each data type
+ * we support (currently time64, Account, string and gnc_numeric).
+ */
+struct GncTransProperty
+{
+    virtual ~GncTransProperty()
+      //Remove pure designation.
+        {}
+    bool m_valid = false;
+};
+
+template<class T>
+struct GncTransPropImpl
+: public GncTransProperty
+{
+public:
+    ~GncTransPropImpl(){};
+    GncTransPropImpl(std::string& val, int fmt)
+    {
+        m_valid = false;
+    };
+
+    static GncTransProperty* make_new(std::string& val,int fmt = 0)
+        { return nullptr; }
+
+    T value;
+};
+
+template<>
+struct GncTransPropImpl<time64*>
+: public GncTransProperty
+{
+    GncTransPropImpl(std::string& val, int fmt)
+    {
+        value = convert_date_col_str (val.c_str(), fmt);
+        m_valid = (value != nullptr);
+    }
+    ~GncTransPropImpl()
+        { if (value) delete value; }
+
+    static std::shared_ptr<GncTransProperty> make_new(std::string& val,int fmt)
+    { return std::shared_ptr<GncTransProperty>(new GncTransPropImpl<time64*>(val, fmt)); }
+
+    time64* value;
+};
+
+
+template<>
+struct GncTransPropImpl<std::string*>
+: public GncTransProperty
+{
+    GncTransPropImpl(std::string& val, int fmt = 0)
+    {
+        value = new std::string(val);
+        m_valid = (value != nullptr);
+    }
+    ~GncTransPropImpl()
+        { if (value) delete value; }
+
+    static std::shared_ptr<GncTransProperty> make_new(std::string& val,int fmt = 0)
+        { return std::shared_ptr<GncTransProperty>(new GncTransPropImpl<std::string*>(val)); } /* Note fmt is not used for strings */
+
+    std::string* value;
+};
+
+template<>
+struct GncTransPropImpl<Account *>
+: public GncTransProperty
+{
+    GncTransPropImpl(std::string& val, int fmt = 0)
+    {
+        value = gnc_csv_account_map_search (val.c_str());
+        m_valid = (value != nullptr);
+    }
+    GncTransPropImpl(Account* val)
+        { value = val; }
+    ~GncTransPropImpl(){};
+
+    static std::shared_ptr<GncTransProperty> make_new(std::string& val,int fmt = 0)
+        { return std::shared_ptr<GncTransProperty>(new GncTransPropImpl<Account*>(val)); } /* Note fmt is not used in for accounts */
+
+    Account* value;
+};
+
+template<>
+struct GncTransPropImpl<gnc_numeric *>
+: public GncTransProperty
+{
+    GncTransPropImpl(std::string& val, int fmt)
+    {
+        value = convert_amount_col_str (val.c_str(), fmt);
+        m_valid = (value != nullptr);
+    }
+    ~GncTransPropImpl()
+        { if (value) delete value; }
+
+    static std::shared_ptr<GncTransProperty> make_new(std::string& val,int fmt)
+    { return std::shared_ptr<GncTransProperty>(new GncTransPropImpl<gnc_numeric*>(val, fmt)); }
+
+    gnc_numeric* value;
+};
+
 /** Adds a split to a transaction.
  * @param trans The transaction to add a split to
  * @param account The account used for the split
@@ -489,8 +594,13 @@ static void trans_add_split (Transaction* trans, Account* account, QofBook* book
         gnc_set_num_action (trans, split, num.c_str(), NULL);
 }
 
-using prop_pair_t = std::pair<GncTransPropType, void*>;
-using prop_map_t = std::map<GncTransPropType, void*>;
+
+/* Shorthand aliases for the container to keep track of property types (a map)
+ * and its iterator (a pair)
+ */
+using prop_pair_t = std::pair<GncTransPropType, std::shared_ptr<GncTransProperty>>;
+using prop_map_t = std::map<GncTransPropType, std::shared_ptr<GncTransProperty>>;
+
 /** Tests a TransPropertyList for having enough essential properties.
  * Essential properties are
  * - "Date"
@@ -524,8 +634,11 @@ static bool trans_properties_verify_essentials (prop_map_t& trans_props, gchar**
     return have_amount && have_date;
 }
 
-/** Create a Transaction from a TransPropertyList.
- * @param list The list of properties
+/** Create a Transaction from a map of transaction properties.
+ * Note: this function assumes all properties in the map have been verified
+ *       to be valid. No further checks are performed here other than that
+ *       the required properties are in the map
+ * @param transprops The map of transaction properties
  * @param error Contains an error on failure
  * @return On success, a GncCsvTransLine; on failure, the trans pointer is NULL
  */
@@ -536,7 +649,7 @@ static GncCsvTransLine* trans_properties_to_trans (prop_map_t& trans_props, gcha
         return NULL;
 
     auto property = trans_props.find (GncTransPropType::ACCOUNT)->second;
-    Account* account = static_cast<Account*> (property);
+    auto account = dynamic_cast<GncTransPropImpl<Account*>*>(property.get())->value;
 
     GncCsvTransLine* trans_line = g_new (GncCsvTransLine, 1);
 
@@ -571,40 +684,49 @@ static GncCsvTransLine* trans_properties_to_trans (prop_map_t& trans_props, gcha
         switch (type)
         {
         case GncTransPropType::DATE:
-            xaccTransSetDatePostedSecsNormalized (trans_line->trans, *((time64*)(prop)));
+            {
+                auto transdate = dynamic_cast<GncTransPropImpl<time64*>*>(prop.get())->value;
+                xaccTransSetDatePostedSecsNormalized (trans_line->trans, *transdate);
+            }
             break;
 
         case GncTransPropType::DESCRIPTION:
-            xaccTransSetDescription (trans_line->trans, (char*)(prop));
+            {
+                auto propstring = dynamic_cast<GncTransPropImpl<std::string*>*>(prop.get())->value;
+                xaccTransSetDescription (trans_line->trans, propstring->c_str());
+            }
             break;
 
         case GncTransPropType::NOTES:
-            xaccTransSetNotes (trans_line->trans, (char*)(prop));
+            {
+                auto propstring = dynamic_cast<GncTransPropImpl<std::string*>*>(prop.get())->value;
+                xaccTransSetNotes (trans_line->trans, propstring->c_str());
+            }
             break;
 
         case GncTransPropType::OACCOUNT:
-            oaccount = ((Account*)(prop));
+            oaccount = dynamic_cast<GncTransPropImpl<Account*>*>(prop.get())->value;
             break;
 
         case GncTransPropType::MEMO:
-            memo = g_strdup ((char*)(prop));
+            memo = *dynamic_cast<GncTransPropImpl<std::string*>*>(prop.get())->value;
             break;
 
         case GncTransPropType::OMEMO:
-            omemo = g_strdup ((char*)(prop));
+            omemo = *dynamic_cast<GncTransPropImpl<std::string*>*>(prop.get())->value;
             break;
 
         case GncTransPropType::NUM:
             /* the 'num' is saved and passed to 'trans_add_split' below where
              * 'gnc_set_num_action' is used to set tran-num and/or split-action
              * per book option */
-            num = g_strdup ((char*)(prop));
+            num = *dynamic_cast<GncTransPropImpl<std::string*>*>(prop.get())->value;
             break;
 
         case GncTransPropType::DEPOSIT: /* Add deposits to the existing amount. */
-            if (prop != NULL)
             {
-                amount = gnc_numeric_add (*((gnc_numeric*)(prop)),
+                auto propval = dynamic_cast<GncTransPropImpl<gnc_numeric*>*>(prop.get())->value;
+                amount = gnc_numeric_add (*propval,
                                          amount,
                                          xaccAccountGetCommoditySCU (account),
                                          GNC_HOW_RND_ROUND_HALF_UP);
@@ -615,9 +737,9 @@ static GncCsvTransLine* trans_properties_to_trans (prop_map_t& trans_props, gcha
             break;
 
         case GncTransPropType::WITHDRAWAL: /* Withdrawals are just negative deposits. */
-            if (prop != NULL)
             {
-                amount = gnc_numeric_add (gnc_numeric_neg(*((gnc_numeric*)(prop))),
+                auto propval = dynamic_cast<GncTransPropImpl<gnc_numeric*>*>(prop.get())->value;
+                amount = gnc_numeric_add (gnc_numeric_neg(*propval),
                                          amount,
                                          xaccAccountGetCommoditySCU (account),
                                          GNC_HOW_RND_ROUND_HALF_UP);
@@ -629,10 +751,11 @@ static GncCsvTransLine* trans_properties_to_trans (prop_map_t& trans_props, gcha
 
         case GncTransPropType::BALANCE: /* The balance gets stored in a separate field in trans_line. */
             /* We will use the "Deposit" and "Withdrawal" columns in preference to "Balance". */
-            if (!amount_set && prop != NULL)
+            if (!amount_set)
             {
+                auto propval = dynamic_cast<GncTransPropImpl<gnc_numeric*>*>(prop.get())->value;
                 /* This gets put into the actual transaction at the end of gnc_csv_parse_to_trans. */
-                trans_line->balance = *((gnc_numeric*)(prop));
+                trans_line->balance = *propval;
                 trans_line->balance_set = true;
             }
             break;
@@ -755,11 +878,11 @@ int GncCsvParseData::parse_to_trans (Account* account,
         bool loop_err = false;
         for (uint j = 0; j < line.size(); j++)
         {
-            void* property;
+            std::shared_ptr<GncTransProperty> property;
             switch (column_types[j])
             {
                 case GncTransPropType::DATE:
-                    property = static_cast<void*> (convert_date_col_str (line[j].c_str(), date_format));
+                    property = GncTransPropImpl<time64*>::make_new (line[j], date_format);
                 break;
 
                 case GncTransPropType::DESCRIPTION:
@@ -767,18 +890,18 @@ int GncCsvParseData::parse_to_trans (Account* account,
                 case GncTransPropType::MEMO:
                 case GncTransPropType::OMEMO:
                 case GncTransPropType::NUM:
-                    property = static_cast<void*> (g_strdup (line[j].c_str()));
+                    property = GncTransPropImpl<std::string*>::make_new (line[j]);
                     break;
 
                 case GncTransPropType::ACCOUNT:
                 case GncTransPropType::OACCOUNT:
-                    property = static_cast<void*> (gnc_csv_account_map_search (line[j].c_str()));
+                    property = GncTransPropImpl<Account*>::make_new (line[j]);
                     break;
 
                 case GncTransPropType::BALANCE:
                 case GncTransPropType::DEPOSIT:
                 case GncTransPropType::WITHDRAWAL:
-                    property = static_cast<void*> (convert_amount_col_str (line[j].c_str(), currency_format));
+                    property = GncTransPropImpl<gnc_numeric*>::make_new (line[j], currency_format);
                     break;
 
                 default:
@@ -786,7 +909,7 @@ int GncCsvParseData::parse_to_trans (Account* account,
                     break;
             }
 
-            if (property)
+            if (property->m_valid)
                 trans_props.insert(prop_pair_t(column_types[j], property));
             else
             {
@@ -801,10 +924,12 @@ int GncCsvParseData::parse_to_trans (Account* account,
         }
         // Add an ACCOUNT property with the home_account if no account column was set by the user
         if (std::find (column_types.begin(), column_types.end(), GncTransPropType::ACCOUNT) == column_types.end())
-            trans_props.insert(prop_pair_t(GncTransPropType::ACCOUNT, static_cast<void*> (home_account)));
+        {
+            auto property = std::shared_ptr<GncTransProperty>(new GncTransPropImpl<Account*>(home_account));
+            trans_props.insert(prop_pair_t(GncTransPropType::ACCOUNT, property));
+        }
 
         if (loop_err)
-            /* FIXME huge memory leak here: the trans_props for this line aren't freed! */
             continue;
 
         /* If column parsing was successful, convert trans properties into a trans line. */

commit bebc871fe8f9a88f135b28064387a67fcdd3e4c0
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sat Sep 10 11:50:35 2016 +0200

    Reduce code duplication by reusing trans_add_split

diff --git a/src/import-export/csv-imp/gnc-csv-imp-trans.cpp b/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
index 0d68039..2015482 100644
--- a/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
+++ b/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
@@ -474,33 +474,19 @@ static gnc_numeric* convert_amount_col_str (const char* str, int currency_format
  * @param amount The amount of the split
  */
 static void trans_add_split (Transaction* trans, Account* account, QofBook* book,
-                            gnc_numeric amount, const char *num, const char *memo)
+                            gnc_numeric amount, const std::string& num, const std::string& memo)
 {
     Split* split = xaccMallocSplit (book);
     xaccSplitSetAccount (split, account);
     xaccSplitSetParent (split, trans);
     xaccSplitSetAmount (split, amount);
     xaccSplitSetValue (split, amount);
-    xaccSplitSetMemo (split, memo);
-    /* set tran-num and/or split-action per book option */
-    gnc_set_num_action (trans, split, num, NULL);
-}
-
-/** Adds a other split to a transaction.
- * @param trans The transaction to add a split to
- * @param account The account used for the other split
- * @param book The book where the split should be stored
- * @param amount The amount of the split
- */
-static void trans_add_osplit (Transaction* trans, Account* account, QofBook* book,
-                            gnc_numeric amount, const char *num, const char *memo)
-{
-    Split *osplit = xaccMallocSplit (book);
-    xaccSplitSetAccount (osplit, account);
-    xaccSplitSetParent (osplit, trans);
-    xaccSplitSetAmount (osplit, amount);
-    xaccSplitSetValue (osplit, gnc_numeric_neg (amount));
-    xaccSplitSetMemo (osplit, memo);
+    if (!memo.empty())
+        xaccSplitSetMemo (split, memo.c_str());
+    /* set tran-num and/or split-action per book option
+     * note this function does nothing if num is NULL also */
+    if (!num.empty())
+        gnc_set_num_action (trans, split, num.c_str(), NULL);
 }
 
 using prop_pair_t = std::pair<GncTransPropType, void*>;
@@ -571,9 +557,9 @@ static GncCsvTransLine* trans_properties_to_trans (prop_map_t& trans_props, gcha
     xaccTransSetCurrency (trans_line->trans, currency);
 
     /* Go through each of the properties and edit the transaction accordingly. */
-    gchar *num = NULL;
-    gchar *memo = NULL;
-    gchar *omemo = NULL;
+    std::string num;
+    std::string memo;
+    std::string omemo;
     Account *oaccount = NULL;
     bool amount_set = false;
     gnc_numeric amount = trans_line->balance;
@@ -659,14 +645,11 @@ static GncCsvTransLine* trans_properties_to_trans (prop_map_t& trans_props, gcha
     trans_add_split (trans_line->trans, account, book, amount, num, memo);
 
     if (oaccount)
-        trans_add_osplit (trans_line->trans, oaccount, book, amount, num, omemo);
-
-    if (num)
-        g_free (num);
-    if (memo)
-        g_free (memo);
-    if (omemo)
-        g_free (omemo);
+        /* Note: the current importer assumes at most 2 splits. This means the second split amount
+         * will be the negative of the the first split amount. We also only set the num field once,
+         * for the first split.
+         */
+        trans_add_split (trans_line->trans, oaccount, book, gnc_numeric_neg(amount), "", omemo);
 
     return trans_line;
 }

commit 42c2f94b384ae5676ed74d7515e8090a13539d18
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Fri Sep 9 18:21:46 2016 +0200

    Fix memory corruption bug

diff --git a/src/import-export/csv-imp/gnc-csv-trans-settings.c b/src/import-export/csv-imp/gnc-csv-trans-settings.c
index cb83b37..2bfdbcb 100644
--- a/src/import-export/csv-imp/gnc-csv-trans-settings.c
+++ b/src/import-export/csv-imp/gnc-csv-trans-settings.c
@@ -232,7 +232,7 @@ gnc_csv_trans_load_settings (CsvSettings *settings_data, gchar *group)
        error = load_error (&key_error, group);
 
     key_char = g_key_file_get_string (keyfile, group, CSV_ENCODING, &key_error);
-    settings_data->encoding = (key_error) ? "UTF-8" : key_char;
+    settings_data->encoding = g_strdup((key_error) ? "UTF-8" : key_char);
     if (key_error)
        error = load_error (&key_error, group);
 

commit 2d42bf5920a1ced2ea38a11f8cbaf62eb1c105c3
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Fri Sep 9 14:51:37 2016 +0200

    Refactor TransProperty stuff to a simple void*
    
    Needs more attention for memory management though.

diff --git a/src/import-export/csv-imp/gnc-csv-imp-trans.cpp b/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
index 490237c..0d68039 100644
--- a/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
+++ b/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
@@ -372,150 +372,99 @@ int GncCsvParseData::parse (gboolean guessColTypes, GError** error)
     return 0;
 }
 
-/** A struct encapsulating a property of a transaction. */
-typedef struct
-{
-    void* value;             /**< Pointer to the data that will be used to configure a transaction */
-} TransProperty;
-
-/** Constructor for TransProperty.
- * @param type The type of the new property (see TransProperty.type for possible values)
- */
-static TransProperty* trans_property_new (void)
-{
-    TransProperty* prop = g_new (TransProperty, 1);
-    prop->value = NULL;
-    return prop;
-}
-
-/** Destructor for TransProperty.
- * @param prop The property to be freed
+/** Convert str into a time64 using the user-specified (import) date format.
+ * @param str The string to be parsed
+ * @param date_format The date format to use.
+ * @return a pointer to a time64 on success, nullptr on failure
  */
-static void trans_property_free (TransProperty* prop, GncTransPropType type)
+static time64* convert_date_col_str (const char* str, int date_format)
 {
-    switch (type)
+    auto parsed_date = parse_date(str, date_format);
+    if (parsed_date == -1)
+        return nullptr;
+    else
     {
-        /* The types for "Date" and "Balance" (time64 and gnc_numeric,
-         * respectively) are typically not pointed to, we have to free
-         * them, unlike types like char* ("Description"). */
-    case GncTransPropType::DATE:
-    case GncTransPropType::BALANCE:
-    case GncTransPropType::DEPOSIT:
-    case GncTransPropType::WITHDRAWAL:
-        if (prop->value != NULL)
-            g_free(prop->value);
-        break;
-    default:
-       break;
+        auto mydate = new time64;
+        *mydate = parsed_date;
+        return mydate;
     }
-    g_free (prop);
 }
 
-/** Sets the value of the property by parsing str. Note: this should
- * only be called once on an instance of TransProperty, as calling it
- * more than once can cause memory leaks.
- * @param prop The property being set
+
+/** Convert str into a gnc_numeric using the user-specified (import) currency format.
  * @param str The string to be parsed
- * @return TRUE on success, FALSE on failure
+ * @param currency_format The currency format to use.
+ * @return a pointer to a gnc_numeric on success, nullptr on failure
  */
-static gboolean trans_property_set (TransProperty* prop, GncTransPropType type, const char* str, int currency_format, int date_format)
+static gnc_numeric* convert_amount_col_str (const char* str, int currency_format)
 {
-    char *endptr, *possible_currency_symbol, *str_dupe;
-    gnc_numeric val;
-    int reti;
+    auto str_dupe = g_strdup (str); /* First, we make a copy so we can't mess up real data. */
+    /* If a cell is empty or just spaces make its value = "0" */
     regex_t regex;
-    switch (type)
+    int reti = regcomp(&regex, "[0-9]", 0);
+    reti = regexec(&regex, str_dupe, 0, NULL, 0);
+    if (reti == REG_NOMATCH)
+    {
+        g_free (str_dupe);
+        str_dupe = g_strdup ("0");
+    }
+    /* Go through str_dupe looking for currency symbols. */
+    for (auto possible_currency_symbol = str_dupe; *possible_currency_symbol;
+            possible_currency_symbol = g_utf8_next_char (possible_currency_symbol))
+    {
+        if (g_unichar_type (g_utf8_get_char (possible_currency_symbol)) == G_UNICODE_CURRENCY_SYMBOL)
+        {
+            /* If we find a currency symbol, save the position just ahead
+             * of the currency symbol (next_symbol), and find the null
+             * terminator of the string (last_symbol). */
+            char *next_symbol = g_utf8_next_char (possible_currency_symbol), *last_symbol = next_symbol;
+            while (*last_symbol)
+                last_symbol = g_utf8_next_char (last_symbol);
+
+            /* Move all of the string (including the null byte, which is
+             * why we have +1 in the size parameter) following the
+             * currency symbol back one character, thereby overwriting the
+             * currency symbol. */
+            memmove (possible_currency_symbol, next_symbol, last_symbol - next_symbol + 1);
+            break;
+        }
+    }
+
+    /* Currency format */
+    gnc_numeric val;
+    char *endptr;
+    switch (currency_format)
     {
-    case GncTransPropType::DATE:
-        prop->value = g_new(time64, 1);
-        *((time64*)(prop->value)) = parse_date(str, date_format);
-        return *((time64*)(prop->value)) != -1;
-
-    case GncTransPropType::DESCRIPTION:
-    case GncTransPropType::NOTES:
-    case GncTransPropType::MEMO:
-    case GncTransPropType::OMEMO:
-    case GncTransPropType::NUM:
-        prop->value = g_strdup (str);
-        return TRUE;
-
-    case GncTransPropType::ACCOUNT:
-    case GncTransPropType::OACCOUNT:
-        prop->value = gnc_csv_account_map_search (str);
-        return TRUE;
-
-    case GncTransPropType::BALANCE:
-    case GncTransPropType::DEPOSIT:
-    case GncTransPropType::WITHDRAWAL:
-        str_dupe = g_strdup (str); /* First, we make a copy so we can't mess up real data. */
-        /* If a cell is empty or just spaces make its value = "0" */
-        reti = regcomp(&regex, "[0-9]", 0);
-        reti = regexec(&regex, str_dupe, 0, NULL, 0);
-        if (reti == REG_NOMATCH)
+    case 0:
+        /* Currency locale */
+        if (!(xaccParseAmount (str_dupe, TRUE, &val, &endptr)))
         {
             g_free (str_dupe);
-            str_dupe = g_strdup ("0");
+            return nullptr;
         }
-        /* Go through str_dupe looking for currency symbols. */
-        for (possible_currency_symbol = str_dupe; *possible_currency_symbol;
-                possible_currency_symbol = g_utf8_next_char (possible_currency_symbol))
+        break;
+    case 1:
+        /* Currency decimal period */
+        if (!(xaccParseAmountExtended (str_dupe, TRUE, '-', '.', ',', "\003\003", "$+", &val, &endptr)))
         {
-            if (g_unichar_type (g_utf8_get_char (possible_currency_symbol)) == G_UNICODE_CURRENCY_SYMBOL)
-            {
-                /* If we find a currency symbol, save the position just ahead
-                 * of the currency symbol (next_symbol), and find the null
-                 * terminator of the string (last_symbol). */
-                char *next_symbol = g_utf8_next_char (possible_currency_symbol), *last_symbol = next_symbol;
-                while (*last_symbol)
-                    last_symbol = g_utf8_next_char (last_symbol);
-
-                /* Move all of the string (including the null byte, which is
-                 * why we have +1 in the size parameter) following the
-                 * currency symbol back one character, thereby overwriting the
-                 * currency symbol. */
-                memmove (possible_currency_symbol, next_symbol, last_symbol - next_symbol + 1);
-                break;
-            }
+            g_free (str_dupe);
+            return nullptr;
         }
-
-        /* Currency format */
-        switch (currency_format)
+        break;
+    case 2:
+        /* Currency decimal comma */
+        if (!(xaccParseAmountExtended (str_dupe, TRUE, '-', ',', '.', "\003\003", "$+", &val, &endptr)))
         {
-        case 0:
-            /* Currency locale */
-            if (!(xaccParseAmount (str_dupe, TRUE, &val, &endptr)))
-            {
-                g_free (str_dupe);
-                return FALSE;
-            }
-            break;
-        case 1:
-            /* Currency decimal period */
-            if (!(xaccParseAmountExtended (str_dupe, TRUE, '-', '.', ',', "\003\003", "$+", &val, &endptr)))
-            {
-                g_free (str_dupe);
-                return FALSE;
-            }
-            break;
-        case 2:
-            /* Currency decimal comma */
-            if (!(xaccParseAmountExtended (str_dupe, TRUE, '-', ',', '.', "\003\003", "$+", &val, &endptr)))
-            {
-                g_free (str_dupe);
-                return FALSE;
-            }
-            break;
+            g_free (str_dupe);
+            return nullptr;
         }
-
-        prop->value = g_new (gnc_numeric, 1);
-        *((gnc_numeric*)(prop->value)) = val;
-        g_free (str_dupe);
-        return TRUE;
-
-    default:
         break;
     }
-    return FALSE; /* We should never actually get here. */
+
+    g_free (str_dupe);
+    auto amount = new gnc_numeric;
+    *amount = val;
+    return amount;
 }
 
 /** Adds a split to a transaction.
@@ -554,6 +503,8 @@ static void trans_add_osplit (Transaction* trans, Account* account, QofBook* boo
     xaccSplitSetMemo (osplit, memo);
 }
 
+using prop_pair_t = std::pair<GncTransPropType, void*>;
+using prop_map_t = std::map<GncTransPropType, void*>;
 /** Tests a TransPropertyList for having enough essential properties.
  * Essential properties are
  * - "Date"
@@ -564,7 +515,7 @@ static void trans_add_osplit (Transaction* trans, Account* account, QofBook* boo
  * @param error Contains an error message on failure
  * @return true if there are enough essentials; false otherwise
  */
-static bool trans_properties_verify_essentials (std::map<GncTransPropType, TransProperty* >& trans_props, gchar** error)
+static bool trans_properties_verify_essentials (prop_map_t& trans_props, gchar** error)
 {
     /* Make sure this is a transaction with all the columns we need. */
     bool have_date = (trans_props.find (GncTransPropType::DATE) != trans_props.end());
@@ -592,14 +543,14 @@ static bool trans_properties_verify_essentials (std::map<GncTransPropType, Trans
  * @param error Contains an error on failure
  * @return On success, a GncCsvTransLine; on failure, the trans pointer is NULL
  */
-static GncCsvTransLine* trans_properties_to_trans (std::map<GncTransPropType, TransProperty* >& trans_props, gchar** error)
+static GncCsvTransLine* trans_properties_to_trans (prop_map_t& trans_props, gchar** error)
 {
 
     if (!trans_properties_verify_essentials(trans_props, error))
         return NULL;
 
-    TransProperty * property = trans_props.find (GncTransPropType::ACCOUNT)->second;
-    Account* account = static_cast<Account*> (property->value);
+    auto property = trans_props.find (GncTransPropType::ACCOUNT)->second;
+    Account* account = static_cast<Account*> (property);
 
     GncCsvTransLine* trans_line = g_new (GncCsvTransLine, 1);
 
@@ -629,45 +580,45 @@ static GncCsvTransLine* trans_properties_to_trans (std::map<GncTransPropType, Tr
 
     for (auto prop_pair : trans_props)
     {
-        GncTransPropType type = prop_pair.first;
-        TransProperty* prop = prop_pair.second;
+        auto type = prop_pair.first;
+        auto prop = prop_pair.second;
         switch (type)
         {
         case GncTransPropType::DATE:
-            xaccTransSetDatePostedSecsNormalized (trans_line->trans, *((time64*)(prop->value)));
+            xaccTransSetDatePostedSecsNormalized (trans_line->trans, *((time64*)(prop)));
             break;
 
         case GncTransPropType::DESCRIPTION:
-            xaccTransSetDescription (trans_line->trans, (char*)(prop->value));
+            xaccTransSetDescription (trans_line->trans, (char*)(prop));
             break;
 
         case GncTransPropType::NOTES:
-            xaccTransSetNotes (trans_line->trans, (char*)(prop->value));
+            xaccTransSetNotes (trans_line->trans, (char*)(prop));
             break;
 
         case GncTransPropType::OACCOUNT:
-            oaccount = ((Account*)(prop->value));
+            oaccount = ((Account*)(prop));
             break;
 
         case GncTransPropType::MEMO:
-            memo = g_strdup ((char*)(prop->value));
+            memo = g_strdup ((char*)(prop));
             break;
 
         case GncTransPropType::OMEMO:
-            omemo = g_strdup ((char*)(prop->value));
+            omemo = g_strdup ((char*)(prop));
             break;
 
         case GncTransPropType::NUM:
             /* the 'num' is saved and passed to 'trans_add_split' below where
              * 'gnc_set_num_action' is used to set tran-num and/or split-action
              * per book option */
-            num = g_strdup ((char*)(prop->value));
+            num = g_strdup ((char*)(prop));
             break;
 
         case GncTransPropType::DEPOSIT: /* Add deposits to the existing amount. */
-            if (prop->value != NULL)
+            if (prop != NULL)
             {
-                amount = gnc_numeric_add (*((gnc_numeric*)(prop->value)),
+                amount = gnc_numeric_add (*((gnc_numeric*)(prop)),
                                          amount,
                                          xaccAccountGetCommoditySCU (account),
                                          GNC_HOW_RND_ROUND_HALF_UP);
@@ -678,9 +629,9 @@ static GncCsvTransLine* trans_properties_to_trans (std::map<GncTransPropType, Tr
             break;
 
         case GncTransPropType::WITHDRAWAL: /* Withdrawals are just negative deposits. */
-            if (prop->value != NULL)
+            if (prop != NULL)
             {
-                amount = gnc_numeric_add (gnc_numeric_neg(*((gnc_numeric*)(prop->value))),
+                amount = gnc_numeric_add (gnc_numeric_neg(*((gnc_numeric*)(prop))),
                                          amount,
                                          xaccAccountGetCommoditySCU (account),
                                          GNC_HOW_RND_ROUND_HALF_UP);
@@ -692,10 +643,10 @@ static GncCsvTransLine* trans_properties_to_trans (std::map<GncTransPropType, Tr
 
         case GncTransPropType::BALANCE: /* The balance gets stored in a separate field in trans_line. */
             /* We will use the "Deposit" and "Withdrawal" columns in preference to "Balance". */
-            if (!amount_set && prop->value != NULL)
+            if (!amount_set && prop != NULL)
             {
                 /* This gets put into the actual transaction at the end of gnc_csv_parse_to_trans. */
-                trans_line->balance = *((gnc_numeric*)(prop->value));
+                trans_line->balance = *((gnc_numeric*)(prop));
                 trans_line->balance_set = true;
             }
             break;
@@ -783,7 +734,7 @@ int GncCsvParseData::parse_to_trans (Account* account,
             orig_lines_it != orig_lines_max;
             ++orig_lines_it, odd_line = !odd_line)
     {
-        std::map<GncTransPropType, TransProperty *> trans_props;
+        prop_map_t trans_props;
 
         /* Skip current line if:
            1. only looking for lines with error AND no error on current line
@@ -817,40 +768,60 @@ int GncCsvParseData::parse_to_trans (Account* account,
             continue;
         }
 
+        /* Affect the transaction appropriately. */
         bool loop_err = false;
         for (uint j = 0; j < line.size(); j++)
         {
-            /* We do nothing with "None"-type columns. */
-            if (column_types[j] != GncTransPropType::NONE)
+            void* property;
+            switch (column_types[j])
             {
-                /* Affect the transaction appropriately. */
-                TransProperty* property = trans_property_new ();
-                gboolean succeeded = trans_property_set (property, column_types[j], line[j].c_str(), currency_format, date_format);
-
-                /* TODO Maybe move error handling to within TransPropertyList functions? */
-                if (succeeded)
-                    trans_props.insert(std::pair<GncTransPropType, TransProperty *>(column_types[j], property));
-                else
-                {
-                    parse_errors = loop_err = true;
-                    gchar *error_message = g_strdup_printf (_("%s column could not be understood."),
-                                                    _(gnc_csv_col_type_strs[column_types[j]]));
-                    orig_lines_it->second = error_message;
+                case GncTransPropType::DATE:
+                    property = static_cast<void*> (convert_date_col_str (line[j].c_str(), date_format));
+                break;
 
-                    g_free (error_message);
-                    trans_property_free (property, column_types[j]);
+                case GncTransPropType::DESCRIPTION:
+                case GncTransPropType::NOTES:
+                case GncTransPropType::MEMO:
+                case GncTransPropType::OMEMO:
+                case GncTransPropType::NUM:
+                    property = static_cast<void*> (g_strdup (line[j].c_str()));
                     break;
-                }
+
+                case GncTransPropType::ACCOUNT:
+                case GncTransPropType::OACCOUNT:
+                    property = static_cast<void*> (gnc_csv_account_map_search (line[j].c_str()));
+                    break;
+
+                case GncTransPropType::BALANCE:
+                case GncTransPropType::DEPOSIT:
+                case GncTransPropType::WITHDRAWAL:
+                    property = static_cast<void*> (convert_amount_col_str (line[j].c_str(), currency_format));
+                    break;
+
+                default:
+                    continue; /* We do nothing with "None"-type columns. */
+                    break;
+            }
+
+            if (property)
+                trans_props.insert(prop_pair_t(column_types[j], property));
+            else
+            {
+                parse_errors = loop_err = true;
+                gchar *error_message = g_strdup_printf (_("%s column could not be understood."),
+                                                _(gnc_csv_col_type_strs[column_types[j]]));
+                orig_lines_it->second = error_message;
+
+                g_free (error_message);
+                break;
             }
         }
         // Add an ACCOUNT property with the home_account if no account column was set by the user
         if (std::find (column_types.begin(), column_types.end(), GncTransPropType::ACCOUNT) == column_types.end())
-        {
-            TransProperty* property = trans_property_new ();
-            property->value = home_account;
-        }
+            trans_props.insert(prop_pair_t(GncTransPropType::ACCOUNT, static_cast<void*> (home_account)));
 
         if (loop_err)
+            /* FIXME huge memory leak here: the trans_props for this line aren't freed! */
             continue;
 
         /* If column parsing was successful, convert trans properties into a trans line. */

commit f26d3cea7d22cbeb3a1e03084a269d686b7da750
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Thu Sep 8 16:59:20 2016 +0200

    Replace private data structure TransPropertyList with a std::map
    
    This required moving around a few other parameters
    - currency_format and date_format will now be passed directly to the function that needs it
    - Account is converted into a trans_prop just like all the other columns the user had selected

diff --git a/src/import-export/csv-imp/gnc-csv-imp-trans.cpp b/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
index 8ab64ae..490237c 100644
--- a/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
+++ b/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
@@ -372,32 +372,18 @@ int GncCsvParseData::parse (gboolean guessColTypes, GError** error)
     return 0;
 }
 
-/** A struct containing TransProperties that all describe a single transaction. */
-typedef struct
-{
-    int date_format; /**< The format for parsing dates */
-    int currency_format; /**< The format for currency */
-    Account* account; /**< The account the transaction belongs to */
-    GList* properties; /**< List of TransProperties */
-} TransPropertyList;
-
 /** A struct encapsulating a property of a transaction. */
 typedef struct
 {
-    GncTransPropType type;   /**< A value from the GncTransPropType enum except
-                               * GncTransPropType::NONE */
     void* value;             /**< Pointer to the data that will be used to configure a transaction */
-    TransPropertyList* list; /**< The list the property belongs to */
 } TransProperty;
 
 /** Constructor for TransProperty.
  * @param type The type of the new property (see TransProperty.type for possible values)
  */
-static TransProperty* trans_property_new (GncTransPropType type, TransPropertyList* list)
+static TransProperty* trans_property_new (void)
 {
     TransProperty* prop = g_new (TransProperty, 1);
-    prop->type = type;
-    prop->list = list;
     prop->value = NULL;
     return prop;
 }
@@ -405,9 +391,9 @@ static TransProperty* trans_property_new (GncTransPropType type, TransPropertyLi
 /** Destructor for TransProperty.
  * @param prop The property to be freed
  */
-static void trans_property_free (TransProperty* prop)
+static void trans_property_free (TransProperty* prop, GncTransPropType type)
 {
-    switch (prop->type)
+    switch (type)
     {
         /* The types for "Date" and "Balance" (time64 and gnc_numeric,
          * respectively) are typically not pointed to, we have to free
@@ -432,17 +418,17 @@ static void trans_property_free (TransProperty* prop)
  * @param str The string to be parsed
  * @return TRUE on success, FALSE on failure
  */
-static gboolean trans_property_set (TransProperty* prop, const char* str)
+static gboolean trans_property_set (TransProperty* prop, GncTransPropType type, const char* str, int currency_format, int date_format)
 {
     char *endptr, *possible_currency_symbol, *str_dupe;
     gnc_numeric val;
     int reti;
     regex_t regex;
-    switch (prop->type)
+    switch (type)
     {
     case GncTransPropType::DATE:
         prop->value = g_new(time64, 1);
-        *((time64*)(prop->value)) = parse_date(str, prop->list->date_format);
+        *((time64*)(prop->value)) = parse_date(str, date_format);
         return *((time64*)(prop->value)) != -1;
 
     case GncTransPropType::DESCRIPTION:
@@ -453,6 +439,7 @@ static gboolean trans_property_set (TransProperty* prop, const char* str)
         prop->value = g_strdup (str);
         return TRUE;
 
+    case GncTransPropType::ACCOUNT:
     case GncTransPropType::OACCOUNT:
         prop->value = gnc_csv_account_map_search (str);
         return TRUE;
@@ -492,7 +479,7 @@ static gboolean trans_property_set (TransProperty* prop, const char* str)
         }
 
         /* Currency format */
-        switch (prop->list->currency_format)
+        switch (currency_format)
         {
         case 0:
             /* Currency locale */
@@ -531,47 +518,6 @@ static gboolean trans_property_set (TransProperty* prop, const char* str)
     return FALSE; /* We should never actually get here. */
 }
 
-/** Constructor for TransPropertyList.
- * @param account The account with which transactions should be built
- * @param date_format An index from date_format_user for how date properties should be parsed
- * @return A pointer to a new TransPropertyList
- */
-static TransPropertyList* trans_property_list_new (Account* account, int date_format, int currency_format)
-{
-    TransPropertyList* list = g_new (TransPropertyList, 1);
-    list->account = account;
-    list->date_format = date_format;
-    list->currency_format = currency_format;
-    list->properties = NULL;
-    return list;
-}
-
-/** Destructor for TransPropertyList.
- * @param list The list to be freed
- */
-static void trans_property_list_free (TransPropertyList* list)
-{
-    /* Free all of the properties in this list before freeing the list itself. */
-    GList* properties_begin = list->properties;
-    while (list->properties != NULL)
-    {
-        trans_property_free ((TransProperty*)(list->properties->data));
-        list->properties = g_list_next (list->properties);
-    }
-    g_list_free (properties_begin);
-    g_free (list);
-}
-
-/** Adds a property to the list it's linked with.
- * (The TransPropertyList is not passed as a parameter because the property is
- * associated with a list when it's constructed.)
- * @param property The property to be added to its list
- */
-static void trans_property_list_add (TransProperty* property)
-{
-    property->list->properties = g_list_append (property->list->properties, property);
-}
-
 /** Adds a split to a transaction.
  * @param trans The transaction to add a split to
  * @param account The account used for the split
@@ -609,98 +555,36 @@ static void trans_add_osplit (Transaction* trans, Account* account, QofBook* boo
 }
 
 /** Tests a TransPropertyList for having enough essential properties.
- * Essential properties are "Date" and one of the following: "Balance", "Deposit", or
- * "Withdrawal".
+ * Essential properties are
+ * - "Date"
+ * - at least one of "Balance", "Deposit", or "Withdrawal"
+ * - "Account"
+ * Note account isn't checked for here as this has been done before
  * @param list The list we are checking
  * @param error Contains an error message on failure
- * @return TRUE if there are enough essentials; FALSE otherwise
+ * @return true if there are enough essentials; false otherwise
  */
-static gboolean trans_property_list_verify_essentials (TransPropertyList* list, gchar** error)
+static bool trans_properties_verify_essentials (std::map<GncTransPropType, TransProperty* >& trans_props, gchar** error)
 {
-    int i;
-    /* possible_errors lists the ways in which a list can fail this test. */
-    enum PossibleErrorTypes {NO_DATE, NO_AMOUNT, NUM_OF_POSSIBLE_ERRORS};
-    const gchar* possible_errors[NUM_OF_POSSIBLE_ERRORS] =
-    {
-        N_("No date column."),
-        N_("No balance, deposit, or withdrawal column.")
-    };
-    int possible_error_lengths[NUM_OF_POSSIBLE_ERRORS] = {0};
-    GList *properties_begin = list->properties, *errors_list = NULL;
-
-    /* Go through each of the properties and erase possible errors. */
-    while (list->properties)
-    {
-        switch (((TransProperty*)(list->properties->data))->type)
-        {
-        case GncTransPropType::DATE:
-            possible_errors[NO_DATE] = NULL;
-            break;
-
-        case GncTransPropType::BALANCE:
-        case GncTransPropType::DEPOSIT:
-        case GncTransPropType::WITHDRAWAL:
-            possible_errors[NO_AMOUNT] = NULL;
-            break;
-        default:
-            break;
-        }
-        list->properties = g_list_next (list->properties);
-    }
-    list->properties = properties_begin;
-
-    /* Accumulate a list of the actual errors. */
-    for (i = 0; i < NUM_OF_POSSIBLE_ERRORS; i++)
+    /* Make sure this is a transaction with all the columns we need. */
+    bool have_date = (trans_props.find (GncTransPropType::DATE) != trans_props.end());
+    bool have_amount = ((trans_props.find (GncTransPropType::DEPOSIT) != trans_props.end()) ||
+                        (trans_props.find (GncTransPropType::WITHDRAWAL) != trans_props.end()) ||
+                        (trans_props.find (GncTransPropType::BALANCE) != trans_props.end()));
+
+    std::string error_message {""};
+    if (!have_date)
+        error_message += N_("No date column.");
+    if (!have_amount)
     {
-        if (possible_errors[i] != NULL)
-        {
-            errors_list = g_list_append (errors_list, GINT_TO_POINTER(i));
-            /* Since we added an error, we want to also store its length for
-             * when we construct the full error string. */
-            possible_error_lengths[i] = strlen (_(possible_errors[i]));
-        }
+        if (!have_date)
+            error_message += "\n";
+        error_message += N_("No balance, deposit, or withdrawal column.");
     }
+    if (!have_date || !have_amount)
+        *error = g_strdup (error_message.c_str());
 
-    /* If there are no errors, we can quit now. */
-    if (errors_list == NULL)
-        return TRUE;
-    else
-    {
-        /* full_error_size is the full length of the error message. */
-        int full_error_size = 0, string_length = 0;
-        GList* errors_list_begin = errors_list;
-        gchar *error_message, *error_message_begin;
-
-        /* Find the value for full_error_size. */
-        while (errors_list)
-        {
-            /* We add an extra 1 to account for spaces in between messages. */
-            full_error_size += possible_error_lengths[GPOINTER_TO_INT(errors_list->data)] + 1;
-            errors_list = g_list_next (errors_list);
-        }
-        errors_list = errors_list_begin;
-
-        /* Append the error messages one after another. */
-        error_message = error_message_begin = g_new (gchar, full_error_size);
-        while (errors_list)
-        {
-            i = GPOINTER_TO_INT(errors_list->data);
-            string_length = possible_error_lengths[i];
-
-            /* Copy the error message and put a space after it. */
-            strncpy(error_message, _(possible_errors[i]), string_length);
-            error_message += string_length;
-            *error_message = ' ';
-            error_message++;
-
-            errors_list = g_list_next (errors_list);
-        }
-        *error_message = '\0'; /* Replace the last space with the null byte. */
-        g_list_free (errors_list_begin);
-
-        *error = error_message_begin;
-        return FALSE;
-    }
+    return have_amount && have_date;
 }
 
 /** Create a Transaction from a TransPropertyList.
@@ -708,49 +592,46 @@ static gboolean trans_property_list_verify_essentials (TransPropertyList* list,
  * @param error Contains an error on failure
  * @return On success, a GncCsvTransLine; on failure, the trans pointer is NULL
  */
-static GncCsvTransLine* trans_property_list_to_trans (TransPropertyList* list, gchar** error)
+static GncCsvTransLine* trans_properties_to_trans (std::map<GncTransPropType, TransProperty* >& trans_props, gchar** error)
 {
-    GncCsvTransLine* trans_line = g_new (GncCsvTransLine, 1);
-    GList* properties_begin = list->properties;
-    QofBook* book = gnc_account_get_book (list->account);
-    gnc_commodity* currency = xaccAccountGetCommodity (list->account);
-    gnc_numeric amount = double_to_gnc_numeric (0.0, xaccAccountGetCommoditySCU (list->account),
-                         GNC_HOW_RND_ROUND_HALF_UP);
-    gchar *num = NULL;
-    gchar *memo = NULL;
-    gchar *omemo = NULL;
-    Account *oaccount = NULL;
 
-    /* This flag is set to TRUE if we can use the "Deposit" or "Withdrawal" column. */
-    gboolean amount_set = FALSE;
+    if (!trans_properties_verify_essentials(trans_props, error))
+        return NULL;
+
+    TransProperty * property = trans_props.find (GncTransPropType::ACCOUNT)->second;
+    Account* account = static_cast<Account*> (property->value);
+
+    GncCsvTransLine* trans_line = g_new (GncCsvTransLine, 1);
 
     /* The balance is 0 by default. */
-    trans_line->balance_set = FALSE;
-    trans_line->balance = amount;
-    trans_line->num = NULL;
+    trans_line->balance_set = false;
+    trans_line->balance = double_to_gnc_numeric (0.0, xaccAccountGetCommoditySCU (account),
+                          GNC_HOW_RND_ROUND_HALF_UP);
 
     /* We make the line_no -1 just to mark that it hasn't been set. We
      * may get rid of line_no soon anyway, so it's not particularly
      * important. */
     trans_line->line_no = -1;
 
-    /* Make sure this is a transaction with all the columns we need. */
-    if (!trans_property_list_verify_essentials (list, error))
-    {
-        g_free(trans_line);
-        return NULL;
-    }
-
+    QofBook* book = gnc_account_get_book (account);
+    gnc_commodity* currency = xaccAccountGetCommodity (account);
     trans_line->trans = xaccMallocTransaction (book);
     xaccTransBeginEdit (trans_line->trans);
     xaccTransSetCurrency (trans_line->trans, currency);
 
     /* Go through each of the properties and edit the transaction accordingly. */
-    list->properties = properties_begin;
-    while (list->properties != NULL)
+    gchar *num = NULL;
+    gchar *memo = NULL;
+    gchar *omemo = NULL;
+    Account *oaccount = NULL;
+    bool amount_set = false;
+    gnc_numeric amount = trans_line->balance;
+
+    for (auto prop_pair : trans_props)
     {
-        TransProperty* prop = (TransProperty*)(list->properties->data);
-        switch (prop->type)
+        GncTransPropType type = prop_pair.first;
+        TransProperty* prop = prop_pair.second;
+        switch (type)
         {
         case GncTransPropType::DATE:
             xaccTransSetDatePostedSecsNormalized (trans_line->trans, *((time64*)(prop->value)));
@@ -781,10 +662,6 @@ static GncCsvTransLine* trans_property_list_to_trans (TransPropertyList* list, g
              * 'gnc_set_num_action' is used to set tran-num and/or split-action
              * per book option */
             num = g_strdup ((char*)(prop->value));
-            /* the 'num' is also saved and used in 'gnc_csv_parse_to_trans' when
-             * it calls 'trans_add_split' after deleting the splits added below
-             * when a balance is used by the user */
-            trans_line->num = g_strdup ((char*)(prop->value));
             break;
 
         case GncTransPropType::DEPOSIT: /* Add deposits to the existing amount. */
@@ -792,11 +669,11 @@ static GncCsvTransLine* trans_property_list_to_trans (TransPropertyList* list, g
             {
                 amount = gnc_numeric_add (*((gnc_numeric*)(prop->value)),
                                          amount,
-                                         xaccAccountGetCommoditySCU (list->account),
+                                         xaccAccountGetCommoditySCU (account),
                                          GNC_HOW_RND_ROUND_HALF_UP);
-                amount_set = TRUE;
+                amount_set = true;
                 /* We will use the "Deposit" and "Withdrawal" columns in preference to "Balance". */
-                trans_line->balance_set = FALSE;
+                trans_line->balance_set = false;
             }
             break;
 
@@ -805,11 +682,11 @@ static GncCsvTransLine* trans_property_list_to_trans (TransPropertyList* list, g
             {
                 amount = gnc_numeric_add (gnc_numeric_neg(*((gnc_numeric*)(prop->value))),
                                          amount,
-                                         xaccAccountGetCommoditySCU (list->account),
+                                         xaccAccountGetCommoditySCU (account),
                                          GNC_HOW_RND_ROUND_HALF_UP);
-                amount_set = TRUE;
+                amount_set = true;
                 /* We will use the "Deposit" and "Withdrawal" columns in preference to "Balance". */
-                trans_line->balance_set = FALSE;
+                trans_line->balance_set = false;
             }
             break;
 
@@ -819,17 +696,16 @@ static GncCsvTransLine* trans_property_list_to_trans (TransPropertyList* list, g
             {
                 /* This gets put into the actual transaction at the end of gnc_csv_parse_to_trans. */
                 trans_line->balance = *((gnc_numeric*)(prop->value));
-                trans_line->balance_set = TRUE;
+                trans_line->balance_set = true;
             }
             break;
         default:
             break;
         }
-        list->properties = g_list_next (list->properties);
     }
 
     /* Add a split with the cumulative amount value. */
-    trans_add_split (trans_line->trans, list->account, book, amount, num, memo);
+    trans_add_split (trans_line->trans, account, book, amount, num, memo);
 
     if (oaccount)
         trans_add_osplit (trans_line->trans, oaccount, book, amount, num, omemo);
@@ -907,6 +783,8 @@ int GncCsvParseData::parse_to_trans (Account* account,
             orig_lines_it != orig_lines_max;
             ++orig_lines_it, odd_line = !odd_line)
     {
+        std::map<GncTransPropType, TransProperty *> trans_props;
+
         /* Skip current line if:
            1. only looking for lines with error AND no error on current line
            OR
@@ -920,9 +798,8 @@ int GncCsvParseData::parse_to_trans (Account* account,
         auto line = orig_lines_it->first;
         GncCsvTransLine* trans_line = NULL;
 
-        home_account = account;
-
         // If account = NULL, we should have an Account column
+        home_account = account;
         if (home_account == NULL)
         {
             for (uint j = 0; j < line.size(); j++)
@@ -940,47 +817,50 @@ int GncCsvParseData::parse_to_trans (Account* account,
             continue;
         }
 
-        TransPropertyList* list = trans_property_list_new (home_account, date_format, currency_format);
-
         bool loop_err = false;
         for (uint j = 0; j < line.size(); j++)
         {
-            /* We do nothing in "None" or "Account" columns. */
-            if ((column_types[j] != GncTransPropType::NONE) && (column_types[j] != GncTransPropType::ACCOUNT))
+            /* We do nothing with "None"-type columns. */
+            if (column_types[j] != GncTransPropType::NONE)
             {
                 /* Affect the transaction appropriately. */
-                TransProperty* property = trans_property_new (column_types[j], list);
-                gboolean succeeded = trans_property_set (property, line[j].c_str());
+                TransProperty* property = trans_property_new ();
+                gboolean succeeded = trans_property_set (property, column_types[j], line[j].c_str(), currency_format, date_format);
 
                 /* TODO Maybe move error handling to within TransPropertyList functions? */
                 if (succeeded)
-                    trans_property_list_add (property);
+                    trans_props.insert(std::pair<GncTransPropType, TransProperty *>(column_types[j], property));
                 else
                 {
                     parse_errors = loop_err = true;
                     gchar *error_message = g_strdup_printf (_("%s column could not be understood."),
-                                                    _(gnc_csv_col_type_strs[property->type]));
+                                                    _(gnc_csv_col_type_strs[column_types[j]]));
                     orig_lines_it->second = error_message;
 
                     g_free (error_message);
-                    trans_property_free (property);
-                    trans_property_list_free (list);
+                    trans_property_free (property, column_types[j]);
                     break;
                 }
             }
         }
+        // Add an ACCOUNT property with the home_account if no account column was set by the user
+        if (std::find (column_types.begin(), column_types.end(), GncTransPropType::ACCOUNT) == column_types.end())
+        {
+            TransProperty* property = trans_property_new ();
+            property->value = home_account;
+        }
+
         if (loop_err)
             continue;
 
         /* If column parsing was successful, convert trans properties into a trans line. */
         gchar *error_message = NULL;
-        trans_line = trans_property_list_to_trans (list, &error_message);
+        trans_line = trans_properties_to_trans (trans_props, &error_message);
         if (trans_line == NULL)
         {
             parse_errors = true;
             orig_lines_it->second = error_message;
             g_free (error_message);
-            trans_property_list_free (list);
             continue;
         }
 
@@ -1068,9 +948,6 @@ int GncCsvParseData::parse_to_trans (Account* account,
                     xaccSplitSetValue (split, gnc_numeric_neg (amount));
                 }
 
-                if (trans_line->num)
-                    g_free (trans_line->num);
-
                 /* This new transaction needs to be added to the balance offset. */
                 balance_offset = gnc_numeric_add (balance_offset,
                                                  amount,
diff --git a/src/import-export/csv-imp/gnc-csv-imp-trans.hpp b/src/import-export/csv-imp/gnc-csv-imp-trans.hpp
index 5498c82..a432f20 100644
--- a/src/import-export/csv-imp/gnc-csv-imp-trans.hpp
+++ b/src/import-export/csv-imp/gnc-csv-imp-trans.hpp
@@ -96,8 +96,7 @@ typedef struct
     int line_no;
     Transaction* trans;
     gnc_numeric balance;  /**< The (supposed) balance after this transaction takes place */
-    gboolean balance_set; /**< TRUE if balance has been set from user data, FALSE otherwise */
-    gchar *num;           /**< Saves the 'num'for use if balance has been set from user data */
+    bool balance_set;     /**< true if balance has been set from user data, false otherwise */
 } GncCsvTransLine;
 
 /* A set of currency formats that the user sees. */

commit 38b0b356e4f1f731af790ade0407f267406a109a
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Tue Jul 5 16:46:39 2016 +0200

    Remove fake csv parsing test

diff --git a/src/import-export/csv-imp/gnc-csv-tokenizer.cpp b/src/import-export/csv-imp/gnc-csv-tokenizer.cpp
index 79e2319..b2d7063 100644
--- a/src/import-export/csv-imp/gnc-csv-tokenizer.cpp
+++ b/src/import-export/csv-imp/gnc-csv-tokenizer.cpp
@@ -62,7 +62,7 @@ int GncCsvTokenizer::tokenize()
 
         // example checking
         // for correctly parsed 3 fields per record
-        if (vec.size() < 3) continue;
+        // if (vec.size() < 3) continue;
 
         tokenized_contents.push_back(vec);
     }

commit e6d9a614adc8713f924ead9be55a34e153bed360
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Tue Jul 5 16:46:19 2016 +0200

    Add some polish to csv_import_trans_file_chooser_confirm_cb
    
    - avoid superfluous string copying
    - flatten the nested if structures
    - set a default file format before attempting to parse

diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index a51774b9..33bb231 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -673,75 +673,64 @@ csv_import_trans_file_chooser_confirm_cb (GtkWidget *button, CsvImportTrans *inf
     GtkAssistant *assistant = GTK_ASSISTANT(info->window);
     gint num = gtk_assistant_get_current_page (assistant);
     GtkWidget *page = gtk_assistant_get_nth_page (assistant, num);
-    GError* error;
-    gchar *file_name;
+    GError* error = NULL;
 
     gtk_assistant_set_page_complete (assistant, page, FALSE);
 
-    file_name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(info->file_chooser));
-
-    if (file_name)
-    {
-        gchar *filepath = gnc_uri_get_path (file_name);
-        gchar *filedir = g_path_get_dirname (filepath);
-        if (info->starting_dir)
-            g_free (info->starting_dir);
-        info->starting_dir = g_strdup (filedir);
-        g_free (filedir);
-        g_free (filepath);
-
-        if (info->file_name)
-            g_free (info->file_name);
-        info->file_name = g_strdup (file_name);
-        error = NULL;
-        /* Load the file into parse_data. */
-        auto parse_data = new GncCsvParseData;
-        // FIXME Filetype isn't set yet !?
-        if (parse_data->load_file (file_name, &error))
-        {
-            /* If we couldn't load the file ... */
-            gnc_error_dialog (NULL, "%s", error->message);
-            if (error->code == GNC_CSV_IMP_ERROR_OPEN)
-            {
-                g_free (file_name);
-                delete parse_data;
-                return;
-            }
-            /* If we couldn't guess the encoding, we are content with just
-             * displaying an error message and move on with a blank
-             * display. */
-        }
-        else
+    gchar *file_name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(info->file_chooser));
+    if (!file_name)
+        return;
+
+    gchar *filepath = gnc_uri_get_path (file_name);
+    if (info->starting_dir)
+        g_free (info->starting_dir);
+    info->starting_dir = g_path_get_dirname (filepath);
+    g_free (filepath);
+
+    if (info->file_name)
+        g_free (info->file_name);
+    info->file_name = file_name;
+
+    DEBUG("file_name selected is %s", info->file_name);
+    DEBUG("starting directory is %s", info->starting_dir);
+
+    /* Load the file into parse_data. */
+    auto parse_data = new GncCsvParseData;
+    /* Assume data is CSV. User can later override to Fixed Width if needed */
+    parse_data->file_format (GncImpFileFormat::CSV, &error);
+    if (parse_data->load_file (file_name, &error))
+    {
+        /* If we couldn't load the file ... */
+        gnc_error_dialog (NULL, "%s", error->message);
+        if (error->code == GNC_CSV_IMP_ERROR_OPEN)
         {
-            /* Parse the data. */
-            if (parse_data->parse (TRUE, &error))
-            {
-                /* If we couldn't parse the data ... */
-                gnc_error_dialog (NULL, "%s", error->message);
-                delete parse_data;
-            }
-            else
-            {
-                if (info->parse_data) // Free parse_data if we have come back here
-                {
-                    delete info->parse_data;
-                    gnc_csv_reset_preview_setting (info, TRUE);
-                }
-                info->parse_data = parse_data;
-                info->previewing_errors = FALSE; /* We're looking at all the data. */
-                info->skip_errors = FALSE; // Set skip_errors to False
-                gtk_assistant_set_page_complete (assistant, page, TRUE);
-            }
+            delete parse_data;
+            return;
         }
+        /* If we couldn't guess the encoding, we are content with just
+         * displaying an error message and move on with a blank
+         * display. */
     }
-    g_free (file_name);
 
-    DEBUG("file_name selected is %s", info->file_name);
-    DEBUG("starting directory is %s", info->starting_dir);
+    /* Parse the data. */
+    if (parse_data->parse (TRUE, &error))
+    {
+        /* If we couldn't parse the data ... */
+        gnc_error_dialog (NULL, "%s", error->message);
+        delete parse_data;
+        return;
+    }
 
-    /* Step to next page if page is complete */
-    if (gtk_assistant_get_page_complete (assistant, page))
-        gtk_assistant_set_current_page (assistant, num + 1);
+    if (info->parse_data) // Free parse_data if we have come back here
+    {
+        delete info->parse_data;
+        gnc_csv_reset_preview_setting (info, TRUE);
+    }
+    info->parse_data = parse_data;
+    info->previewing_errors = FALSE; /* We're looking at all the data. */
+    info->skip_errors = FALSE; // Set skip_errors to False
+    gtk_assistant_set_page_complete (assistant, page, TRUE);
+    gtk_assistant_set_current_page (assistant, num + 1);
 }
 
 

commit 8f9b3d322ae5f6858ffa7d2d5090dc5169be517a
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sat Jun 25 19:00:20 2016 +0200

    Prevent c++ name mangling on functions used as callbacks by gtk

diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index 5caa19e..a51774b9 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -156,21 +156,12 @@ typedef struct
 
 /*************************************************************************/
 
+extern "C"
+{
 void csv_import_trans_assistant_prepare (GtkAssistant  *assistant, GtkWidget *page, gpointer user_data);
 void csv_import_trans_assistant_finish (GtkAssistant *gtkassistant, gpointer user_data);
 void csv_import_trans_assistant_cancel (GtkAssistant *gtkassistant, gpointer user_data);
 void csv_import_trans_assistant_close (GtkAssistant *gtkassistant, gpointer user_data);
-
-void csv_import_trans_assistant_start_page_prepare (GtkAssistant *gtkassistant, gpointer user_data);
-void csv_import_trans_assistant_file_page_prepare (GtkAssistant *assistant, gpointer user_data);
-void csv_import_trans_assistant_preview_page_prepare (GtkAssistant *gtkassistant, gpointer user_data);
-void csv_import_trans_assistant_account_page_prepare (GtkAssistant *assistant, gpointer user_data);
-void csv_import_trans_assistant_account_match_page_prepare (GtkAssistant *assistant, gpointer user_data);
-void csv_import_trans_assistant_doc_page_prepare (GtkAssistant *assistant, gpointer user_data);
-void csv_import_trans_assistant_match_page_prepare (GtkAssistant *assistant, gpointer user_data);
-void csv_import_trans_assistant_finish_page_prepare (GtkAssistant *assistant, gpointer user_data);
-void csv_import_trans_assistant_summary_page_prepare (GtkAssistant *assistant, gpointer user_data);
-
 void csv_import_trans_srow_cb (GtkWidget *spin, gpointer user_data);
 void csv_import_trans_erow_cb (GtkWidget *spin, gpointer user_data);
 void csv_import_trans_skip_errors_cb (GtkWidget *cb, gpointer user_data);
@@ -181,9 +172,20 @@ void csv_import_trans_file_chooser_confirm_cb (GtkWidget *button, CsvImportTrans
 void csv_import_trans_delete_settings_cb (GtkWidget *button, CsvImportTrans *info);
 void csv_import_trans_save_settings_cb (GtkWidget *button, CsvImportTrans *info);
 void csv_import_trans_changed_settings_cb (GtkWidget *button, CsvImportTrans *info);
+void sep_button_clicked (GtkWidget* widget, CsvImportTrans* info);
+}
+
+void csv_import_trans_assistant_start_page_prepare (GtkAssistant *gtkassistant, gpointer user_data);
+void csv_import_trans_assistant_file_page_prepare (GtkAssistant *assistant, gpointer user_data);
+void csv_import_trans_assistant_preview_page_prepare (GtkAssistant *gtkassistant, gpointer user_data);
+void csv_import_trans_assistant_account_page_prepare (GtkAssistant *assistant, gpointer user_data);
+void csv_import_trans_assistant_account_match_page_prepare (GtkAssistant *assistant, gpointer user_data);
+void csv_import_trans_assistant_doc_page_prepare (GtkAssistant *assistant, gpointer user_data);
+void csv_import_trans_assistant_match_page_prepare (GtkAssistant *assistant, gpointer user_data);
+void csv_import_trans_assistant_summary_page_prepare (GtkAssistant *assistant, gpointer user_data);
+
 void csv_import_trans_load_settings (CsvImportTrans *info);
 
-void sep_button_clicked (GtkWidget* widget, CsvImportTrans* info);
 static void gnc_csv_preview_update_assist (CsvImportTrans* info);
 void gnc_csv_reset_preview_setting (CsvImportTrans* info, gboolean block);
 gboolean preview_settings_valid (CsvImportTrans *info);

commit 5c9f9059a150bd81bc2cdbf8213cc0ff0d854cb2
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sat Jun 25 18:21:23 2016 +0200

    csv-imp - use enum instead of strings to track user selected column types

diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index 620c9a6..5caa19e 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -310,35 +310,36 @@ csv_import_trans_load_settings (CsvImportTrans *info)
 
             columns = g_strsplit (info->settings_data->column_types, ",", -1);
 
-            // store contains the actual strings appearing in the column types treeview.
+            // store contains the column types and their (translated) string representation appearing in the column types treeview.
             store = gtk_tree_view_get_model (info->ctreeview);
 
             // Get an iterator for the first (and only) row.
             gtk_tree_model_get_iter_first (store, &iter);
 
-            // Even Entries are the column types / names
             for (i=0; columns[i] != NULL; i++)
             {
-                int s = i * 2 + 1;
-                gboolean found = FALSE;
+                auto col_type = GncTransPropType::NONE;
+                int saved_col_type = atoi (columns[i]);
 
-                for (auto col_type : gnc_csv_col_type_strs )
+                if (saved_col_type >= static_cast<int>(GncTransPropType::NONE) &&
+                    saved_col_type <= static_cast<int>(GncTransPropType::OMEMO))
                 {
-                    // Check to see if column type is valid
-                    if (g_strcmp0 (columns[i], col_type.second) == 0)
-                    {
-                        info->parse_data->column_types.at(i) = col_type.first;
-                        /* Get the type string first. (store is arranged so that every two
-                        * columns is a pair of the model used for the combobox and the
-                        * string that appears, so that store looks like:
-                        * model 0, string 0, model 1, string 1, ..., model ncols, string ncols. */
-                        if (s < gtk_tree_model_get_n_columns (store))
-                            gtk_list_store_set (GTK_LIST_STORE(store), &iter, s, columns[i], -1);
-                        found = TRUE;
-                        break;
-                    }
+                    col_type = static_cast<GncTransPropType>(saved_col_type);
+                    info->parse_data->column_types.at(i) = col_type;
+                    /* Set the column type. Store is arranged so that every three
+                     * columns is a triplet of
+                     * - model used for the combobox
+                     * - the column type as a user visible (translated) string
+                     * - the internal type for this column
+                     * So store looks like:
+                     * model 0, col_type_str 0, col_type, model 1, col_type_str 1, col_type 1, ..., model ncols, col_type_str ncols, col_type ncols. */
+                    if ((3 * i + 2) < gtk_tree_model_get_n_columns (store))
+                        gtk_list_store_set (GTK_LIST_STORE(store), &iter,
+                                3 * i + 1, _(gnc_csv_col_type_strs[col_type]),
+                                3 * i + 2, col_type,
+                                -1);
                 }
-                if (!found)
+                else
                     error = TRUE;
             }
             if (error)
@@ -566,25 +567,30 @@ csv_import_trans_save_settings_cb (GtkWidget *button, CsvImportTrans *info)
         // Get an iterator for the first (and only) row.
         gtk_tree_model_get_iter_first (store, &iter);
 
-        for (column = columns, i = 1; column; column = g_list_next (column), i = i + 2)
+        for (column = columns, i = 2; column; column = g_list_next (column), i += 3)
         {
-            gchar *contents = NULL;
-
-            /* Get the type string first. (store is arranged so that every two
-            * columns is a pair of the model used for the combobox and the
-            * string that appears, so that store looks like:
-            * model 0, string 0, model 1, string 1, ..., model ncols, string ncols. */
-            gtk_tree_model_get (store, &iter, i, &contents, -1);
-
+            auto col_type = GncTransPropType::NONE;
+            gchar *col_type_str = NULL;
+
+            /* Get the column type. Store is arranged so that every three
+             * columns is a triplet of
+             * - model used for the combobox
+             * - the column type as a user visible (translated) string
+             * - the internal type for this column
+             * So store looks like:
+             * model 0, col_type_str 0, col_type, model 1, col_type_str 1, col_type 1, ..., model ncols, col_type_str ncols, col_type ncols. */
+            gtk_tree_model_get (store, &iter, i, &col_type, -1);
+
+            col_type_str = g_strdup_printf ("%i", static_cast<int>(col_type));
             if (!details)
-                details = g_strdup (contents);
+                details = col_type_str;
             else
             {
                 gchar *details_prev = details;
-                details = g_strjoin (",", details_prev, contents, NULL);
+                details = g_strjoin (",", details_prev, col_type_str, NULL);
                 g_free (details_prev);
+                g_free (col_type_str);
             }
-            g_free (contents);
         }
         g_list_free (columns);
 
@@ -1314,10 +1320,14 @@ static void column_type_changed (GtkCellRenderer* renderer, gchar* path,
     gint textColumn;
     GtkTreeIter iter;
     gchar* new_text;
+    auto new_col_type = GncTransPropType::NONE;
 
     /* Get the new text */
     g_object_get (renderer, "model", &model, "text-column", &textColumn, NULL);
-    gtk_tree_model_get (model, new_text_iter, textColumn, &new_text, -1);
+    gtk_tree_model_get (model, new_text_iter,
+            textColumn, &new_text,
+            1, &new_col_type,            // Invisible column in the combobox' model containing the colum type
+            -1);
 
     /* Get an iterator for the first (and only) row. */
     gtk_tree_model_get_iter_first (store, &iter);
@@ -1338,28 +1348,35 @@ static void column_type_changed (GtkCellRenderer* renderer, gchar* path,
         /* If this is not the column that was changed ... */
         if (col_renderer != renderer)
         {
-            /* The string that appears in the column */
-            gchar* contents = NULL;
-            /* Get the type string. (store is arranged so that every two
-             * columns is a pair of the model used for the combobox and the
-             * string that appears, so that store looks like:
-             * model 0, string 0, model 1, string 1, ..., model ncols, string ncols. */
-            gtk_tree_model_get(store, &iter, 2 * i + 1, &contents, -1);
-            /* If this column has the same string that the user selected ... */
-            if (!g_strcmp0 (contents, new_text))
+            /* The data type of this column */
+            auto cur_col_type = GncTransPropType::NONE;
+            /* Get the column type. Store is arranged so that every three
+             * columns is a triplet of
+             * - model used for the combobox
+             * - the column type as a user visible (translated) string
+             * - the internal type for this column
+             * So store looks like:
+             * model 0, col_type_str 0, col_type, model 1, col_type_str 1, col_type 1, ..., model ncols, col_type_str ncols, col_type ncols. */
+            gtk_tree_model_get(store, &iter, 3 * i + 2, &cur_col_type, -1);
+            /* If this column has the same type as the user selected ... */
+            if (cur_col_type == new_col_type)
             {
                 /* ... set this column to the "None" type. (We can't allow duplicate types.) */
-                gtk_list_store_set (GTK_LIST_STORE(store), &iter, 2 * i + 1,
-                                   _(gnc_csv_col_type_strs[GncTransPropType::NONE]), -1);
+                gtk_list_store_set (GTK_LIST_STORE(store), &iter,
+                        3 * i + 1, _(gnc_csv_col_type_strs[GncTransPropType::NONE]),
+                        3 * i + 2, GncTransPropType::NONE,
+                        -1);
             }
-            g_free (contents);
         }
         else /* If this is the column that was changed ... */
         {
-            /* Set the text for this column to what the user selected. (See
-             * comment above "Get the type string. ..." for why we set
-             * column 2*i+1 in store.) */
-            gtk_list_store_set (GTK_LIST_STORE(store), &iter, 2 * i + 1, new_text, -1);
+            /* Set the type for this column to what the user selected. (See
+             * comment above "Get the column type. ..." for why we set
+             * column 3*i+1 in store.) */
+            gtk_list_store_set (GTK_LIST_STORE(store), &iter,
+                    3 * i + 1, new_text,
+                    3 * i + 2, new_col_type,
+                    -1);
         }
     }
 }
@@ -1458,70 +1475,64 @@ gboolean preview_settings_valid (CsvImportTrans* info)
     /* Go through each of the columns. */
     for (i = 0; i < ncols; i++)
     {
-        gchar* contents = NULL; /* The column type string in this column. */
         gchar* prevstr = NULL; /* The string in this column from datastore. */
-        /* Get the type string first. (store is arranged so that every two
-         * columns is a pair of the model used for the combobox and the
-         * string that appears, so that store looks like:
-         * model 0, string 0, model 1, string 1, ..., model ncols, string ncols. */
-        gtk_tree_model_get (store, &iter1, 2 * i + 1, &contents, -1);
-
-        /* Go through each column type until ... */
-        for (auto col_type : gnc_csv_col_type_strs)
+        auto col_type = GncTransPropType::NONE;
+        /* Get the column type. Store is arranged so that every three
+         * columns is a triplet of
+         * - model used for the combobox
+         * - the column type as a user visible (translated) string
+         * - the internal type for this column
+         * So store looks like:
+         * model 0, col_type_str 0, col_type, model 1, col_type_str 1, col_type 1, ..., model ncols, col_type_str ncols, col_type ncols. */
+        gtk_tree_model_get (store, &iter1, 3 * i + 2, &col_type, -1);
+
+        /* Set the column_types array appropriately*/
+        info->parse_data->column_types[i] = col_type;
+
+        switch (col_type)
         {
-            /* ... we find one that matches with what's in the column. */
-            if (!g_strcmp0 (contents, _(col_type.second)))
-            {
-                /* Set the column_types array appropriately and quit. */
-                info->parse_data->column_types[i] = col_type.first;
+        case GncTransPropType::DATE:
+            weight = weight + 1000;
+            gtk_tree_model_get (datastore, &iter2, i + 1, &prevstr, -1);
 
-                switch (col_type.first)
-                {
-                case GncTransPropType::DATE:
-                    weight = weight + 1000;
-                    gtk_tree_model_get (datastore, &iter2, i + 1, &prevstr, -1);
-
-                    if (parse_date (prevstr, info->parse_data->date_format) == -1)
-                        valid = FALSE;
-                    break;
-
-                case GncTransPropType::DESCRIPTION:
-                    weight = weight + 100;
-                    break;
-
-                case GncTransPropType::BALANCE:
-                    havebalance = TRUE;
-                    /* No break */
-                case GncTransPropType::DEPOSIT:
-                case GncTransPropType::WITHDRAWAL:
-                    weight = weight + 10;
-                    break;
-
-                case GncTransPropType::NUM:
-                case GncTransPropType::NOTES:
-                case GncTransPropType::MEMO:
-                    weight = weight + 1;
-                    break;
-
-                case GncTransPropType::ACCOUNT:
-                    weight = weight + 1;
-                    break;
-
-                case GncTransPropType::OACCOUNT:
-                    oweight = oweight + 100;
-                    break;
-
-                case GncTransPropType::OMEMO:
-                    oweight = oweight + 1;
-                    break;
-                default:
-                    break;
-                }
-                break;
-            }
+            if (parse_date (prevstr, info->parse_data->date_format) == -1)
+                valid = FALSE;
+            break;
+
+        case GncTransPropType::DESCRIPTION:
+            weight = weight + 100;
+            break;
+
+        case GncTransPropType::BALANCE:
+            havebalance = TRUE;
+            /* No break */
+        case GncTransPropType::DEPOSIT:
+        case GncTransPropType::WITHDRAWAL:
+            weight = weight + 10;
+            break;
+
+        case GncTransPropType::NUM:
+        case GncTransPropType::NOTES:
+        case GncTransPropType::MEMO:
+            weight = weight + 1;
+            break;
+
+        case GncTransPropType::ACCOUNT:
+            weight = weight + 1;
+            break;
+
+        case GncTransPropType::OACCOUNT:
+            oweight = oweight + 100;
+            break;
+
+        case GncTransPropType::OMEMO:
+            oweight = oweight + 1;
+            break;
+        default:
+            break;
         }
+
         /* Free the type string created by gtk_tree_model_get() */
-        g_free (contents);
         g_free (prevstr);
     }
 
@@ -1615,21 +1626,19 @@ gboolean get_list_of_accounts (CsvImportTrans* info, GtkTreeModel *store)
         /* Go through each of the columns. */
         for (i = 0; i < ncols; i++)
         {
-            gchar* contents = NULL; /* The column type string in this column. */
             gchar* accstr = NULL;   /* The string in this column from datastore. */
+            auto col_type = GncTransPropType::NONE;
 
-            /* Get the type string first. (store is arranged so that every two
-             * columns is a pair of the model used for the combobox and the
-             * string that appears, so that store looks like:
-             * model 0, string 0, model 1, string 1, ..., model ncols, string ncols. */
-            gtk_tree_model_get (columnstore, &iter1, 2 * i + 1, &contents, -1);
+            /* Get the column type. Store is arranged so that every three
+             * columns is a triplet of
+             * - model used for the combobox
+             * - the column type as a user visible (translated) string
+             * - the internal type for this column
+             * So store looks like:
+             * model 0, col_type_str 0, col_type, model 1, col_type_str 1, col_type 1, ..., model ncols, col_type_str ncols, col_type ncols. */
+            gtk_tree_model_get (columnstore, &iter1, 3 * i + 2, &col_type, -1);
 
             /* We're only interested in columns of type ACCOUNT and OACCOUNT. */
-            auto col_type = GncTransPropType::NONE;
-            if (!g_strcmp0 (contents, _(gnc_csv_col_type_strs[GncTransPropType::ACCOUNT])))
-                col_type = GncTransPropType::ACCOUNT;
-            else if(!g_strcmp0 (contents, _(gnc_csv_col_type_strs[GncTransPropType::OACCOUNT])))
-                col_type = GncTransPropType::OACCOUNT;
             if ((col_type == GncTransPropType::ACCOUNT) || (col_type == GncTransPropType::OACCOUNT))
             {
                 /* Get an iterator for the row in the data store. */
@@ -1653,8 +1662,6 @@ gboolean get_list_of_accounts (CsvImportTrans* info, GtkTreeModel *store)
                     g_free (accstr);
                 }
             }
-            /* Free the type string created by gtk_tree_model_get() */
-            g_free (contents);
         }
     }
     info->home_account_number = home_account_number;
@@ -1682,19 +1689,25 @@ static void gnc_csv_preview_update_assist (CsvImportTrans* info)
     guint i, ncols = info->parse_data->column_types.size();
 
     /* store contains only strings. */
-    GType* types = g_new (GType, 2 * ncols);
+    GType* types = g_new (GType, 3 * ncols);
     for (i = 0; i <  ncols + 1; i++)
         types[i] = G_TYPE_STRING;
     store = gtk_list_store_newv (ncols + 1, types);
 
-    /* ctstore is arranged as follows:
-     * model 0, text 0, model 1, text 1, ..., model ncols, text ncols. */
-    for (i = 0; i < 2 * ncols; i += 2)
+    /* ctstore is arranged so that every three
+     * columns is a triplet of
+     * - model used for the combobox
+     * - the column type as a user visible (translated) string
+     * - the internal type for this column
+     * So store looks like:
+     * model 0, col_type_str 0, col_type, model 1, col_type_str 1, col_type 1, ..., model ncols, col_type_str ncols, col_type ncols. */
+    for (i = 0; i < 3 * ncols; i += 3)
     {
         types[i] = GTK_TYPE_TREE_MODEL;
         types[i+1] = G_TYPE_STRING;
+        types[i+2] = G_TYPE_INT;
     }
-    ctstore = gtk_list_store_newv (2 * ncols, types);
+    ctstore = gtk_list_store_newv (3 * ncols, types);
 
     g_free (types);
 
@@ -1702,12 +1715,14 @@ static void gnc_csv_preview_update_assist (CsvImportTrans* info)
     cstores = g_new (GtkListStore*, ncols);
     for (i = 0; i < ncols; i++)
     {
-        cstores[i] = gtk_list_store_new (1, G_TYPE_STRING);
+        cstores[i] = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
         /* Add all of the possible entries to the combo box. */
         for (auto col_type : gnc_csv_col_type_strs)
         {
             gtk_list_store_append (cstores[i], &iter);
-            gtk_list_store_set (cstores[i], &iter, 0, _(col_type.second), -1);
+            gtk_list_store_set (cstores[i], &iter, 0, _(col_type.second),
+                                                   1, static_cast<int>(col_type.first),
+                                                   -1);
         }
     }
 
@@ -1760,12 +1775,13 @@ static void gnc_csv_preview_update_assist (CsvImportTrans* info)
 
     /* Set all the column types to what's in the parse data. */
     gtk_list_store_append (ctstore, &iter);
-    gtk_list_store_set (ctstore, &iter, 0, NULL, -1); /* Dummy Column to match row color */
     for (i = 0; i < ncols; i++)
     {
-        gtk_list_store_set (ctstore, &iter, 2 * i, cstores[i], 2 * i + 1,
-                           _(gnc_csv_col_type_strs[info->parse_data->column_types[i]]),
-                           -1);
+        gtk_list_store_set (ctstore, &iter,
+                3 * i, cstores[i],
+                3 * i + 1, _(gnc_csv_col_type_strs[info->parse_data->column_types[i]]),
+                3 * i + 2, static_cast<int>(info->parse_data->column_types[i]),
+                -1);
     }
 
     info->treeview_buttons = g_new (GtkWidget*, ncols);
@@ -1799,8 +1815,8 @@ static void gnc_csv_preview_update_assist (CsvImportTrans* info)
         /* Use the alternating model and text entries from ctstore in
          * info->ctreeview. */
         gtk_tree_view_insert_column_with_attributes (info->ctreeview,
-                -1, "", crenderer, "model", 2 * i,
-                "text", 2 * i + 1, NULL);
+                -1, "", crenderer, "model", 3 * i,
+                "text", 3 * i + 1, NULL);
 
         /* We need to allow clicking on the column headers for fixed-width
          * column splitting and merging. */

commit 0b73a56c295a0cde3ad21ad17eeeb63fe6b59c3b
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sun Jun 12 20:00:52 2016 +0200

    Convert column type to name mapping into a std::map

diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index e68410c..620c9a6 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -322,16 +322,12 @@ csv_import_trans_load_settings (CsvImportTrans *info)
                 int s = i * 2 + 1;
                 gboolean found = FALSE;
 
-                for (auto t = GncTransPropType::NONE;
-                          t != GncTransPropType::NUM_COL_TYPES;
-                          static_cast<GncTransPropType>(static_cast<int>(t) + 1))
+                for (auto col_type : gnc_csv_col_type_strs )
                 {
-                    const gchar *type = gnc_csv_column_type_strs[static_cast<int>(t)];
-
                     // Check to see if column type is valid
-                    if (g_strcmp0 (type, columns[i]) == 0)
+                    if (g_strcmp0 (columns[i], col_type.second) == 0)
                     {
-                        info->parse_data->column_types.at(i) = t;
+                        info->parse_data->column_types.at(i) = col_type.first;
                         /* Get the type string first. (store is arranged so that every two
                         * columns is a pair of the model used for the combobox and the
                         * string that appears, so that store looks like:
@@ -339,6 +335,7 @@ csv_import_trans_load_settings (CsvImportTrans *info)
                         if (s < gtk_tree_model_get_n_columns (store))
                             gtk_list_store_set (GTK_LIST_STORE(store), &iter, s, columns[i], -1);
                         found = TRUE;
+                        break;
                     }
                 }
                 if (!found)
@@ -1353,7 +1350,7 @@ static void column_type_changed (GtkCellRenderer* renderer, gchar* path,
             {
                 /* ... set this column to the "None" type. (We can't allow duplicate types.) */
                 gtk_list_store_set (GTK_LIST_STORE(store), &iter, 2 * i + 1,
-                                   _(gnc_csv_column_type_strs[static_cast<int>(GncTransPropType::NONE)]), -1);
+                                   _(gnc_csv_col_type_strs[GncTransPropType::NONE]), -1);
             }
             g_free (contents);
         }
@@ -1470,17 +1467,15 @@ gboolean preview_settings_valid (CsvImportTrans* info)
         gtk_tree_model_get (store, &iter1, 2 * i + 1, &contents, -1);
 
         /* Go through each column type until ... */
-        for (auto col_type = GncTransPropType::NONE;
-                  col_type != GncTransPropType::NUM_COL_TYPES;
-                  col_type = static_cast<GncTransPropType>(static_cast<int>(col_type) + 1))
+        for (auto col_type : gnc_csv_col_type_strs)
         {
             /* ... we find one that matches with what's in the column. */
-            if (!g_strcmp0 (contents, _(gnc_csv_column_type_strs[static_cast<int>(col_type)])))
+            if (!g_strcmp0 (contents, _(col_type.second)))
             {
                 /* Set the column_types array appropriately and quit. */
-                info->parse_data->column_types[i] = col_type;
+                info->parse_data->column_types[i] = col_type.first;
 
-                switch (col_type)
+                switch (col_type.first)
                 {
                 case GncTransPropType::DATE:
                     weight = weight + 1000;
@@ -1631,9 +1626,9 @@ gboolean get_list_of_accounts (CsvImportTrans* info, GtkTreeModel *store)
 
             /* We're only interested in columns of type ACCOUNT and OACCOUNT. */
             auto col_type = GncTransPropType::NONE;
-            if (!g_strcmp0 (contents, _(gnc_csv_column_type_strs[static_cast<int>(GncTransPropType::ACCOUNT)])))
+            if (!g_strcmp0 (contents, _(gnc_csv_col_type_strs[GncTransPropType::ACCOUNT])))
                 col_type = GncTransPropType::ACCOUNT;
-            else if(!g_strcmp0 (contents, _(gnc_csv_column_type_strs[static_cast<int>(GncTransPropType::OACCOUNT)])))
+            else if(!g_strcmp0 (contents, _(gnc_csv_col_type_strs[GncTransPropType::OACCOUNT])))
                 col_type = GncTransPropType::OACCOUNT;
             if ((col_type == GncTransPropType::ACCOUNT) || (col_type == GncTransPropType::OACCOUNT))
             {
@@ -1684,7 +1679,7 @@ static void gnc_csv_preview_update_assist (CsvImportTrans* info)
     GtkTreeIter iter;
     GtkTreeSelection *selection;
     /* ncols is the number of columns in the file data. */
-    guint i, j, ncols = info->parse_data->column_types.size();
+    guint i, ncols = info->parse_data->column_types.size();
 
     /* store contains only strings. */
     GType* types = g_new (GType, 2 * ncols);
@@ -1709,10 +1704,10 @@ static void gnc_csv_preview_update_assist (CsvImportTrans* info)
     {
         cstores[i] = gtk_list_store_new (1, G_TYPE_STRING);
         /* Add all of the possible entries to the combo box. */
-        for (j = 0; j < static_cast<int>(GncTransPropType::NUM_COL_TYPES); j++)
+        for (auto col_type : gnc_csv_col_type_strs)
         {
             gtk_list_store_append (cstores[i], &iter);
-            gtk_list_store_set (cstores[i], &iter, 0, _(gnc_csv_column_type_strs[j]), -1);
+            gtk_list_store_set (cstores[i], &iter, 0, _(col_type.second), -1);
         }
     }
 
@@ -1769,7 +1764,7 @@ static void gnc_csv_preview_update_assist (CsvImportTrans* info)
     for (i = 0; i < ncols; i++)
     {
         gtk_list_store_set (ctstore, &iter, 2 * i, cstores[i], 2 * i + 1,
-                           _(gnc_csv_column_type_strs[(int)(info->parse_data->column_types[i])]),
+                           _(gnc_csv_col_type_strs[info->parse_data->column_types[i]]),
                            -1);
     }
 
diff --git a/src/import-export/csv-imp/gnc-csv-imp-trans.cpp b/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
index 8a2b24d..8ab64ae 100644
--- a/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
+++ b/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
@@ -20,6 +20,8 @@
  *                                                                  *
 \********************************************************************/
 
+#include <guid.hpp>
+
 extern "C" {
 #include <platform.h>
 #if PLATFORM(WINDOWS)
@@ -124,20 +126,20 @@ const char* date_regex[] = {
 //                                       N_("Comma: 123.456,78")
 //                                      };
 //
-/* This array contains all of the different strings for different column types. */
-const gchar* gnc_csv_col_type_strs[GncTransPropType::NUM_COL_TYPES] = {
-        N_("None"),
-        N_("Date"),
-        N_("Num"),
-        N_("Description"),
-        N_("Notes"),
-        N_("Account"),
-        N_("Deposit"),
-        N_("Withdrawal"),
-        N_("Balance"),
-        N_("Memo"),
-        N_("Other Account"),
-        N_("Other Memo")
+/* This map contains a set of strings representing the different column types. */
+std::map<GncTransPropType, const char*> gnc_csv_col_type_strs = {
+        { GncTransPropType::NONE, N_("None") },
+        { GncTransPropType::DATE, N_("Date") },
+        { GncTransPropType::NUM, N_("Num") },
+        { GncTransPropType::DESCRIPTION, N_("Description") },
+        { GncTransPropType::NOTES, N_("Notes") },
+        { GncTransPropType::ACCOUNT, N_("Account") },
+        { GncTransPropType::DEPOSIT, N_("Deposit") },
+        { GncTransPropType::WITHDRAWAL, N_("Withdrawal") },
+        { GncTransPropType::BALANCE, N_("Balance") },
+        { GncTransPropType::MEMO, N_("Memo") },
+        { GncTransPropType::OACCOUNT, N_("Other Account") },
+        { GncTransPropType::OMEMO, N_("Other Memo") }
 };
 
 /** Parses a string into a date, given a format. This function
@@ -383,7 +385,7 @@ typedef struct
 typedef struct
 {
     GncTransPropType type;   /**< A value from the GncTransPropType enum except
-                               * GncTransPropType::NONE and GncTransPropType::NUM_COL_TYPES */
+                               * GncTransPropType::NONE */
     void* value;             /**< Pointer to the data that will be used to configure a transaction */
     TransPropertyList* list; /**< The list the property belongs to */
 } TransProperty;
diff --git a/src/import-export/csv-imp/gnc-csv-imp-trans.hpp b/src/import-export/csv-imp/gnc-csv-imp-trans.hpp
index f6e8266..5498c82 100644
--- a/src/import-export/csv-imp/gnc-csv-imp-trans.hpp
+++ b/src/import-export/csv-imp/gnc-csv-imp-trans.hpp
@@ -37,6 +37,7 @@ extern "C" {
 }
 
 #include <vector>
+#include <map>
 #include <memory>
 
 #include "gnc-tokenizer.hpp"
@@ -58,10 +59,15 @@ enum class GncTransPropType {
     BALANCE,
     MEMO,
     OACCOUNT,
-    OMEMO,
-    NUM_COL_TYPES
+    OMEMO
 };
 
+/** Maps all column types to a string representation.
+ *  The actual definition is in gnc-csv-imp-trans.cpp.
+ *  Attention: that definition should be adjusted for any
+ *  changes to enum class GncTransPropType ! */
+extern std::map<GncTransPropType, const char*> gnc_csv_col_type_strs;
+
 /** Error domain for the csv importer. */
 #define GNC_CSV_IMP_ERROR gnc_csv_imp_error_quark ()
 GQuark gnc_csv_imp_error_quark (void);
@@ -102,9 +108,6 @@ extern const gchar* currency_format_user[];
 extern const int num_date_formats;
 extern const gchar* date_format_user[];
 
-/* This array contains all of the different strings for different column types. */
-extern const gchar* gnc_csv_col_type_strs[];
-
 /** Pair to hold a tokenized line of input and an optional error string */
 using parse_line_t = std::pair<str_vec, std::string>;
 

commit 6f15805c0b2c1e138e4bf7f81eea96cde1f3e4e7
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sun Jun 12 16:08:44 2016 +0200

    Adapt assistant for c++ guid changes

diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index 0c477a0..e68410c 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -27,6 +27,8 @@
     @author Copyright (c) 2012 Robert Fewell
 */
 
+#include <guid.hpp>
+
 extern "C"
 {
 #include "config.h"
diff --git a/src/import-export/csv-imp/test/utest-gnc-csv-imp-trans.cpp b/src/import-export/csv-imp/test/utest-gnc-csv-imp-trans.cpp
index c198b62..d5b58ac 100644
--- a/src/import-export/csv-imp/test/utest-gnc-csv-imp-trans.cpp
+++ b/src/import-export/csv-imp/test/utest-gnc-csv-imp-trans.cpp
@@ -21,6 +21,8 @@
  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
  * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
  ********************************************************************/
+#include "guid.hpp"
+
 extern "C" {
 #include <config.h>
 #include <string.h>

commit c21cf18847cf03baca4978ac45b33c12270cccb8
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sat Jan 30 14:53:22 2016 +0100

    Refactor csv assistant code to use GncCsvParseData class instead of gnc-csv-model code

diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.cpp b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
index aafabdf..0c477a0 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -51,11 +51,14 @@ extern "C"
 #include "import-main-matcher.h"
 #include "gnc-csv-account-map.h"
 
-#include "gnc-csv-model.h"
 #include "gnc-csv-gnumeric-popup.h"
 #include <goffice/go-charmap-sel.h>
 }
 
+#include "gnc-csv-imp-trans.hpp"
+#include "gnc-fw-tokenizer.hpp"
+#include "gnc-csv-tokenizer.hpp"
+
 #define MIN_COL_WIDTH 70
 #define GNC_PREFS_GROUP "dialogs.import.csv"
 #define ASSISTANT_CSV_IMPORT_TRANS_CM_CLASS "assistant-csv-trans-import"
@@ -178,10 +181,10 @@ void csv_import_trans_save_settings_cb (GtkWidget *button, CsvImportTrans *info)
 void csv_import_trans_changed_settings_cb (GtkWidget *button, CsvImportTrans *info);
 void csv_import_trans_load_settings (CsvImportTrans *info);
 
+void sep_button_clicked (GtkWidget* widget, CsvImportTrans* info);
 static void gnc_csv_preview_update_assist (CsvImportTrans* info);
 void gnc_csv_reset_preview_setting (CsvImportTrans* info, gboolean block);
 gboolean preview_settings_valid (CsvImportTrans *info);
-static gboolean delete_column (CsvImportTrans* info, int col, gboolean test_only);
 gboolean get_list_of_accounts (CsvImportTrans* info, GtkTreeModel *store);
 
 /*************************************************************************/
@@ -230,15 +233,18 @@ csv_import_trans_load_settings (CsvImportTrans *info)
         }
 
         // Set start row
+        info->parse_data->start_row = info->settings_data->header_rows;
         adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(info->start_row_spin));
         gtk_spin_button_set_value (GTK_SPIN_BUTTON(info->start_row_spin), info->settings_data->header_rows);
 
         // Set end row
+        info->parse_data->end_row = info->num_of_rows - info->settings_data->footer_rows;
         adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(info->end_row_spin));
         gtk_adjustment_set_upper (adj, info->num_of_rows);
         gtk_spin_button_set_value (GTK_SPIN_BUTTON(info->end_row_spin), info->num_of_rows - info->settings_data->footer_rows);
 
         // Set Alternate rows
+        info->parse_data->skip_rows = info->settings_data->skip_alt_rows;
         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->skip_rows), info->settings_data->skip_alt_rows);
 
         // Set Import Format
@@ -248,6 +254,7 @@ csv_import_trans_load_settings (CsvImportTrans *info)
         // This Section deals with the separators
         if (info->settings_data->csv_format)
         {
+            info->parse_data->file_format (GncImpFileFormat::CSV, NULL);
             for (i = 0; i < SEP_NUM_OF_TYPES; i++)
             {
                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->sep_buttons[i]), info->settings_data->separator[i]);
@@ -257,65 +264,46 @@ csv_import_trans_load_settings (CsvImportTrans *info)
                 gtk_entry_set_text (GTK_ENTRY(info->custom_entry), info->settings_data->custom_entry);
             else
                 gtk_entry_set_text (GTK_ENTRY(info->custom_entry), "");
+
+            sep_button_clicked (NULL, info);
         }
 
         // This section deals with the combo's and character encoding
+        info->parse_data->date_format = info->settings_data->date_active;
         gtk_combo_box_set_active (GTK_COMBO_BOX(info->date_format_combo), info->settings_data->date_active);
+
+        info->parse_data->currency_format = info->settings_data->currency_active;
         gtk_combo_box_set_active (GTK_COMBO_BOX(info->currency_format_combo), info->settings_data->currency_active);
+        info->parse_data->convert_encoding (info->settings_data->encoding);
         go_charmap_sel_set_encoding (info->encselector, info->settings_data->encoding);
 
-        // This section deals with the column widths
-        if ((!info->settings_data->csv_format) && (g_strcmp0 (info->settings_data->column_widths, NULL) != 0))
+        // This section deals with the column widths (which are only used for fixed width files)
+        if (!info->settings_data->csv_format)
         {
-            GError  *error = NULL;
-            gchar  **widths;
-            int      i, max_line;
-            int      colcount = stf_parse_options_fixed_splitpositions_count (info->parse_data->options);
+            info->parse_data->file_format (GncImpFileFormat::FIXED_WIDTH, NULL);
+            GncFwTokenizer *fwtok = dynamic_cast<GncFwTokenizer*>(info->parse_data->tokenizer.get());
+            if (info->settings_data->column_widths, NULL)
+                fwtok->cols_from_string (std::string(info->settings_data->column_widths));
 
-            // Clear the fixed width entries, if any...
-            if (colcount != 0)
-            {
-                for (i = colcount; i >= 0; i--)
-                {
-                    delete_column (info, i, FALSE);
-                }
-            }
-
-            widths = g_strsplit (info->settings_data->column_widths, ",", -1);
-            max_line = info->longest_line;
-
-            for (i=0; widths[i] != NULL; i++)
+            GError  *error = NULL;
+            if (info->parse_data->parse (FALSE, &error))
             {
-                int charindex = 0;
-
-                if (widths[i] != NULL)
-                    charindex = atoi (widths[i]);
-
-                if (max_line > charindex)
-                    stf_parse_options_fixed_splitpositions_add (info->parse_data->options, charindex);
-                else
-                    gnc_error_dialog (NULL, "%s", _("There was a problem with the column widths, please review."));
-
-                if (gnc_csv_parse (info->parse_data, FALSE, &error))
-                {
-                    gnc_error_dialog (NULL, "%s", _("There was a problem with the column widths, please review."));
-                    g_error_free (error);
-                    g_free (group);
-                    g_free (name);
-                    return;
-                }
-                gnc_csv_preview_update_assist (info);
+                gnc_error_dialog (NULL, "%s", _("There was a problem with the column widths, please review."));
+                g_error_free (error);
+                g_free (group);
+                g_free (name);
+                return;
             }
-            g_strfreev (widths);
+            gnc_csv_preview_update_assist (info);
         }
 
         // This section deals with the column types
-        if (g_strcmp0 (info->settings_data->column_types, NULL) != 0)
+        if (info->settings_data->column_types)
         {
             GtkTreeModel *store;
             GtkTreeIter   iter;
             gchar       **columns;
-            int           i, t;
+            int           i;
             gboolean      error = FALSE;
 
             columns = g_strsplit (info->settings_data->column_types, ",", -1);
@@ -332,13 +320,16 @@ csv_import_trans_load_settings (CsvImportTrans *info)
                 int s = i * 2 + 1;
                 gboolean found = FALSE;
 
-                for (t = 0; t < GNC_CSV_NUM_COL_TYPES; t++)
+                for (auto t = GncTransPropType::NONE;
+                          t != GncTransPropType::NUM_COL_TYPES;
+                          static_cast<GncTransPropType>(static_cast<int>(t) + 1))
                 {
-                    const gchar *type = gnc_csv_column_type_strs[t];
+                    const gchar *type = gnc_csv_column_type_strs[static_cast<int>(t)];
 
                     // Check to see if column type is valid
                     if (g_strcmp0 (type, columns[i]) == 0)
                     {
+                        info->parse_data->column_types.at(i) = t;
                         /* Get the type string first. (store is arranged so that every two
                         * columns is a pair of the model used for the combobox and the
                         * string that appears, so that store looks like:
@@ -606,26 +597,8 @@ csv_import_trans_save_settings_cb (GtkWidget *button, CsvImportTrans *info)
             info->settings_data->column_widths = "5,10,15";
         else
         {
-            gchar *details = NULL;
-            int i = 0;
-            int number_of_splits = stf_parse_options_fixed_splitpositions_count (info->parse_data->options);
-
-            for (i = 0; i < number_of_splits - 1; i++)
-            {
-                gchar *str_width = g_strdup_printf ("%d", stf_parse_options_fixed_splitpositions_nth (info->parse_data->options, i));
-
-                if (!details)
-                    details = g_strdup (str_width);
-                else
-                {
-                    gchar *details_prev = details;
-                    details = g_strjoin (",", details_prev, str_width, NULL);
-                    g_free (details_prev);
-                }
-                g_free (str_width);
-            }
-            info->settings_data->column_widths = g_strdup (details);
-            g_free (details);
+            GncFwTokenizer *fwtok = dynamic_cast<GncFwTokenizer*>(info->parse_data->tokenizer.get());
+            info->settings_data->column_widths = g_strdup (fwtok->cols_to_string().c_str());
         }
 
         // Save the settings
@@ -695,7 +668,6 @@ csv_import_trans_file_chooser_confirm_cb (GtkWidget *button, CsvImportTrans *inf
     GtkWidget *page = gtk_assistant_get_nth_page (assistant, num);
     GError* error;
     gchar *file_name;
-    GncCsvParseData* parse_data;
 
     gtk_assistant_set_page_complete (assistant, page, FALSE);
 
@@ -716,15 +688,16 @@ csv_import_trans_file_chooser_confirm_cb (GtkWidget *button, CsvImportTrans *inf
         info->file_name = g_strdup (file_name);
         error = NULL;
         /* Load the file into parse_data. */
-        parse_data = gnc_csv_new_parse_data();
-        if (gnc_csv_load_file (parse_data, file_name, &error))
+        auto parse_data = new GncCsvParseData;
+        // FIXME Filetype isn't set yet !?
+        if (parse_data->load_file (file_name, &error))
         {
             /* If we couldn't load the file ... */
             gnc_error_dialog (NULL, "%s", error->message);
             if (error->code == GNC_CSV_IMP_ERROR_OPEN)
             {
                 g_free (file_name);
-                gnc_csv_parse_data_free (parse_data);
+                delete parse_data;
                 return;
             }
             /* If we couldn't guess the encoding, we are content with just
@@ -734,17 +707,17 @@ csv_import_trans_file_chooser_confirm_cb (GtkWidget *button, CsvImportTrans *inf
         else
         {
             /* Parse the data. */
-            if (gnc_csv_parse (parse_data, TRUE, &error))
+            if (parse_data->parse (TRUE, &error))
             {
                 /* If we couldn't parse the data ... */
                 gnc_error_dialog (NULL, "%s", error->message);
-                gnc_csv_parse_data_free (parse_data);
+                delete parse_data;
             }
             else
             {
                 if (info->parse_data) // Free parse_data if we have come back here
                 {
-                    gnc_csv_parse_data_free (info->parse_data);
+                    delete info->parse_data;
                     gnc_csv_reset_preview_setting (info, TRUE);
                 }
                 info->parse_data = parse_data;
@@ -945,19 +918,23 @@ static GtkCellRenderer* gnc_csv_preview_get_cell_renderer (CsvImportTrans* info,
  * @param widget The widget that was changed
  * @param info The data that is being configured
  */
-static void sep_button_clicked (GtkWidget* widget, CsvImportTrans* info)
+void sep_button_clicked (GtkWidget* widget, CsvImportTrans* info)
 {
     int i;
-    const char* stock_separator_characters[] = {" ", "\t", ",", ":", ";", "-"};
-    GSList* checked_separators = NULL;
-    GError* error;
+    const std::string stock_separator_characters(" \t,:;-");
+    std::string checked_separators;
+
+    /* Only manipulate separator characters if the currently open file is
+     * csv separated. */
+    if (info->parse_data->file_format() != GncImpFileFormat::CSV)
+        return;
 
     /* Add the corresponding characters to checked_separators for each
      * button that is checked. */
     for (i = 0; i < SEP_NUM_OF_TYPES; i++)
     {
         if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(info->sep_buttons[i])))
-            checked_separators = g_slist_append (checked_separators, (gpointer) stock_separator_characters[i]);
+            checked_separators += stock_separator_characters[i];
     }
 
     /* Add the custom separator if the user checked its button. */
@@ -965,33 +942,34 @@ static void sep_button_clicked (GtkWidget* widget, CsvImportTrans* info)
     {
         char* custom_sep = (char*)gtk_entry_get_text (info->custom_entry);
         if (custom_sep[0] != '\0') /* Don't add a blank separator (bad things will happen!). */
-            checked_separators = g_slist_append (checked_separators, custom_sep);
+            checked_separators += custom_sep;
     }
 
     /* Set the parse options using the checked_separators list. */
-    stf_parse_options_csv_set_separators (info->parse_data->options, NULL, checked_separators);
-    g_slist_free (checked_separators);
+    GncCsvTokenizer *csvtok = dynamic_cast<GncCsvTokenizer*>(info->parse_data->tokenizer.get());
+    csvtok->set_separators (checked_separators);
 
     /* Parse the data using the new options. We don't want to reguess
      * the column types because we want to leave the user's
-     * configurations in tact. */
-    if (gnc_csv_parse (info->parse_data, FALSE, &error))
+     * configurations intact. */
+    GError* error;
+    if (info->parse_data->parse (FALSE, &error))
     {
         /* Warn the user there was a problem and try to undo what caused
          * the error. (This will cause a reparsing and ideally a usable
          * configuration.) */
         gnc_error_dialog (NULL, "Error in parsing");
+        /* If we're here because the user changed the file format, we should just wait for the user
+         * to update the configuration */
+        if (!widget)
+            return;
         /* If the user changed the custom separator, erase that custom separator. */
         if (widget == GTK_WIDGET(info->custom_entry))
-        {
             gtk_entry_set_text (GTK_ENTRY(widget), "");
-        }
         /* If the user checked a checkbutton, toggle that checkbutton back. */
         else
-        {
             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(widget),
                                          !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget)));
-        }
         return;
     }
 
@@ -1010,19 +988,21 @@ static void sep_button_clicked (GtkWidget* widget, CsvImportTrans* info)
  */
 static void separated_or_fixed_selected (GtkToggleButton* csv_button, CsvImportTrans* info)
 {
-    GError* error = NULL;
     /* Set the parsing type correctly. */
     if (gtk_toggle_button_get_active (csv_button)) /* If we're in CSV mode ... */
     {
-        stf_parse_options_set_type (info->parse_data->options, PARSE_TYPE_CSV);
-    }
-    else /* If we're in fixed-width mode ... */
-    {
-        stf_parse_options_set_type (info->parse_data->options, PARSE_TYPE_FIXED);
+        info->parse_data->file_format (GncImpFileFormat::CSV, NULL);
+        sep_button_clicked (NULL, info);
+        // Note: sep_button_clicked also handles reparsing the data, so we're done here
+        return;
     }
 
+    /* So we're in fixed-width mode ... */
+    info->parse_data->file_format (GncImpFileFormat::FIXED_WIDTH, NULL);
+
     /* Reparse the data. */
-    if (gnc_csv_parse (info->parse_data, FALSE, &error))
+    GError* error = NULL;
+    if (info->parse_data->parse (FALSE, &error))
     {
         /* Show an error dialog explaining the problem. */
         gnc_error_dialog (NULL, "%s", error->message);
@@ -1061,16 +1041,16 @@ static void encoding_selected (GOCharmapSel* selector, const char* encoding,
     /* If this is the second time the function is called ... */
     if (info->encoding_selected_called)
     {
-        const char* previous_encoding = info->parse_data->encoding;
+        std::string previous_encoding = info->parse_data->tokenizer->encoding().c_str();
         GError* error = NULL;
         /* Try converting the new encoding and reparsing. */
-        if (gnc_csv_convert_encoding (info->parse_data, encoding, &error) ||
-                gnc_csv_parse (info->parse_data, FALSE, &error))
+        info->parse_data->convert_encoding (encoding);
+        if (info->parse_data->parse (FALSE, &error))
         {
             /* If it fails, change back to the old encoding. */
             gnc_error_dialog (NULL, "%s", _("Invalid encoding selected"));
             info->encoding_selected_called = FALSE;
-            go_charmap_sel_set_encoding (selector, previous_encoding);
+            go_charmap_sel_set_encoding (selector, previous_encoding.c_str());
             return;
         }
 
@@ -1171,157 +1151,23 @@ static GnumericPopupMenuElement const popup_elements[] =
     { NULL, NULL, 0, 0, 0 },
 };
 
-static gboolean
-make_new_column (CsvImportTrans* info, int col, int dx, gboolean test_only)
+static uint get_new_col_rel_pos (CsvImportTrans* info, int col, int dx)
 {
-    PangoLayout *layout;
     PangoFontDescription *font_desc;
-    int charindex, width;
-    GtkCellRenderer *cell =	gnc_csv_preview_get_cell_renderer (info, col);
-    int colstart, colend;
-    GError* error = NULL;
-
-    colstart = (col == 0)
-               ? 0
-               : stf_parse_options_fixed_splitpositions_nth (info->parse_data->options, col - 1);
-    colend = stf_parse_options_fixed_splitpositions_nth (info->parse_data->options, col);
-
+    int width;
+    uint charindex;
+    GtkCellRenderer *cell = gnc_csv_preview_get_cell_renderer (info, col);
     g_object_get (G_OBJECT(cell), "font_desc", &font_desc, NULL);
-    layout = gtk_widget_create_pango_layout (GTK_WIDGET(info->treeview), "x");
+
+    PangoLayout *layout = gtk_widget_create_pango_layout (GTK_WIDGET(info->treeview), "x");
     pango_layout_set_font_description (layout, font_desc);
     pango_layout_get_pixel_size (layout, &width, NULL);
     if (width < 1) width = 1;
-    charindex = colstart + (dx + width / 2) / width;
+    charindex = (dx + width / 2) / width;
     g_object_unref (layout);
     pango_font_description_free (font_desc);
 
-    if (charindex <= colstart || (colend != -1 && charindex >= colend))
-        return FALSE;
-
-    if (!test_only)
-    {
-        stf_parse_options_fixed_splitpositions_add (info->parse_data->options, charindex);
-        if (gnc_csv_parse (info->parse_data, FALSE, &error))
-        {
-            gnc_error_dialog (NULL, "%s", error->message);
-            return FALSE;
-        }
-        gnc_csv_preview_update_assist (info);
-
-        /* Refresh the row highlighting */
-        row_selection_update (info);
-    }
-
-    return TRUE;
-}
-
-static gboolean
-widen_column (CsvImportTrans* info, int col, gboolean test_only)
-{
-    int colcount = stf_parse_options_fixed_splitpositions_count (info->parse_data->options);
-    int nextstart, nextnextstart;
-    GError* error = NULL;
-
-    if (col >= colcount - 1)
-        return FALSE;
-
-    nextstart = stf_parse_options_fixed_splitpositions_nth (info->parse_data->options, col);
-
-    nextnextstart = (col == colcount - 2)
-                    ? info->longest_line
-                    : stf_parse_options_fixed_splitpositions_nth (info->parse_data->options, col + 1);
-
-    if (nextstart + 1 >= nextnextstart)
-        return FALSE;
-
-    if (!test_only)
-    {
-        stf_parse_options_fixed_splitpositions_remove (info->parse_data->options, nextstart);
-        stf_parse_options_fixed_splitpositions_add (info->parse_data->options, nextstart + 1);
-        if (gnc_csv_parse (info->parse_data, FALSE, &error))
-        {
-            gnc_error_dialog (NULL, "%s", error->message);
-            return FALSE;
-        }
-        gnc_csv_preview_update_assist (info);
-
-        /* Refresh the row highlighting */
-        row_selection_update (info);
-    }
-    return TRUE;
-}
-
-static gboolean
-narrow_column (CsvImportTrans* info, int col, gboolean test_only)
-{
-    int colcount = stf_parse_options_fixed_splitpositions_count (info->parse_data->options);
-    int thisstart, nextstart;
-    GError* error = NULL;
-
-    if (col >= colcount - 1)
-        return FALSE;
-
-    thisstart = (col == 0)
-                ? 0
-                : stf_parse_options_fixed_splitpositions_nth (info->parse_data->options, col - 1);
-    nextstart = stf_parse_options_fixed_splitpositions_nth (info->parse_data->options, col);
-
-    if (nextstart - 1 <= thisstart)
-        return FALSE;
-
-    if (!test_only)
-    {
-        stf_parse_options_fixed_splitpositions_remove (info->parse_data->options, nextstart);
-        stf_parse_options_fixed_splitpositions_add (info->parse_data->options, nextstart - 1);
-        if (gnc_csv_parse (info->parse_data, FALSE, &error))
-        {
-            gnc_error_dialog (NULL, "%s", error->message);
-            return FALSE;
-        }
-        gnc_csv_preview_update_assist (info);
-
-        /* Refresh the row highlighting */
-        row_selection_update (info);
-    }
-    return TRUE;
-}
-
-static gboolean
-delete_column (CsvImportTrans* info, int col, gboolean test_only)
-{
-    GError* error = NULL;
-    int colcount = stf_parse_options_fixed_splitpositions_count (info->parse_data->options);
-    if (col < 0 || col >= colcount - 1)
-        return FALSE;
-
-    if (!test_only)
-    {
-        int nextstart = stf_parse_options_fixed_splitpositions_nth (info->parse_data->options, col);
-        stf_parse_options_fixed_splitpositions_remove (info->parse_data->options, nextstart);
-        if (gnc_csv_parse (info->parse_data, FALSE, &error))
-        {
-            gnc_error_dialog (NULL, "%s", error->message);
-            return FALSE;
-        }
-        gnc_csv_preview_update_assist (info);
-
-        /* Refresh the row highlighting */
-        row_selection_update (info);
-    }
-    return TRUE;
-}
-
-static void
-select_column (CsvImportTrans* info, int col)
-{
-    int colcount = stf_parse_options_fixed_splitpositions_count (info->parse_data->options);
-    GtkTreeViewColumn *column;
-
-    if (col < 0 || col >= colcount)
-        return;
-
-    column = gtk_tree_view_get_column (info->treeview, col);
-    gtk_widget_grab_focus (column->button);
+    return charindex;
 }
 
 static gboolean
@@ -1330,29 +1176,53 @@ fixed_context_menu_handler (GnumericPopupMenuElement const *element,
 {
     CsvImportTrans *info = (CsvImportTrans*) user_data;
     int col = info->fixed_context_col;
+    uint rel_pos = get_new_col_rel_pos (info, col, info->fixed_context_dx);
+    GncFwTokenizer *fwtok = dynamic_cast<GncFwTokenizer*>(info->parse_data->tokenizer.get());
 
     switch (element->index)
     {
     case CONTEXT_STF_IMPORT_MERGE_LEFT:
-        delete_column (info, col - 1, FALSE);
+        fwtok->col_delete (col - 1);
         break;
     case CONTEXT_STF_IMPORT_MERGE_RIGHT:
-        delete_column (info, col, FALSE);
+        fwtok->col_delete (col);
         break;
     case CONTEXT_STF_IMPORT_SPLIT:
-        make_new_column (info, col, info->fixed_context_dx, FALSE);
+        fwtok->col_split (col, rel_pos);
         break;
     case CONTEXT_STF_IMPORT_WIDEN:
-        widen_column (info, col, FALSE);
+        fwtok->col_widen (col);
         break;
     case CONTEXT_STF_IMPORT_NARROW:
-        narrow_column (info, col, FALSE);
+        fwtok->col_narrow (col);
         break;
     default:
         ; /* Nothing */
     }
+
+    GError* error = NULL;
+    if (info->parse_data->parse (FALSE, &error))
+    {
+        gnc_error_dialog (NULL, "%s", error->message);
+        return FALSE;
+    }
+    gnc_csv_preview_update_assist (info);
+
+    /* Refresh the row highlighting */
+    row_selection_update (info);
     return TRUE;
 }
+static void
+select_column (CsvImportTrans* info, int col)
+{
+    GtkTreeViewColumn *column;
+
+    if (col < 0)
+        return;
+
+    column = gtk_tree_view_get_column (info->treeview, col);
+    gtk_widget_grab_focus (column->button);
+}
 
 static void
 fixed_context_menu (CsvImportTrans* info, GdkEventButton *event,
@@ -1360,18 +1230,20 @@ fixed_context_menu (CsvImportTrans* info, GdkEventButton *event,
 {
     int sensitivity_filter = 0;
 
+    GncFwTokenizer *fwtok = dynamic_cast<GncFwTokenizer*>(info->parse_data->tokenizer.get());
     info->fixed_context_col = col;
     info->fixed_context_dx = dx;
+    uint rel_pos = get_new_col_rel_pos (info, col, dx);
 
-    if (!delete_column (info, col - 1, TRUE))
+    if (!fwtok->col_can_delete (col - 1))
         sensitivity_filter |= (1 << CONTEXT_STF_IMPORT_MERGE_LEFT);
-    if (!delete_column (info, col, TRUE))
+    if (!fwtok->col_can_delete (col))
         sensitivity_filter |= (1 << CONTEXT_STF_IMPORT_MERGE_RIGHT);
-    if (!make_new_column (info, col, dx, TRUE))
+    if (!fwtok->col_can_split (col, rel_pos))
         sensitivity_filter |= (1 << CONTEXT_STF_IMPORT_SPLIT);
-    if (!widen_column (info, col, TRUE))
+    if (!fwtok->col_can_widen (col))
         sensitivity_filter |= (1 << CONTEXT_STF_IMPORT_WIDEN);
-    if (!narrow_column (info, col, TRUE))
+    if (!fwtok->col_can_narrow (col))
         sensitivity_filter |= (1 << CONTEXT_STF_IMPORT_NARROW);
 
     select_column (info, col);
@@ -1394,7 +1266,7 @@ fixed_context_menu (CsvImportTrans* info, GdkEventButton *event,
 static void treeview_resized (GtkWidget* widget, GtkAllocation* allocation, CsvImportTrans* info)
 {
     /* ncols is the number of columns in the data. */
-    int i, ncols = info->parse_data->column_types->len;
+    int i, ncols = info->parse_data->column_types.size();
 
     /* Go through each column except for the last. (We don't want to set
      * the width of the last column because the user won't be able to
@@ -1436,7 +1308,7 @@ static void column_type_changed (GtkCellRenderer* renderer, gchar* path,
                                 GtkTreeIter* new_text_iter, CsvImportTrans* info)
 {
     /* ncols is the number of columns in the data. */
-    int i, ncols = info->parse_data->column_types->len;
+    int i, ncols = info->parse_data->column_types.size();
     /* store has the actual strings that appear in info->ctreeview. */
     GtkTreeModel* store = gtk_tree_view_get_model (info->ctreeview);
     GtkTreeModel* model;
@@ -1479,7 +1351,7 @@ static void column_type_changed (GtkCellRenderer* renderer, gchar* path,
             {
                 /* ... set this column to the "None" type. (We can't allow duplicate types.) */
                 gtk_list_store_set (GTK_LIST_STORE(store), &iter, 2 * i + 1,
-                                   _(gnc_csv_column_type_strs[GNC_CSV_NONE]), -1);
+                                   _(gnc_csv_column_type_strs[static_cast<int>(GncTransPropType::NONE)]), -1);
             }
             g_free (contents);
         }
@@ -1493,6 +1365,25 @@ static void column_type_changed (GtkCellRenderer* renderer, gchar* path,
     }
 }
 
+static void
+split_column (CsvImportTrans* info, int col, int dx)
+{
+    uint rel_pos = get_new_col_rel_pos (info, col, dx);
+
+    GncFwTokenizer *fwtok = dynamic_cast<GncFwTokenizer*>(info->parse_data->tokenizer.get());
+    fwtok->col_split (col, rel_pos);
+    GError* error = NULL;
+    if (info->parse_data->parse (FALSE, &error))
+    {
+        gnc_error_dialog (NULL, "%s", error->message);
+        return;
+    }
+    gnc_csv_preview_update_assist (info);
+
+    /* Refresh the row highlighting */
+    row_selection_update (info);
+}
+
 
 /** Event handler for clicking on column headers. This function is
  * called whenever the user clicks on column headers in
@@ -1508,7 +1399,7 @@ static void header_button_press_handler (GtkWidget* button, GdkEventButton* even
        to correct for the indentation of button. */
     int i, offset;
     GtkAllocation alloc;
-    int col = 0, ncols = info->parse_data->column_types->len;
+    int col = 0, ncols = info->parse_data->column_types.size();
 
     gtk_widget_get_allocation (gtk_bin_get_child (GTK_BIN(button)), &alloc);
     offset = alloc.x - alloc.x;
@@ -1523,7 +1414,7 @@ static void header_button_press_handler (GtkWidget* button, GdkEventButton* even
     }
 
     /* Don't let the user affect the last column if it has error messages. */
-    if (info->parse_data->orig_max_row < ncols && ncols - col == 1)
+    if (col == ncols)
     {
         return;
     }
@@ -1531,7 +1422,7 @@ static void header_button_press_handler (GtkWidget* button, GdkEventButton* even
     /* Double clicks can split columns. */
     if (event->type == GDK_2BUTTON_PRESS && event->button == 1)
     {
-        make_new_column (info, col, (int)event->x - offset, FALSE);
+        split_column (info, col, (int)event->x - offset);
     }
     /* Right clicking brings up a context menu. */
     else if (event->type == GDK_BUTTON_PRESS && event->button == 3)
@@ -1549,9 +1440,7 @@ static void header_button_press_handler (GtkWidget* button, GdkEventButton* even
  */
 gboolean preview_settings_valid (CsvImportTrans* info)
 {
-    /* Shorten the column_types identifier. */
-    GArray* column_types = info->parse_data->column_types;
-    int i, ncols = column_types->len; /* ncols is the number of columns in the data. */
+    int i, ncols = info->parse_data->column_types.size(); /* ncols is the number of columns in the data. */
     int weight = 0;
     int oweight = 0;
     gboolean valid = TRUE;
@@ -1567,14 +1456,9 @@ gboolean preview_settings_valid (CsvImportTrans* info)
     /* Get an iterator for the first required row in the data store. */
     gtk_tree_model_iter_nth_child (GTK_TREE_MODEL(datastore), &iter2, NULL, info->start_row);
 
-    // If we are looking at errors, remove the error column
-    if (info->previewing_errors)
-        ncols = ncols - 1;
-
     /* Go through each of the columns. */
     for (i = 0; i < ncols; i++)
     {
-        int type; /* The column type contained in this column. */
         gchar* contents = NULL; /* The column type string in this column. */
         gchar* prevstr = NULL; /* The string in this column from datastore. */
         /* Get the type string first. (store is arranged so that every two
@@ -1584,17 +1468,19 @@ gboolean preview_settings_valid (CsvImportTrans* info)
         gtk_tree_model_get (store, &iter1, 2 * i + 1, &contents, -1);
 
         /* Go through each column type until ... */
-        for (type = 0; type < GNC_CSV_NUM_COL_TYPES; type++)
+        for (auto col_type = GncTransPropType::NONE;
+                  col_type != GncTransPropType::NUM_COL_TYPES;
+                  col_type = static_cast<GncTransPropType>(static_cast<int>(col_type) + 1))
         {
             /* ... we find one that matches with what's in the column. */
-            if (!g_strcmp0 (contents, _(gnc_csv_column_type_strs[type])))
+            if (!g_strcmp0 (contents, _(gnc_csv_column_type_strs[static_cast<int>(col_type)])))
             {
                 /* Set the column_types array appropriately and quit. */
-                column_types->data[i] = type;
+                info->parse_data->column_types[i] = col_type;
 
-                switch (type)
+                switch (col_type)
                 {
-                case GNC_CSV_DATE:
+                case GncTransPropType::DATE:
                     weight = weight + 1000;
                     gtk_tree_model_get (datastore, &iter2, i + 1, &prevstr, -1);
 
@@ -1602,34 +1488,37 @@ gboolean preview_settings_valid (CsvImportTrans* info)
                         valid = FALSE;
                     break;
 
-                case GNC_CSV_DESCRIPTION:
+                case GncTransPropType::DESCRIPTION:
                     weight = weight + 100;
                     break;
 
-                case GNC_CSV_BALANCE:
+                case GncTransPropType::BALANCE:
                     havebalance = TRUE;
-                case GNC_CSV_DEPOSIT:
-                case GNC_CSV_WITHDRAWAL:
+                    /* No break */
+                case GncTransPropType::DEPOSIT:
+                case GncTransPropType::WITHDRAWAL:
                     weight = weight + 10;
                     break;
 
-                case GNC_CSV_NUM:
-                case GNC_CSV_NOTES:
-                case GNC_CSV_MEMO:
+                case GncTransPropType::NUM:
+                case GncTransPropType::NOTES:
+                case GncTransPropType::MEMO:
                     weight = weight + 1;
                     break;
 
-                case GNC_CSV_ACCOUNT:
+                case GncTransPropType::ACCOUNT:
                     weight = weight + 1;
                     break;
 
-                case GNC_CSV_OACCOUNT:
+                case GncTransPropType::OACCOUNT:
                     oweight = oweight + 100;
                     break;
 
-                case GNC_CSV_OMEMO:
+                case GncTransPropType::OMEMO:
                     oweight = oweight + 1;
                     break;
+                default:
+                    break;
                 }
                 break;
             }
@@ -1709,9 +1598,7 @@ check_for_duplicates (GtkListStore *liststore, const gchar *string)
  */
 gboolean get_list_of_accounts (CsvImportTrans* info, GtkTreeModel *store)
 {
-    /* Shorten the column_types identifier. */
-    GArray  *column_types = info->parse_data->column_types;
-    int      i, j, ncols = column_types->len; /* ncols is the number of columns in the data. */
+    int      i, j, ncols = info->parse_data->column_types.size(); /* ncols is the number of columns in the data. */
     gboolean have_accounts = FALSE;
     gint     home_account_number = 0;
     gint     other_account_number = 0;
@@ -1726,16 +1613,11 @@ gboolean get_list_of_accounts (CsvImportTrans* info, GtkTreeModel *store)
     /* Get an iterator for the first (and only) row of the column store. */
     gtk_tree_model_get_iter_first (columnstore, &iter1);
 
-    // If we are looking at errors, remove the error column
-    if (info->previewing_errors)
-        ncols = ncols - 1;
-
     for (j = info->start_row; j <= info->end_row; j++)
     {
         /* Go through each of the columns. */
         for (i = 0; i < ncols; i++)
         {
-            int type;               /* The column type contained in this column. */
             gchar* contents = NULL; /* The column type string in this column. */
             gchar* accstr = NULL;   /* The string in this column from datastore. */
 
@@ -1745,40 +1627,33 @@ gboolean get_list_of_accounts (CsvImportTrans* info, GtkTreeModel *store)
              * model 0, string 0, model 1, string 1, ..., model ncols, string ncols. */
             gtk_tree_model_get (columnstore, &iter1, 2 * i + 1, &contents, -1);
 
-            /* Go through each column type until ... */
-            for (type = 0; type < GNC_CSV_NUM_COL_TYPES; type++)
+            /* We're only interested in columns of type ACCOUNT and OACCOUNT. */
+            auto col_type = GncTransPropType::NONE;
+            if (!g_strcmp0 (contents, _(gnc_csv_column_type_strs[static_cast<int>(GncTransPropType::ACCOUNT)])))
+                col_type = GncTransPropType::ACCOUNT;
+            else if(!g_strcmp0 (contents, _(gnc_csv_column_type_strs[static_cast<int>(GncTransPropType::OACCOUNT)])))
+                col_type = GncTransPropType::OACCOUNT;
+            if ((col_type == GncTransPropType::ACCOUNT) || (col_type == GncTransPropType::OACCOUNT))
             {
-                /* ... we find one that matches with what's in the column. */
-                if (!g_strcmp0 (contents, _(gnc_csv_column_type_strs[type])))
+                /* Get an iterator for the row in the data store. */
+                if (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL(datastore), &iter2, NULL, j))
                 {
-                    switch (type)
+                    gtk_tree_model_get (datastore, &iter2, i + 1, &accstr, -1);
+
+                    // Append the entry
+                    if (!check_for_duplicates (GTK_LIST_STORE(store), accstr))
                     {
-                    case GNC_CSV_ACCOUNT:
-                    case GNC_CSV_OACCOUNT:
-
-                        /* Get an iterator for the  row in the data store. */
-                        if (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL(datastore), &iter2, NULL, j))
-                        {
-                            gtk_tree_model_get (datastore, &iter2, i + 1, &accstr, -1);
-
-                            // Append the entry
-                            if (!check_for_duplicates (GTK_LIST_STORE(store), accstr))
-                            {
-                                if (type == GNC_CSV_ACCOUNT) // Count the number of unique account strings
-                                    home_account_number = home_account_number + 1;
-                                else
-                                    other_account_number = other_account_number + 1;
-
-                                gtk_list_store_append (GTK_LIST_STORE(store), &iter3);
-                                gtk_list_store_set (GTK_LIST_STORE(store), &iter3, MAPPING_STRING, accstr,
-                                                    MAPPING_FULLPATH, _("No Linked Account"), MAPPING_ACCOUNT, NULL, -1);
-                                have_accounts = TRUE;
-                            }
-                            g_free (accstr);
-                            break;
-                        }
+                        if (col_type == GncTransPropType::ACCOUNT) // Count the number of unique account strings
+                            home_account_number = home_account_number + 1;
+                        else
+                            other_account_number = other_account_number + 1;
+
+                        gtk_list_store_append (GTK_LIST_STORE(store), &iter3);
+                        gtk_list_store_set (GTK_LIST_STORE(store), &iter3, MAPPING_STRING, accstr,
+                                            MAPPING_FULLPATH, _("No Linked Account"), MAPPING_ACCOUNT, NULL, -1);
+                        have_accounts = TRUE;
                     }
-                    break;
+                    g_free (accstr);
                 }
             }
             /* Free the type string created by gtk_tree_model_get() */
@@ -1807,8 +1682,7 @@ static void gnc_csv_preview_update_assist (CsvImportTrans* info)
     GtkTreeIter iter;
     GtkTreeSelection *selection;
     /* ncols is the number of columns in the file data. */
-    guint i, j, ncols = info->parse_data->column_types->len,
-                max_str_len = info->parse_data->file_str.end - info->parse_data->file_str.begin;
+    guint i, j, ncols = info->parse_data->column_types.size();
 
     /* store contains only strings. */
     GType* types = g_new (GType, 2 * ncols);
@@ -1833,7 +1707,7 @@ static void gnc_csv_preview_update_assist (CsvImportTrans* info)
     {
         cstores[i] = gtk_list_store_new (1, G_TYPE_STRING);
         /* Add all of the possible entries to the combo box. */
-        for (j = 0; j < GNC_CSV_NUM_COL_TYPES; j++)
+        for (j = 0; j < static_cast<int>(GncTransPropType::NUM_COL_TYPES); j++)
         {
             gtk_list_store_append (cstores[i], &iter);
             gtk_list_store_set (cstores[i], &iter, 0, _(gnc_csv_column_type_strs[j]), -1);
@@ -1864,58 +1738,27 @@ static void gnc_csv_preview_update_assist (CsvImportTrans* info)
 
     /* Fill the data treeview with data from the file. */
     /* Also, update the longest line value within the following loop (whichever is executed). */
-    info->longest_line = 0;
-    if (info->previewing_errors) /* If we are showing only errors ... */
+    info->num_of_rows = 0;
+    for (auto parse_line : info->parse_data->orig_lines)
     {
-        /* ... only pick rows that are in info->error_lines. */
-        GList* error_lines = info->parse_data->error_lines;
-        while (error_lines != NULL)
-        {
-            int this_line_length = 0;
-            i = GPOINTER_TO_INT(error_lines->data);
-            gtk_list_store_append (store, &iter);
-
-            /* Row Color column */
-            gtk_list_store_set (store, &iter, 0, NULL, -1);
+        // When previewing errors skip all lines that don't have errors
+        if (info->previewing_errors && parse_line.second.empty())
+            continue;
 
-            for (j = 0; j < ((GPtrArray*)(info->parse_data->orig_lines->pdata[i]))->len; j++)
-            {
-                /* Add this cell's length to the row's length and set the value of the list store. */
-                gchar* cell_string = (gchar*)((GPtrArray*)(info->parse_data->orig_lines->pdata[i]))->pdata[j];
-                this_line_length += g_utf8_strlen(cell_string, max_str_len);
-                gtk_list_store_set (store, &iter, j + 1, cell_string, -1);
-            }
+        gtk_list_store_append (store, &iter);
 
-            if (this_line_length > info->longest_line)
-                info->longest_line = this_line_length;
+        /* Row Color column */
+        gtk_list_store_set (store, &iter, 0, NULL, -1);
 
-            error_lines = g_list_next (error_lines);
-        }
-    }
-    else /* Otherwise, put in all of the data. */
-    {
-        for (i = 0; i < info->parse_data->orig_lines->len; i++)
+        for (auto cell_str_it = parse_line.first.cbegin(); cell_str_it != parse_line.first.cend(); cell_str_it++)
         {
-            int this_line_length = 0;
-            gtk_list_store_append (store, &iter);
-
-            /* Row Color column */
-            gtk_list_store_set (store, &iter, 0, NULL, -1);
-
-            for (j = 0; j < ((GPtrArray*)(info->parse_data->orig_lines->pdata[i]))->len; j++)
-            {
-                /* Add this cell's length to the row's length and set the value of the list store. */
-                gchar* cell_string = (gchar*)((GPtrArray*)(info->parse_data->orig_lines->pdata[i]))->pdata[j];
-                this_line_length += g_utf8_strlen(cell_string, max_str_len);
-                gtk_list_store_set (store, &iter, j + 1, cell_string, -1);
-            }
-
-            if (this_line_length > info->longest_line)
-                info->longest_line = this_line_length;
-
-            /* Set the number of rows in the store */
-            info->num_of_rows = i + 1;
+            /* Add this cell's length to the row's length and set the value of the list store. */
+            uint pos = cell_str_it - parse_line.first.cbegin() + 1;
+            gtk_list_store_set (store, &iter, pos, cell_str_it->c_str(), -1);
         }
+
+        /* Set the number of rows in the store */
+        info->num_of_rows++;
     }
 
     /* Set all the column types to what's in the parse data. */
@@ -1924,7 +1767,7 @@ static void gnc_csv_preview_update_assist (CsvImportTrans* info)
     for (i = 0; i < ncols; i++)
     {
         gtk_list_store_set (ctstore, &iter, 2 * i, cstores[i], 2 * i + 1,
-                           _(gnc_csv_column_type_strs[(int)(info->parse_data->column_types->data[i])]),
+                           _(gnc_csv_column_type_strs[(int)(info->parse_data->column_types[i])]),
                            -1);
     }
 
@@ -1991,7 +1834,7 @@ static void gnc_csv_preview_update_assist (CsvImportTrans* info)
 
     /* Set the encoding selector to the right encoding. */
     info->code_encoding_calls = 2;
-    go_charmap_sel_set_encoding (info->encselector, info->parse_data->encoding);
+    go_charmap_sel_set_encoding (info->encselector, info->parse_data->tokenizer->encoding().c_str());
 
     /* Set the date format to what's in the combo box (since we don't
      * necessarily know if this will always be the same). */
@@ -2011,15 +1854,12 @@ void gnc_csv_reset_preview_setting (CsvImportTrans *info, gboolean block)
 {
     int i;
     GtkAdjustment  *adj;
-    int colcount = stf_parse_options_fixed_splitpositions_count (info->parse_data->options);
 
     // Clear the fixed width entries, if any...
-    if (colcount != 0)
+    if (info->parse_data->file_format() == GncImpFileFormat::FIXED_WIDTH)
     {
-        for (i = colcount; i >= 0; i--)
-        {
-            delete_column (info, i, FALSE);
-        }
+        GncFwTokenizer *fwtok = dynamic_cast<GncFwTokenizer*>(info->parse_data->tokenizer.get());
+        fwtok->columns();
     }
 
     // Reset Start Row
@@ -2070,6 +1910,19 @@ void gnc_csv_reset_preview_setting (CsvImportTrans *info, gboolean block)
     gtk_combo_box_set_active (GTK_COMBO_BOX(info->date_format_combo), 0);
     gtk_combo_box_set_active (GTK_COMBO_BOX(info->currency_format_combo), 0);
     go_charmap_sel_set_encoding (info->encselector, "UTF-8");
+
+// FIXME Should I explicitly do a reparse here, or did the above code already trigger it in some way ?
+//    GError* error = NULL;
+//    if (gnc_csv_parse (info->parse_data, FALSE, &error))
+//    {
+//        gnc_error_dialog (NULL, "%s", error->message);
+//        return FALSE;
+//    }
+//    gnc_csv_preview_update_assist (info);
+//
+//    /* Refresh the row highlighting */
+//    row_selection_update (info);
+
 }
 
 
@@ -2249,7 +2102,7 @@ csv_import_trans_assistant_account_page_prepare (GtkAssistant *assistant,
     else
     {
         // Check to see if we do not have an account column
-        if (!gnc_csv_parse_check_for_column_type (info->parse_data, GNC_CSV_ACCOUNT))
+        if (!info->parse_data->check_for_column_type (GncTransPropType::ACCOUNT))
         {
             text = g_strdup_printf (gettext ("No Account column present, to select import account double click on the required account and then click Forward to proceed."));
             mtext = g_strdup_printf ("<span size=\"medium\" color=\"red\"><b>%s</b></span>", text);
@@ -2593,7 +2446,7 @@ csv_import_trans_assistant_match_page_prepare (GtkAssistant *assistant,
     /* Block going back */
     gtk_assistant_commit (GTK_ASSISTANT(info->window));
 
-    if ((info->parse_data->error_lines == NULL) || (info->skip_errors == TRUE))
+    if (!info->parse_data->parse_errors || (info->skip_errors == TRUE))
     {
         GList* transactions; /* A list of the transactions we create */
 
@@ -2700,7 +2553,7 @@ csv_import_trans_forward_page_func (gint current_page, gpointer user_data)
                 info->settings_valid = preview_settings_valid (info);
 
                 // Check to see if we have an account column
-                if (gnc_csv_parse_check_for_column_type (info->parse_data, GNC_CSV_ACCOUNT))
+                if (info->parse_data->check_for_column_type (GncTransPropType::ACCOUNT))
                     next_page = 4;
                 else
                     next_page = 3;
@@ -2721,8 +2574,8 @@ csv_import_trans_forward_page_func (gint current_page, gpointer user_data)
                 info->account = gnc_import_account_assist_update (info->account_picker);
 
                 // Check to see if we have an account / other account columns
-                if (gnc_csv_parse_check_for_column_type (info->parse_data, GNC_CSV_ACCOUNT) ||
-                    gnc_csv_parse_check_for_column_type (info->parse_data, GNC_CSV_OACCOUNT))
+                if (info->parse_data->check_for_column_type (GncTransPropType::ACCOUNT) ||
+                        info->parse_data->check_for_column_type (GncTransPropType::OACCOUNT))
                     next_page = 4;
                 else
                     next_page = 5;
@@ -2752,13 +2605,10 @@ csv_import_trans_forward_page_func (gint current_page, gpointer user_data)
             {
                 /* Create transactions from the parsed data, first time with FALSE
                    Subsequent times with TRUE */
-                if (info->match_parse_run == FALSE)
-                    gnc_csv_parse_to_trans (info->parse_data, info->account, FALSE);
-                else
-                    gnc_csv_parse_to_trans (info->parse_data, info->account, TRUE);
+                    info->parse_data->parse_to_trans (info->account, info->match_parse_run);
 
                 /* if there are errors, we jump back to preview to correct */
-                if (!(info->parse_data->error_lines == NULL) && (info->skip_errors == FALSE) )
+                if (info->parse_data->parse_errors && (info->skip_errors == FALSE) )
                 {
                     info->previewing_errors = TRUE; /* We're looking at errors. */
                     next_page = 2;
@@ -2881,7 +2731,7 @@ csv_import_trans_close_handler (gpointer user_data)
 
     /* Free the memory we allocated. */
     if (!(info->parse_data == NULL))
-        gnc_csv_parse_data_free (info->parse_data);
+        delete info->parse_data;
 
     if (!(info->settings_data == NULL))
         gnc_csv_trans_settings_data_free (info->settings_data);

commit b9646e9b9a81e7945e43f7e80691b979cfdf4d12
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Tue Jun 7 21:27:29 2016 +0200

    Build assistant-csv-trans-import.c(pp) as c++

diff --git a/src/import-export/csv-imp/CMakeLists.txt b/src/import-export/csv-imp/CMakeLists.txt
index 026b4bb..789ec1b 100644
--- a/src/import-export/csv-imp/CMakeLists.txt
+++ b/src/import-export/csv-imp/CMakeLists.txt
@@ -5,7 +5,7 @@ SET(csv_import_SOURCES
   gncmod-csv-import.c
   assistant-csv-account-import.c
   assistant-csv-fixed-trans-import.c
-  assistant-csv-trans-import.c
+  assistant-csv-trans-import.cpp
   gnc-plugin-csv-import.c
   csv-account-import.c
   csv-fixed-trans-import.c
diff --git a/src/import-export/csv-imp/Makefile.am b/src/import-export/csv-imp/Makefile.am
index dab3f97..0c4920a 100644
--- a/src/import-export/csv-imp/Makefile.am
+++ b/src/import-export/csv-imp/Makefile.am
@@ -6,7 +6,7 @@ libgncmod_csv_import_la_SOURCES = \
   gncmod-csv-import.c \
   assistant-csv-account-import.c \
   assistant-csv-fixed-trans-import.c \
-  assistant-csv-trans-import.c \
+  assistant-csv-trans-import.cpp \
   gnc-plugin-csv-import.c \
   csv-account-import.c \
   csv-fixed-trans-import.c \
diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.c b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
similarity index 97%
rename from src/import-export/csv-imp/assistant-csv-trans-import.c
rename to src/import-export/csv-imp/assistant-csv-trans-import.cpp
index 751b394..aafabdf 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.c
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -26,6 +26,9 @@
     @brief CSV Import Assistant
     @author Copyright (c) 2012 Robert Fewell
 */
+
+extern "C"
+{
 #include "config.h"
 
 #include <gtk/gtk.h>
@@ -51,6 +54,7 @@
 #include "gnc-csv-model.h"
 #include "gnc-csv-gnumeric-popup.h"
 #include <goffice/go-charmap-sel.h>
+}
 
 #define MIN_COL_WIDTH 70
 #define GNC_PREFS_GROUP "dialogs.import.csv"
@@ -215,7 +219,7 @@ csv_import_trans_load_settings (CsvImportTrans *info)
             const gchar  *title = _("Load the Import Settings.");
 
             dialog = gtk_message_dialog_new (GTK_WINDOW(info->window),
-                                        0,
+                                        (GtkDialogFlags) 0,
                                         GTK_MESSAGE_ERROR,
                                         GTK_BUTTONS_OK,
                                         "%s", title);
@@ -330,7 +334,7 @@ csv_import_trans_load_settings (CsvImportTrans *info)
 
                 for (t = 0; t < GNC_CSV_NUM_COL_TYPES; t++)
                 {
-                    gchar *type = gnc_csv_column_type_strs[t];
+                    const gchar *type = gnc_csv_column_type_strs[t];
 
                     // Check to see if column type is valid
                     if (g_strcmp0 (type, columns[i]) == 0)
@@ -386,7 +390,7 @@ csv_import_trans_delete_settings_cb (GtkWidget *button, CsvImportTrans *info)
         if (g_strcmp0 (group, NULL) == 0)
         {
             dialog = gtk_message_dialog_new (GTK_WINDOW(info->window),
-                                        0,
+                                        (GtkDialogFlags) 0,
                                         GTK_MESSAGE_ERROR,
                                         GTK_BUTTONS_OK,
                                         "%s", title);
@@ -398,7 +402,7 @@ csv_import_trans_delete_settings_cb (GtkWidget *button, CsvImportTrans *info)
         else
         {
             dialog = gtk_message_dialog_new (GTK_WINDOW(info->window),
-                                        0,
+                                        (GtkDialogFlags) 0,
                                         GTK_MESSAGE_QUESTION,
                                         GTK_BUTTONS_OK_CANCEL,
                                         "%s", title);
@@ -463,7 +467,7 @@ csv_import_trans_save_settings_cb (GtkWidget *button, CsvImportTrans *info)
         if (g_strcmp0 (group, NULL) == 0)
         {
             dialog = gtk_message_dialog_new (GTK_WINDOW(info->window),
-                                        0,
+                                        (GtkDialogFlags) 0,
                                         GTK_MESSAGE_ERROR,
                                         GTK_BUTTONS_OK,
                                         "%s", title);
@@ -481,7 +485,7 @@ csv_import_trans_save_settings_cb (GtkWidget *button, CsvImportTrans *info)
         if (strlen (entry_text) == 0 || g_strcmp0 (entry_text, NULL) == 0)
         {
             dialog = gtk_message_dialog_new (GTK_WINDOW(info->window),
-                                        0,
+                                        (GtkDialogFlags) 0,
                                         GTK_MESSAGE_ERROR,
                                         GTK_BUTTONS_OK,
                                         "%s", title);
@@ -521,7 +525,7 @@ csv_import_trans_save_settings_cb (GtkWidget *button, CsvImportTrans *info)
             if (found) // We have found entry_text in liststore
             {
                 dialog = gtk_message_dialog_new (GTK_WINDOW(info->window),
-                                        0,
+                                        (GtkDialogFlags) 0,
                                         GTK_MESSAGE_QUESTION,
                                         GTK_BUTTONS_OK_CANCEL,
                                         "%s", title);
@@ -632,7 +636,7 @@ csv_import_trans_save_settings_cb (GtkWidget *button, CsvImportTrans *info)
             gboolean      valid = FALSE;
 
             dialog = gtk_message_dialog_new (GTK_WINDOW(info->window),
-                                        0,
+                                        (GtkDialogFlags) 0,
                                         GTK_MESSAGE_INFO,
                                         GTK_BUTTONS_OK,
                                         "%s", title);
@@ -665,7 +669,7 @@ csv_import_trans_save_settings_cb (GtkWidget *button, CsvImportTrans *info)
         else
         {
             dialog = gtk_message_dialog_new (GTK_WINDOW(info->window),
-                                        0,
+                                        (GtkDialogFlags) 0,
                                         GTK_MESSAGE_ERROR,
                                         GTK_BUTTONS_OK,
                                         "%s", title);
@@ -847,7 +851,7 @@ void row_selection_update (CsvImportTrans* info)
  *******************************************************/
 void csv_import_trans_srow_cb (GtkWidget *spin, gpointer user_data)
 {
-    CsvImportTrans *info = user_data;
+    CsvImportTrans *info = (CsvImportTrans*) user_data;
     GtkAdjustment *adj;
 
     /* Get number of rows for header */
@@ -870,7 +874,7 @@ void csv_import_trans_srow_cb (GtkWidget *spin, gpointer user_data)
  *******************************************************/
 void csv_import_trans_erow_cb (GtkWidget *spin, gpointer user_data)
 {
-    CsvImportTrans *info = user_data;
+    CsvImportTrans *info = (CsvImportTrans*) user_data;
     GtkAdjustment *adj;
 
     /* Get number of rows for header */
@@ -893,7 +897,7 @@ void csv_import_trans_erow_cb (GtkWidget *spin, gpointer user_data)
  *******************************************************/
 void csv_import_trans_skip_errors_cb (GtkWidget *cb, gpointer user_data)
 {
-    CsvImportTrans *info = user_data;
+    CsvImportTrans *info = (CsvImportTrans*) user_data;
 
     if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(cb)))
         info->skip_errors = TRUE;
@@ -909,7 +913,7 @@ void csv_import_trans_skip_errors_cb (GtkWidget *cb, gpointer user_data)
  *******************************************************/
 void csv_import_trans_skiprows_cb (GtkWidget *checkbox, gpointer user_data)
 {
-    CsvImportTrans *info = user_data;
+    CsvImportTrans *info = (CsvImportTrans*) user_data;
 
     /* Set the skip_rows variable */
     info->parse_data->skip_rows = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(checkbox));
@@ -944,7 +948,7 @@ static GtkCellRenderer* gnc_csv_preview_get_cell_renderer (CsvImportTrans* info,
 static void sep_button_clicked (GtkWidget* widget, CsvImportTrans* info)
 {
     int i;
-    char* stock_separator_characters[] = {" ", "\t", ",", ":", ";", "-"};
+    const char* stock_separator_characters[] = {" ", "\t", ",", ":", ";", "-"};
     GSList* checked_separators = NULL;
     GError* error;
 
@@ -953,7 +957,7 @@ static void sep_button_clicked (GtkWidget* widget, CsvImportTrans* info)
     for (i = 0; i < SEP_NUM_OF_TYPES; i++)
     {
         if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(info->sep_buttons[i])))
-            checked_separators = g_slist_append (checked_separators, stock_separator_characters[i]);
+            checked_separators = g_slist_append (checked_separators, (gpointer) stock_separator_characters[i]);
     }
 
     /* Add the custom separator if the user checked its button. */
@@ -1324,7 +1328,7 @@ static gboolean
 fixed_context_menu_handler (GnumericPopupMenuElement const *element,
                             gpointer user_data)
 {
-    CsvImportTrans* info = user_data;
+    CsvImportTrans *info = (CsvImportTrans*) user_data;
     int col = info->fixed_context_col;
 
     switch (element->index)
@@ -1452,13 +1456,12 @@ static void column_type_changed (GtkCellRenderer* renderer, gchar* path,
     {
         /* We need all this stuff so that we can find out whether or not
          * this was the column that was changed. */
-        GtkCellRenderer* col_renderer; /* The renderer for this column. */
         /* The column in the treeview we are looking at */
         GtkTreeViewColumn* col = gtk_tree_view_get_column (info->ctreeview, i);
         /* The list of renderers for col */
         GList* rend_list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT(col));
         /* rend_list has only one entry, which we put in col_renderer. */
-        col_renderer = rend_list->data;
+        GtkCellRenderer* col_renderer = (GtkCellRenderer*) rend_list->data;
         g_list_free (rend_list);
 
         /* If this is not the column that was changed ... */
@@ -1804,8 +1807,8 @@ static void gnc_csv_preview_update_assist (CsvImportTrans* info)
     GtkTreeIter iter;
     GtkTreeSelection *selection;
     /* ncols is the number of columns in the file data. */
-    int i, j, ncols = info->parse_data->column_types->len,
-              max_str_len = info->parse_data->file_str.end - info->parse_data->file_str.begin;
+    guint i, j, ncols = info->parse_data->column_types->len,
+                max_str_len = info->parse_data->file_str.end - info->parse_data->file_str.begin;
 
     /* store contains only strings. */
     GType* types = g_new (GType, 2 * ncols);
@@ -2032,35 +2035,35 @@ void gnc_csv_reset_preview_setting (CsvImportTrans *info, gboolean block)
     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->skip_rows), FALSE);
 
     // Reset Import Format
-    g_signal_handlers_block_by_func (info->csv_button, separated_or_fixed_selected, info);
+    g_signal_handlers_block_by_func (info->csv_button, (gpointer) separated_or_fixed_selected, info);
     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->csv_button), TRUE);
-    g_signal_handlers_unblock_by_func (info->csv_button, separated_or_fixed_selected, info);
+    g_signal_handlers_unblock_by_func (info->csv_button, (gpointer) separated_or_fixed_selected, info);
 
     if (block) // We need to block these when we go back to the file page
     {
-        g_signal_handlers_block_by_func (info->custom_cbutton, sep_button_clicked, info);
-        g_signal_handlers_block_by_func (info->custom_entry, sep_button_clicked, info);
+        g_signal_handlers_block_by_func (info->custom_cbutton, (gpointer) sep_button_clicked, info);
+        g_signal_handlers_block_by_func (info->custom_entry, (gpointer) sep_button_clicked, info);
     }
 
     // Reset the separators
     for (i = 0; i < SEP_NUM_OF_TYPES; i++)
     {
         if (block) // We need to block these when we go back to the file page
-            g_signal_handlers_block_by_func (info->sep_buttons[i], sep_button_clicked, info);
+            g_signal_handlers_block_by_func (info->sep_buttons[i], (gpointer) sep_button_clicked, info);
         if (i == 2)
             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->sep_buttons[i]), TRUE);
         else
             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->sep_buttons[i]), FALSE);
         if (block) // Now unblock
-            g_signal_handlers_unblock_by_func (info->sep_buttons[i], sep_button_clicked, info);
+            g_signal_handlers_unblock_by_func (info->sep_buttons[i], (gpointer) sep_button_clicked, info);
     }
     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->custom_cbutton), FALSE);
     gtk_entry_set_text (GTK_ENTRY(info->custom_entry), "");
 
     if (block) // Now unblock
     {
-        g_signal_handlers_unblock_by_func (info->custom_cbutton, sep_button_clicked, info);
-        g_signal_handlers_unblock_by_func (info->custom_entry, sep_button_clicked, info);
+        g_signal_handlers_unblock_by_func (info->custom_cbutton, (gpointer) sep_button_clicked, info);
+        g_signal_handlers_unblock_by_func (info->custom_entry, (gpointer) sep_button_clicked, info);
     }
 
     // Reset the combo's and character encoding
@@ -2101,7 +2104,7 @@ void
 csv_import_trans_assistant_start_page_prepare (GtkAssistant *assistant,
         gpointer user_data)
 {
-    CsvImportTrans *info = user_data;
+    CsvImportTrans *info = (CsvImportTrans*) user_data;
     GtkTreeModel   *store;
 
     gint num = gtk_assistant_get_current_page (assistant);
@@ -2120,7 +2123,7 @@ void
 csv_import_trans_assistant_file_page_prepare (GtkAssistant *assistant,
         gpointer user_data)
 {
-    CsvImportTrans *info = user_data;
+    CsvImportTrans *info = (CsvImportTrans*) user_data;
     GtkAdjustment  *adj;
     GtkTreeModel   *settings_store;
     gint            num = gtk_assistant_get_current_page (assistant);
@@ -2155,7 +2158,7 @@ void
 csv_import_trans_assistant_preview_page_prepare (GtkAssistant *assistant,
         gpointer user_data)
 {
-    CsvImportTrans *info = user_data;
+    CsvImportTrans *info = (CsvImportTrans*) user_data;
     GtkAdjustment *adj;
 
     g_signal_connect (G_OBJECT(info->treeview), "size-allocate",
@@ -2226,7 +2229,7 @@ void
 csv_import_trans_assistant_account_page_prepare (GtkAssistant *assistant,
         gpointer user_data)
 {
-    CsvImportTrans *info = user_data;
+    CsvImportTrans *info = (CsvImportTrans*) user_data;
     gint num = gtk_assistant_get_current_page (assistant);
     GtkWidget *page = gtk_assistant_get_nth_page (assistant, num);
     gchar *text, *mtext;
@@ -2386,7 +2389,7 @@ import_account_text_parse (gchar *text)
 static void
 import_account_select_cb (GtkWidget *widget, gpointer user_data)
 {
-    CsvImportTrans *info = user_data;
+    CsvImportTrans *info = (CsvImportTrans*) user_data;
     GtkTreeSelection *selection;
     GtkTreeModel *model;
     GtkTreeIter iter;
@@ -2437,7 +2440,7 @@ import_account_select_cb (GtkWidget *widget, gpointer user_data)
 static gboolean
 import_account_button_cb (GtkWidget *widget, GdkEventButton *event, gpointer user_data)
 {
-    CsvImportTrans     *info = user_data;
+    CsvImportTrans     *info = (CsvImportTrans*) user_data;
     GtkTreeModel       *model;
     GtkTreeIter         iter;
     GtkTreePath        *path;
@@ -2507,7 +2510,7 @@ void
 csv_import_trans_assistant_account_match_page_prepare (GtkAssistant *assistant,
         gpointer user_data)
 {
-    CsvImportTrans *info = user_data;
+    CsvImportTrans *info = (CsvImportTrans*) user_data;
     gint            num = gtk_assistant_get_current_page (assistant);
     gchar          *text, *mtext;
 
@@ -2555,7 +2558,7 @@ void
 csv_import_trans_assistant_doc_page_prepare (GtkAssistant *assistant,
         gpointer user_data)
 {
-    CsvImportTrans *info = user_data;
+    CsvImportTrans *info = (CsvImportTrans*) user_data;
 
     /* Block going back */
     gtk_assistant_commit (GTK_ASSISTANT(info->window));
@@ -2582,7 +2585,7 @@ void
 csv_import_trans_assistant_match_page_prepare (GtkAssistant *assistant,
         gpointer user_data)
 {
-    CsvImportTrans *info = user_data;
+    CsvImportTrans *info = (CsvImportTrans*) user_data;
     gint num = gtk_assistant_get_current_page (assistant);
     GtkWidget *page = gtk_assistant_get_nth_page (assistant, num);
     gchar *text, *mtext;
@@ -2616,7 +2619,7 @@ csv_import_trans_assistant_match_page_prepare (GtkAssistant *assistant,
             /* Copy all of the transactions to the importer GUI. */
             while (transactions != NULL)
             {
-                GncCsvTransLine* trans_line = transactions->data;
+                GncCsvTransLine* trans_line = (GncCsvTransLine*) transactions->data;
                 gnc_gen_trans_list_add_trans (info->gnc_csv_importer_gui, trans_line->trans);
                 transactions = g_list_next (transactions);
             }
@@ -2632,7 +2635,7 @@ void
 csv_import_trans_assistant_summary_page_prepare (GtkAssistant *assistant,
         gpointer user_data)
 {
-    CsvImportTrans *info = user_data;
+    CsvImportTrans *info = (CsvImportTrans*) user_data;
     gchar *text, *mtext;
 
     /* Save the Window size and directory */
@@ -2653,7 +2656,7 @@ csv_import_trans_assistant_summary_page_prepare (GtkAssistant *assistant,
 static gint
 csv_import_trans_forward_page_func (gint current_page, gpointer user_data)
 {
-    CsvImportTrans *info = user_data;
+    CsvImportTrans *info = (CsvImportTrans*) user_data;
     gint next_page = 0;
 
     /* Note: This function gets called multiple times by the GtkAssistant code and so
@@ -2800,7 +2803,7 @@ void
 csv_import_trans_assistant_prepare (GtkAssistant *assistant, GtkWidget *page,
                                     gpointer user_data)
 {
-    CsvImportTrans *info = user_data;
+    CsvImportTrans *info = (CsvImportTrans*) user_data;
 
     // Reset callcount on every prepare
     info->callcount = 0;
@@ -2832,7 +2835,7 @@ csv_import_trans_assistant_prepare (GtkAssistant *assistant, GtkWidget *page,
 static void
 csv_import_trans_assistant_destroy_cb (GtkWidget *object, gpointer user_data)
 {
-    CsvImportTrans *info = user_data;
+    CsvImportTrans *info = (CsvImportTrans*) user_data;
     gnc_unregister_gui_component_by_data (ASSISTANT_CSV_IMPORT_TRANS_CM_CLASS, info);
     g_free (info);
 }
@@ -2840,7 +2843,7 @@ csv_import_trans_assistant_destroy_cb (GtkWidget *object, gpointer user_data)
 void
 csv_import_trans_assistant_cancel (GtkAssistant *assistant, gpointer user_data)
 {
-    CsvImportTrans *info = user_data;
+    CsvImportTrans *info = (CsvImportTrans*) user_data;
 
     if (!(info->gnc_csv_importer_gui == NULL))
         gnc_gen_trans_list_delete (info->gnc_csv_importer_gui);
@@ -2851,14 +2854,14 @@ csv_import_trans_assistant_cancel (GtkAssistant *assistant, gpointer user_data)
 void
 csv_import_trans_assistant_close (GtkAssistant *assistant, gpointer user_data)
 {
-    CsvImportTrans *info = user_data;
+    CsvImportTrans *info = (CsvImportTrans*) user_data;
     gnc_close_gui_component_by_data (ASSISTANT_CSV_IMPORT_TRANS_CM_CLASS, info);
 }
 
 void
 csv_import_trans_assistant_finish (GtkAssistant *assistant, gpointer user_data)
 {
-    CsvImportTrans *info = user_data;
+    CsvImportTrans *info = (CsvImportTrans*) user_data;
 
     /* Start the import */
     if (info->parse_data->transactions != NULL)
@@ -2870,7 +2873,7 @@ csv_import_trans_assistant_finish (GtkAssistant *assistant, gpointer user_data)
 static void
 csv_import_trans_close_handler (gpointer user_data)
 {
-    CsvImportTrans *info = user_data;
+    CsvImportTrans *info = (CsvImportTrans*) user_data;
 
     g_free(info->file_name);
     g_free(info->starting_dir);
@@ -2968,13 +2971,13 @@ csv_import_trans_assistant_create (CsvImportTrans *info)
 
     /* Preview Settings Page */
     {
-        char* sep_button_names[] = {"space_cbutton",
-                                    "tab_cbutton",
-                                    "comma_cbutton",
-                                    "colon_cbutton",
-                                    "semicolon_cbutton",
-                                    "hyphen_cbutton"
-                                   };
+        const char* sep_button_names[] = {"space_cbutton",
+                                          "tab_cbutton",
+                                          "comma_cbutton",
+                                          "colon_cbutton",
+                                          "semicolon_cbutton",
+                                          "hyphen_cbutton"
+        };
         GtkContainer *date_format_container, *currency_format_container;
         int           i;
         GtkTable     *enctable;

commit 142fb61724823149e830e63b01e384b45855182d
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sun Jun 12 12:41:51 2016 +0200

    Introduce parse error state

diff --git a/src/import-export/csv-imp/gnc-csv-imp-trans.cpp b/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
index dfb3f28..8a2b24d 100644
--- a/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
+++ b/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
@@ -230,6 +230,7 @@ GncCsvParseData::GncCsvParseData(GncImpFileFormat format)
     start_row = 0;
     end_row = 1000;
     skip_rows = FALSE;
+    parse_errors = false;
 
     file_fmt = format;
     tokenizer = GncTokenizerFactory(file_fmt);
@@ -899,6 +900,7 @@ int GncCsvParseData::parse_to_trans (Account* account,
 
     Account *home_account = NULL;
     bool odd_line = false;
+    parse_errors = false;
     for (orig_lines_it, odd_line;
             orig_lines_it != orig_lines_max;
             ++orig_lines_it, odd_line = !odd_line)
@@ -931,6 +933,7 @@ int GncCsvParseData::parse_to_trans (Account* account,
 
         if (home_account == NULL)
         {
+            parse_errors = true;
             orig_lines_it->second = _("Account column could not be understood.");
             continue;
         }
@@ -952,7 +955,7 @@ int GncCsvParseData::parse_to_trans (Account* account,
                     trans_property_list_add (property);
                 else
                 {
-                    loop_err = true;
+                    parse_errors = loop_err = true;
                     gchar *error_message = g_strdup_printf (_("%s column could not be understood."),
                                                     _(gnc_csv_col_type_strs[property->type]));
                     orig_lines_it->second = error_message;
@@ -972,6 +975,7 @@ int GncCsvParseData::parse_to_trans (Account* account,
         trans_line = trans_property_list_to_trans (list, &error_message);
         if (trans_line == NULL)
         {
+            parse_errors = true;
             orig_lines_it->second = error_message;
             g_free (error_message);
             trans_property_list_free (list);
diff --git a/src/import-export/csv-imp/gnc-csv-imp-trans.hpp b/src/import-export/csv-imp/gnc-csv-imp-trans.hpp
index ac6fc0d..f6e8266 100644
--- a/src/import-export/csv-imp/gnc-csv-imp-trans.hpp
+++ b/src/import-export/csv-imp/gnc-csv-imp-trans.hpp
@@ -135,6 +135,7 @@ public:
     guint end_row;              /**< The end row to generate transactions from. */
     gboolean skip_rows;         /**< Skip Alternate Rows from start row. */
     int currency_format;        /**< The currency format, 0 for locale, 1 for comma dec and 2 for period */
+    bool parse_errors;          /**< Indicates whether the last parse_to_trans run had any errors */
 
 private:
     GncImpFileFormat file_fmt = GncImpFileFormat::UNKNOWN;

commit 0f6dc53cd1c8bb4687b1e5f4a0ac412fb13d5322
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sun Jun 12 12:38:50 2016 +0200

    Small local variable cleanups

diff --git a/src/import-export/csv-imp/gnc-csv-imp-trans.cpp b/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
index 5b28577..dfb3f28 100644
--- a/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
+++ b/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
@@ -854,12 +854,6 @@ static GncCsvTransLine* trans_property_list_to_trans (TransPropertyList* list, g
 int GncCsvParseData::parse_to_trans (Account* account,
                                      gboolean redo_errors)
 {
-    Account *home_account = NULL;
-
-    /* last_transaction points to the last element in
-     * transactions, or NULL if it's empty. */
-    GList* last_transaction = NULL;
-
     /* Free error_lines and transactions if they
      * already exist. */
     if (!redo_errors) /* If we're redoing errors, we save freeing until the end. */
@@ -871,6 +865,9 @@ int GncCsvParseData::parse_to_trans (Account* account,
             g_list_free (transactions);
     }
 
+    /* last_transaction points to the last element in
+     * transactions, or NULL if it's empty. */
+    GList* last_transaction = NULL;
     if (redo_errors) /* If we're looking only at error data ... */
     {
         if (transactions == NULL)
@@ -900,6 +897,7 @@ int GncCsvParseData::parse_to_trans (Account* account,
     else
         std::advance(orig_lines_max, end_row);
 
+    Account *home_account = NULL;
     bool odd_line = false;
     for (orig_lines_it, odd_line;
             orig_lines_it != orig_lines_max;

commit d85de0124c4e1ef794d394f7140f303c3277aec9
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Tue Jun 7 10:33:38 2016 +0200

    C++ - use std::pair to store tokenized line together with its error message
    
    Also drop a few variables that carry superfluous data and add more c++11 semantics

diff --git a/src/import-export/csv-imp/gnc-csv-imp-trans.cpp b/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
index f4891fd..5b28577 100644
--- a/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
+++ b/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
@@ -335,9 +335,16 @@ int GncCsvParseData::load_file (const char* filename,
  */
 int GncCsvParseData::parse (gboolean guessColTypes, GError** error)
 {
+    uint max_cols = 0;
     tokenizer->tokenize();
     orig_lines.clear();
-    orig_lines = tokenizer->get_tokens();
+    for (auto tokenized_line : tokenizer->get_tokens())
+    {
+        orig_lines.push_back (std::make_pair (tokenized_line, std::string()));
+        auto length = tokenized_line.size();
+        if (length > max_cols)
+            max_cols = length;
+    }
 
     /* If it failed, generate an error. */
     if (orig_lines.size() == 0)
@@ -346,26 +353,12 @@ int GncCsvParseData::parse (gboolean guessColTypes, GError** error)
         return 1;
     }
 
-
-    /* Record the original row lengths of orig_lines. */
-    orig_row_lengths.clear();
-
-    orig_row_lengths.reserve(orig_lines.size());
-    orig_max_row = 0;
-    for(std::vector<str_vec>::iterator it = orig_lines.begin(); it != orig_lines.end(); ++it)
-    {
-        std::vector<std::string>::size_type length = it->size();
-        orig_row_lengths.push_back(length);
-        if (length > orig_max_row)
-            orig_max_row = length;
-    }
-
     if (guessColTypes)
     {
         /* Free column_types if it's already been created. */
         column_types.clear();
     }
-    column_types.resize(orig_max_row, GncTransPropType::NONE);
+    column_types.resize(max_cols, GncTransPropType::NONE);
 
     if (guessColTypes)
     {
@@ -871,8 +864,8 @@ int GncCsvParseData::parse_to_trans (Account* account,
      * already exist. */
     if (!redo_errors) /* If we're redoing errors, we save freeing until the end. */
     {
-        line_errors.clear();
-        line_errors.resize(orig_lines.size());
+        for (auto orig_line : orig_lines)
+            orig_line.second.clear();
 
         if (transactions != NULL)
             g_list_free (transactions);
@@ -898,21 +891,19 @@ int GncCsvParseData::parse_to_trans (Account* account,
     }
 
     /* compute start and end iterators based on user-set restrictions */
-    auto line_errs_it = line_errors.begin();
-    std::advance(line_errs_it, start_row);
-    auto orig_lines_it = orig_lines.cbegin();
+    auto orig_lines_it = orig_lines.begin();
     std::advance(orig_lines_it, start_row);
 
-    auto orig_lines_max = orig_lines.cbegin();
+    auto orig_lines_max = orig_lines.begin();
     if (end_row > orig_lines.size())
-        orig_lines_max = orig_lines.cend();
+        orig_lines_max = orig_lines.end();
     else
         std::advance(orig_lines_max, end_row);
 
     bool odd_line = false;
-    for (orig_lines_it, line_errs_it, odd_line;
+    for (orig_lines_it, odd_line;
             orig_lines_it != orig_lines_max;
-            ++orig_lines_it, ++line_errs_it, odd_line = !odd_line)
+            ++orig_lines_it, odd_line = !odd_line)
     {
         /* Skip current line if:
            1. only looking for lines with error AND no error on current line
@@ -920,11 +911,11 @@ int GncCsvParseData::parse_to_trans (Account* account,
            2. looking for all lines AND
               skip_rows is enabled AND
               current line is an odd line */
-        if ((redo_errors && line_errs_it->empty()) ||
+        if ((redo_errors && orig_lines_it->second.empty()) ||
            (!redo_errors && skip_rows && odd_line))
             continue;
 
-        std::vector<std::string> line = *orig_lines_it;
+        auto line = orig_lines_it->first;
         GncCsvTransLine* trans_line = NULL;
 
         home_account = account;
@@ -942,7 +933,7 @@ int GncCsvParseData::parse_to_trans (Account* account,
 
         if (home_account == NULL)
         {
-            *line_errs_it = _("Account column could not be understood.");
+            orig_lines_it->second = _("Account column could not be understood.");
             continue;
         }
 
@@ -966,7 +957,7 @@ int GncCsvParseData::parse_to_trans (Account* account,
                     loop_err = true;
                     gchar *error_message = g_strdup_printf (_("%s column could not be understood."),
                                                     _(gnc_csv_col_type_strs[property->type]));
-                    *line_errs_it = error_message;
+                    orig_lines_it->second = error_message;
 
                     g_free (error_message);
                     trans_property_free (property);
@@ -983,7 +974,7 @@ int GncCsvParseData::parse_to_trans (Account* account,
         trans_line = trans_property_list_to_trans (list, &error_message);
         if (trans_line == NULL)
         {
-            *line_errs_it = error_message;
+            orig_lines_it->second = error_message;
             g_free (error_message);
             trans_property_list_free (list);
             continue;
diff --git a/src/import-export/csv-imp/gnc-csv-imp-trans.hpp b/src/import-export/csv-imp/gnc-csv-imp-trans.hpp
index 1d983ec..ac6fc0d 100644
--- a/src/import-export/csv-imp/gnc-csv-imp-trans.hpp
+++ b/src/import-export/csv-imp/gnc-csv-imp-trans.hpp
@@ -105,7 +105,8 @@ extern const gchar* date_format_user[];
 /* This array contains all of the different strings for different column types. */
 extern const gchar* gnc_csv_col_type_strs[];
 
-using str_vec_t = std::vector<std::string> ;
+/** Pair to hold a tokenized line of input and an optional error string */
+using parse_line_t = std::pair<str_vec, std::string>;
 
 /** Struct containing data for parsing a CSV/Fixed-Width file. */
 class GncCsvParseData
@@ -126,8 +127,7 @@ public:
     bool check_for_column_type (GncTransPropType type);
 
     std::unique_ptr<GncTokenizer> tokenizer;    /**< Will handle file loading/encoding conversion/splitting into fields */
-    std::vector<str_vec_t> orig_lines;      /**< file_str parsed into a two-dimensional array of strings */
-    std::vector<str_vec>::size_type orig_max_row;           /**< Holds the maximum value in orig_row_lengths */
+    std::vector<parse_line_t> orig_lines;      /**< file_str parsed into a two-dimensional array of strings */
     std::vector<GncTransPropType> column_types;       /**< Vector of values from the GncCsvColumnType enumeration */
     GList* transactions;        /**< List of GncCsvTransLine*s created using orig_lines and column_types */
     int date_format;            /**< The format of the text in the date columns from date_format_internal. */
@@ -137,10 +137,6 @@ public:
     int currency_format;        /**< The currency format, 0 for locale, 1 for comma dec and 2 for period */
 
 private:
-    std::vector<std::vector<str_vec>::size_type>
-              orig_row_lengths; /**< The lengths of rows in orig_lines
-                                      before error messages are appended */
-    std::vector<std::string> line_errors;
     GncImpFileFormat file_fmt = GncImpFileFormat::UNKNOWN;
 };
 

commit 17b2b4668ee4eb9d3a091a18df4198437afc00fe
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sun Jan 31 13:53:45 2016 +0100

    C++11 Convert unscoped enum into scoped one
    
    As per recommendation 10 in Meyer's Effective Modern C++
    
    This also means the string array with column type names
    can no longer be shared between c and c++ code, so
    set up a separate one in c++

diff --git a/src/import-export/csv-imp/gnc-csv-imp-trans.cpp b/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
index 4bdc07d..f4891fd 100644
--- a/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
+++ b/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
@@ -124,21 +124,21 @@ const char* date_regex[] = {
 //                                       N_("Comma: 123.456,78")
 //                                      };
 //
-///* This array contains all of the different strings for different column types. */
-//const gchar* gnc_csv_column_type_strs[GNC_CSV_NUM_COL_TYPES] = {
-//        N_("None"),
-//        N_("Date"),
-//        N_("Num"),
-//        N_("Description"),
-//        N_("Notes"),
-//        N_("Account"),
-//        N_("Deposit"),
-//        N_("Withdrawal"),
-//        N_("Balance"),
-//        N_("Memo"),
-//        N_("Other Account"),
-//        N_("Other Memo")
-//};
+/* This array contains all of the different strings for different column types. */
+const gchar* gnc_csv_col_type_strs[GncTransPropType::NUM_COL_TYPES] = {
+        N_("None"),
+        N_("Date"),
+        N_("Num"),
+        N_("Description"),
+        N_("Notes"),
+        N_("Account"),
+        N_("Deposit"),
+        N_("Withdrawal"),
+        N_("Balance"),
+        N_("Memo"),
+        N_("Other Account"),
+        N_("Other Memo")
+};
 
 /** Parses a string into a date, given a format. This function
  * requires only knowing the order in which the year, month and day
@@ -326,7 +326,7 @@ int GncCsvParseData::load_file (const char* filename,
  * set according to how the user wants before calling this
  * function. (Note: this function must be called with guessColTypes as
  * TRUE before it is ever called with it as FALSE.) (Note: if
- * guessColTypes is TRUE, all the column types will be GNC_CSV_NONE
+ * guessColTypes is TRUE, all the column types will be GncTransPropType::NONE
  * right now.)
  * @param parse_data Data that is being parsed
  * @param guessColTypes TRUE to guess what the types of columns are based on the cell contents
@@ -365,7 +365,7 @@ int GncCsvParseData::parse (gboolean guessColTypes, GError** error)
         /* Free column_types if it's already been created. */
         column_types.clear();
     }
-    column_types.resize(orig_max_row, GNC_CSV_NONE);
+    column_types.resize(orig_max_row, GncTransPropType::NONE);
 
     if (guessColTypes)
     {
@@ -388,8 +388,8 @@ typedef struct
 /** A struct encapsulating a property of a transaction. */
 typedef struct
 {
-    int type;                /**< A value from the GncCsvColumnType enum except
-                               * GNC_CSV_NONE and GNC_CSV_NUM_COL_TYPES */
+    GncTransPropType type;   /**< A value from the GncTransPropType enum except
+                               * GncTransPropType::NONE and GncTransPropType::NUM_COL_TYPES */
     void* value;             /**< Pointer to the data that will be used to configure a transaction */
     TransPropertyList* list; /**< The list the property belongs to */
 } TransProperty;
@@ -397,7 +397,7 @@ typedef struct
 /** Constructor for TransProperty.
  * @param type The type of the new property (see TransProperty.type for possible values)
  */
-static TransProperty* trans_property_new (int type, TransPropertyList* list)
+static TransProperty* trans_property_new (GncTransPropType type, TransPropertyList* list)
 {
     TransProperty* prop = g_new (TransProperty, 1);
     prop->type = type;
@@ -416,10 +416,10 @@ static void trans_property_free (TransProperty* prop)
         /* The types for "Date" and "Balance" (time64 and gnc_numeric,
          * respectively) are typically not pointed to, we have to free
          * them, unlike types like char* ("Description"). */
-    case GNC_CSV_DATE:
-    case GNC_CSV_BALANCE:
-    case GNC_CSV_DEPOSIT:
-    case GNC_CSV_WITHDRAWAL:
+    case GncTransPropType::DATE:
+    case GncTransPropType::BALANCE:
+    case GncTransPropType::DEPOSIT:
+    case GncTransPropType::WITHDRAWAL:
         if (prop->value != NULL)
             g_free(prop->value);
         break;
@@ -444,26 +444,26 @@ static gboolean trans_property_set (TransProperty* prop, const char* str)
     regex_t regex;
     switch (prop->type)
     {
-    case GNC_CSV_DATE:
+    case GncTransPropType::DATE:
         prop->value = g_new(time64, 1);
         *((time64*)(prop->value)) = parse_date(str, prop->list->date_format);
         return *((time64*)(prop->value)) != -1;
 
-    case GNC_CSV_DESCRIPTION:
-    case GNC_CSV_NOTES:
-    case GNC_CSV_MEMO:
-    case GNC_CSV_OMEMO:
-    case GNC_CSV_NUM:
+    case GncTransPropType::DESCRIPTION:
+    case GncTransPropType::NOTES:
+    case GncTransPropType::MEMO:
+    case GncTransPropType::OMEMO:
+    case GncTransPropType::NUM:
         prop->value = g_strdup (str);
         return TRUE;
 
-    case GNC_CSV_OACCOUNT:
+    case GncTransPropType::OACCOUNT:
         prop->value = gnc_csv_account_map_search (str);
         return TRUE;
 
-    case GNC_CSV_BALANCE:
-    case GNC_CSV_DEPOSIT:
-    case GNC_CSV_WITHDRAWAL:
+    case GncTransPropType::BALANCE:
+    case GncTransPropType::DEPOSIT:
+    case GncTransPropType::WITHDRAWAL:
         str_dupe = g_strdup (str); /* First, we make a copy so we can't mess up real data. */
         /* If a cell is empty or just spaces make its value = "0" */
         reti = regcomp(&regex, "[0-9]", 0);
@@ -637,13 +637,13 @@ static gboolean trans_property_list_verify_essentials (TransPropertyList* list,
     {
         switch (((TransProperty*)(list->properties->data))->type)
         {
-        case GNC_CSV_DATE:
+        case GncTransPropType::DATE:
             possible_errors[NO_DATE] = NULL;
             break;
 
-        case GNC_CSV_BALANCE:
-        case GNC_CSV_DEPOSIT:
-        case GNC_CSV_WITHDRAWAL:
+        case GncTransPropType::BALANCE:
+        case GncTransPropType::DEPOSIT:
+        case GncTransPropType::WITHDRAWAL:
             possible_errors[NO_AMOUNT] = NULL;
             break;
         default:
@@ -756,31 +756,31 @@ static GncCsvTransLine* trans_property_list_to_trans (TransPropertyList* list, g
         TransProperty* prop = (TransProperty*)(list->properties->data);
         switch (prop->type)
         {
-        case GNC_CSV_DATE:
+        case GncTransPropType::DATE:
             xaccTransSetDatePostedSecsNormalized (trans_line->trans, *((time64*)(prop->value)));
             break;
 
-        case GNC_CSV_DESCRIPTION:
+        case GncTransPropType::DESCRIPTION:
             xaccTransSetDescription (trans_line->trans, (char*)(prop->value));
             break;
 
-        case GNC_CSV_NOTES:
+        case GncTransPropType::NOTES:
             xaccTransSetNotes (trans_line->trans, (char*)(prop->value));
             break;
 
-        case GNC_CSV_OACCOUNT:
+        case GncTransPropType::OACCOUNT:
             oaccount = ((Account*)(prop->value));
             break;
 
-        case GNC_CSV_MEMO:
+        case GncTransPropType::MEMO:
             memo = g_strdup ((char*)(prop->value));
             break;
 
-        case GNC_CSV_OMEMO:
+        case GncTransPropType::OMEMO:
             omemo = g_strdup ((char*)(prop->value));
             break;
 
-        case GNC_CSV_NUM:
+        case GncTransPropType::NUM:
             /* the 'num' is saved and passed to 'trans_add_split' below where
              * 'gnc_set_num_action' is used to set tran-num and/or split-action
              * per book option */
@@ -791,7 +791,7 @@ static GncCsvTransLine* trans_property_list_to_trans (TransPropertyList* list, g
             trans_line->num = g_strdup ((char*)(prop->value));
             break;
 
-        case GNC_CSV_DEPOSIT: /* Add deposits to the existing amount. */
+        case GncTransPropType::DEPOSIT: /* Add deposits to the existing amount. */
             if (prop->value != NULL)
             {
                 amount = gnc_numeric_add (*((gnc_numeric*)(prop->value)),
@@ -804,7 +804,7 @@ static GncCsvTransLine* trans_property_list_to_trans (TransPropertyList* list, g
             }
             break;
 
-        case GNC_CSV_WITHDRAWAL: /* Withdrawals are just negative deposits. */
+        case GncTransPropType::WITHDRAWAL: /* Withdrawals are just negative deposits. */
             if (prop->value != NULL)
             {
                 amount = gnc_numeric_add (gnc_numeric_neg(*((gnc_numeric*)(prop->value))),
@@ -817,7 +817,7 @@ static GncCsvTransLine* trans_property_list_to_trans (TransPropertyList* list, g
             }
             break;
 
-        case GNC_CSV_BALANCE: /* The balance gets stored in a separate field in trans_line. */
+        case GncTransPropType::BALANCE: /* The balance gets stored in a separate field in trans_line. */
             /* We will use the "Deposit" and "Withdrawal" columns in preference to "Balance". */
             if (!amount_set && prop->value != NULL)
             {
@@ -935,7 +935,7 @@ int GncCsvParseData::parse_to_trans (Account* account,
             for (uint j = 0; j < line.size(); j++)
             {
                 /* Look for "Account" columns. */
-                if (column_types[j] == GNC_CSV_ACCOUNT)
+                if (column_types[j] == GncTransPropType::ACCOUNT)
                     home_account = gnc_csv_account_map_search (line[j].c_str());
             }
         }
@@ -952,7 +952,7 @@ int GncCsvParseData::parse_to_trans (Account* account,
         for (uint j = 0; j < line.size(); j++)
         {
             /* We do nothing in "None" or "Account" columns. */
-            if ((column_types[j] != GNC_CSV_NONE) && (column_types[j] != GNC_CSV_ACCOUNT))
+            if ((column_types[j] != GncTransPropType::NONE) && (column_types[j] != GncTransPropType::ACCOUNT))
             {
                 /* Affect the transaction appropriately. */
                 TransProperty* property = trans_property_new (column_types[j], list);
@@ -965,7 +965,7 @@ int GncCsvParseData::parse_to_trans (Account* account,
                 {
                     loop_err = true;
                     gchar *error_message = g_strdup_printf (_("%s column could not be understood."),
-                                                    _(gnc_csv_column_type_strs[property->type]));
+                                                    _(gnc_csv_col_type_strs[property->type]));
                     *line_errs_it = error_message;
 
                     g_free (error_message);
@@ -1026,7 +1026,7 @@ int GncCsvParseData::parse_to_trans (Account* account,
         }
     }
 
-    if (std::find(column_types.begin(),column_types.end(), GNC_CSV_BALANCE) !=
+    if (std::find(column_types.begin(),column_types.end(), GncTransPropType::BALANCE) !=
         column_types.end()) // This is only used if we have one home account
     {
         Split      *split, *osplit;
@@ -1091,15 +1091,7 @@ int GncCsvParseData::parse_to_trans (Account* account,
 
 
 bool
-GncCsvParseData::check_for_column_type (int type)
+GncCsvParseData::check_for_column_type (GncTransPropType type)
 {
-    gboolean ret = FALSE;
-    int j, ncols = column_types.size(); /* ncols is the number of columns in the data. */
-
-    for (j = 0; j < ncols; j++)
-    {
-        if (column_types[j] == type)
-            ret = TRUE;
-    }
-    return ret;
+    return (std::find (column_types.begin(), column_types.end(), type) != column_types.end());
 }
diff --git a/src/import-export/csv-imp/gnc-csv-imp-trans.hpp b/src/import-export/csv-imp/gnc-csv-imp-trans.hpp
index ded3596..1d983ec 100644
--- a/src/import-export/csv-imp/gnc-csv-imp-trans.hpp
+++ b/src/import-export/csv-imp/gnc-csv-imp-trans.hpp
@@ -44,22 +44,22 @@ extern "C" {
 
 /** Enumeration for column types. These are the different types of
  * columns that can exist in a CSV/Fixed-Width file. There should be
- * no two columns with the same type except for the GNC_CSV_NONE
+ * no two columns with the same type except for the GncTransPropType::NONE
  * type. */
-enum GncCsvColumnType {
-    GNC_CSV_NONE,
-    GNC_CSV_DATE,
-    GNC_CSV_NUM,
-    GNC_CSV_DESCRIPTION,
-    GNC_CSV_NOTES,
-    GNC_CSV_ACCOUNT,
-    GNC_CSV_DEPOSIT,
-    GNC_CSV_WITHDRAWAL,
-    GNC_CSV_BALANCE,
-    GNC_CSV_MEMO,
-    GNC_CSV_OACCOUNT,
-    GNC_CSV_OMEMO,
-    GNC_CSV_NUM_COL_TYPES
+enum class GncTransPropType {
+    NONE,
+    DATE,
+    NUM,
+    DESCRIPTION,
+    NOTES,
+    ACCOUNT,
+    DEPOSIT,
+    WITHDRAWAL,
+    BALANCE,
+    MEMO,
+    OACCOUNT,
+    OMEMO,
+    NUM_COL_TYPES
 };
 
 /** Error domain for the csv importer. */
@@ -103,7 +103,7 @@ extern const int num_date_formats;
 extern const gchar* date_format_user[];
 
 /* This array contains all of the different strings for different column types. */
-extern const gchar* gnc_csv_column_type_strs[];
+extern const gchar* gnc_csv_col_type_strs[];
 
 using str_vec_t = std::vector<std::string> ;
 
@@ -123,12 +123,12 @@ public:
 
     int parse (gboolean guessColTypes, GError** error);
     int parse_to_trans (Account* account, gboolean redo_errors);
-    bool check_for_column_type (int type);
+    bool check_for_column_type (GncTransPropType type);
 
     std::unique_ptr<GncTokenizer> tokenizer;    /**< Will handle file loading/encoding conversion/splitting into fields */
     std::vector<str_vec_t> orig_lines;      /**< file_str parsed into a two-dimensional array of strings */
     std::vector<str_vec>::size_type orig_max_row;           /**< Holds the maximum value in orig_row_lengths */
-    std::vector<GncCsvColumnType> column_types;       /**< Vector of values from the GncCsvColumnType enumeration */
+    std::vector<GncTransPropType> column_types;       /**< Vector of values from the GncCsvColumnType enumeration */
     GList* transactions;        /**< List of GncCsvTransLine*s created using orig_lines and column_types */
     int date_format;            /**< The format of the text in the date columns from date_format_internal. */
     guint start_row;            /**< The start row to generate transactions from. */

commit 8f9d2ee826c6179faf0b63f0aa0ab72c6a77f5ac
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sun Jan 31 14:25:17 2016 +0100

    Use more C++11 features
    
    - use iterators to loop and std::find
      This allows us to use 'continue' on the loop in case
      of errors instead of using an ever more indenting if/else
    - Use auto where possible
    - Use constant iterators where possible

diff --git a/src/import-export/csv-imp/gnc-csv-imp-trans.cpp b/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
index 5212f32..4bdc07d 100644
--- a/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
+++ b/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
@@ -33,6 +33,7 @@ extern "C" {
 #include "engine-helpers.h"
 
 #include <string.h>
+
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <regex.h>
@@ -42,6 +43,7 @@ extern "C" {
 #include <math.h>
 }
 
+#include <algorithm>
 #include <boost/regex.hpp>
 
 #include "gnc-csv-imp-trans.hpp"
@@ -859,7 +861,6 @@ static GncCsvTransLine* trans_property_list_to_trans (TransPropertyList* list, g
 int GncCsvParseData::parse_to_trans (Account* account,
                                      gboolean redo_errors)
 {
-    gboolean hasBalanceColumn;
     Account *home_account = NULL;
 
     /* last_transaction points to the last element in
@@ -896,11 +897,22 @@ int GncCsvParseData::parse_to_trans (Account* account,
         last_transaction = NULL;
     }
 
-    /* set end_row to number of lines */
+    /* compute start and end iterators based on user-set restrictions */
+    auto line_errs_it = line_errors.begin();
+    std::advance(line_errs_it, start_row);
+    auto orig_lines_it = orig_lines.cbegin();
+    std::advance(orig_lines_it, start_row);
+
+    auto orig_lines_max = orig_lines.cbegin();
     if (end_row > orig_lines.size())
-        end_row = orig_lines.size();
+        orig_lines_max = orig_lines.cend();
+    else
+        std::advance(orig_lines_max, end_row);
 
-    for (uint i = 0; i < end_row; ++i)
+    bool odd_line = false;
+    for (orig_lines_it, line_errs_it, odd_line;
+            orig_lines_it != orig_lines_max;
+            ++orig_lines_it, ++line_errs_it, odd_line = !odd_line)
     {
         /* Skip current line if:
            1. only looking for lines with error AND no error on current line
@@ -908,14 +920,11 @@ int GncCsvParseData::parse_to_trans (Account* account,
            2. looking for all lines AND
               skip_rows is enabled AND
               current line is an odd line */
-        if ((redo_errors && line_errors[i].empty()) ||
-           (!redo_errors && skip_rows && (i % 2 == 1)))
+        if ((redo_errors && line_errs_it->empty()) ||
+           (!redo_errors && skip_rows && odd_line))
             continue;
 
-        std::vector<std::string> line = orig_lines[i];
-        /* This flag is TRUE if there are any errors in this row. */
-        gboolean errors = FALSE;
-        TransPropertyList* list;
+        std::vector<std::string> line = *orig_lines_it;
         GncCsvTransLine* trans_line = NULL;
 
         home_account = account;
@@ -927,115 +936,98 @@ int GncCsvParseData::parse_to_trans (Account* account,
             {
                 /* Look for "Account" columns. */
                 if (column_types[j] == GNC_CSV_ACCOUNT)
-                {
                     home_account = gnc_csv_account_map_search (line[j].c_str());
-                }
             }
         }
 
         if (home_account == NULL)
         {
-            line_errors[i] = _("Account column could not be understood.");
-            errors = TRUE;
+            *line_errs_it = _("Account column could not be understood.");
+            continue;
         }
-        else
-        {
-            list = trans_property_list_new (home_account, date_format, currency_format);
 
-            for (j = 0; j < line.size(); j++)
-            {
-                /* We do nothing in "None" or "Account" columns. */
-                if ((column_types[j] != GNC_CSV_NONE) && (column_types[j] != GNC_CSV_ACCOUNT))
-                {
-                    /* Affect the transaction appropriately. */
-                    TransProperty* property = trans_property_new (column_types[j], list);
-                    gboolean succeeded = trans_property_set (property, line[j].c_str());
-
-                    /* TODO Maybe move error handling to within TransPropertyList functions? */
-                    if (succeeded)
-                        trans_property_list_add (property);
-                    else
-                    {
-                        errors = TRUE;
-                        gchar *error_message = g_strdup_printf (_("%s column could not be understood."),
-                                                        _(gnc_csv_column_type_strs[property->type]));
-                        line_errors[i] = error_message;
-                        g_free (error_message);
-                        trans_property_free (property);
-                        break;
-                    }
-                }
-            }
+        TransPropertyList* list = trans_property_list_new (home_account, date_format, currency_format);
 
-            /* If we had success, add the transaction to transaction. */
-            if (!errors)
+        bool loop_err = false;
+        for (uint j = 0; j < line.size(); j++)
+        {
+            /* We do nothing in "None" or "Account" columns. */
+            if ((column_types[j] != GNC_CSV_NONE) && (column_types[j] != GNC_CSV_ACCOUNT))
             {
-                gchar *error_message = NULL;
-                trans_line = trans_property_list_to_trans (list, &error_message);
-                errors = (trans_line == NULL);
-                if (errors)
+                /* Affect the transaction appropriately. */
+                TransProperty* property = trans_property_new (column_types[j], list);
+                gboolean succeeded = trans_property_set (property, line[j].c_str());
+
+                /* TODO Maybe move error handling to within TransPropertyList functions? */
+                if (succeeded)
+                    trans_property_list_add (property);
+                else
                 {
-                    line_errors[i] = error_message;
+                    loop_err = true;
+                    gchar *error_message = g_strdup_printf (_("%s column could not be understood."),
+                                                    _(gnc_csv_column_type_strs[property->type]));
+                    *line_errs_it = error_message;
+
                     g_free (error_message);
+                    trans_property_free (property);
+                    trans_property_list_free (list);
+                    break;
                 }
             }
+        }
+        if (loop_err)
+            continue;
+
+        /* If column parsing was successful, convert trans properties into a trans line. */
+        gchar *error_message = NULL;
+        trans_line = trans_property_list_to_trans (list, &error_message);
+        if (trans_line == NULL)
+        {
+            *line_errs_it = error_message;
+            g_free (error_message);
             trans_property_list_free (list);
+            continue;
         }
 
         /* If all went well, add this transaction to the list. */
-        if (!errors)
+        /* We keep the transactions sorted by date. We start at the end
+         * of the list and go backward, simply because the file itself
+         * is probably also sorted by date (but we need to handle the
+         * exception anyway). */
+
+        /* If we can just put it at the end, do so and increment last_transaction. */
+        if (last_transaction == NULL ||
+                xaccTransGetDate (((GncCsvTransLine*)(last_transaction->data))->trans) <= xaccTransGetDate (trans_line->trans))
         {
-            trans_line->line_no = i;
-
-            /* We keep the transactions sorted by date. We start at the end
-             * of the list and go backward, simply because the file itself
-             * is probably also sorted by date (but we need to handle the
-             * exception anyway). */
-
-            /* If we can just put it at the end, do so and increment last_transaction. */
-            if (last_transaction == NULL ||
-                    xaccTransGetDate (((GncCsvTransLine*)(last_transaction->data))->trans) <= xaccTransGetDate (trans_line->trans))
+            transactions = g_list_append (transactions, trans_line);
+            /* If this is the first transaction, we need to get last_transaction on track. */
+            if (last_transaction == NULL)
+                last_transaction = transactions;
+            else /* Otherwise, we can just continue. */
+                last_transaction = g_list_next (last_transaction);
+        }
+        /* Otherwise, search backward for the correct spot. */
+        else
+        {
+            GList* insertion_spot = last_transaction;
+            while (insertion_spot != NULL &&
+                    xaccTransGetDate (((GncCsvTransLine*)(insertion_spot->data))->trans) > xaccTransGetDate (trans_line->trans))
             {
-                transactions = g_list_append (transactions, trans_line);
-                /* If this is the first transaction, we need to get last_transaction on track. */
-                if (last_transaction == NULL)
-                    last_transaction = transactions;
-                else /* Otherwise, we can just continue. */
-                    last_transaction = g_list_next (last_transaction);
+                insertion_spot = g_list_previous (insertion_spot);
             }
-            /* Otherwise, search backward for the correct spot. */
+            /* Move insertion_spot one location forward since we have to
+             * use the g_list_insert_before function. */
+            if (insertion_spot == NULL) /* We need to handle the case of inserting at the beginning of the list. */
+                insertion_spot = transactions;
             else
-            {
-                GList* insertion_spot = last_transaction;
-                while (insertion_spot != NULL &&
-                        xaccTransGetDate (((GncCsvTransLine*)(insertion_spot->data))->trans) > xaccTransGetDate (trans_line->trans))
-                {
-                    insertion_spot = g_list_previous (insertion_spot);
-                }
-                /* Move insertion_spot one location forward since we have to
-                 * use the g_list_insert_before function. */
-                if (insertion_spot == NULL) /* We need to handle the case of inserting at the beginning of the list. */
-                    insertion_spot = transactions;
-                else
-                    insertion_spot = g_list_next (insertion_spot);
+                insertion_spot = g_list_next (insertion_spot);
 
-                transactions = g_list_insert_before (transactions, insertion_spot, trans_line);
-            }
-        }
-    }
-
-    /* If we have a balance column, set the appropriate amounts on the transactions. */
-    hasBalanceColumn = FALSE;
-    for (i = 0; i < column_types.size(); i++)
-    {
-        if (column_types[i] == GNC_CSV_BALANCE)
-        {
-            hasBalanceColumn = TRUE;
-            break;
+            transactions = g_list_insert_before (transactions, insertion_spot, trans_line);
         }
     }
 
-    if (hasBalanceColumn) // This is only used if we have one home account
+    if (std::find(column_types.begin(),column_types.end(), GNC_CSV_BALANCE) !=
+        column_types.end()) // This is only used if we have one home account
     {
         Split      *split, *osplit;
         gnc_numeric balance_offset;

commit bcae6628d634ff05c3fb10687fa49ec15199ab44
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Mon Feb 8 18:47:57 2016 +0100

    Revert to boost.regex without ICU support
    
    For some reason named capture groups trigger an uninitialized shared_ptr assert

diff --git a/src/import-export/csv-imp/gnc-csv-imp-trans.cpp b/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
index acc74be..5212f32 100644
--- a/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
+++ b/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
@@ -43,7 +43,6 @@ extern "C" {
 }
 
 #include <boost/regex.hpp>
-#include <boost/regex/icu.hpp>
 
 #include "gnc-csv-imp-trans.hpp"
 #include "gnc-csv-tokenizer.hpp"
@@ -68,53 +67,53 @@ G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT;
 /* Regular expressions used to parse dates per date format */
 const char* date_regex[] = {
                              "(?:"                                   // either y-m-d
-                                 "(?<YEAR>[[:Nd:]]+)[[:P*:][:Zs:]]+"
-                                 "(?<MONTH>[[:Nd:]]+)[[:P*:][:Zs:]]+"
-                                 "(?<DAY>[[:Nd:]]+)"
+                                 "(?<YEAR>[0-9]+)[-/.' ]+"
+                                 "(?<MONTH>[0-9]+)[-/.' ]+"
+                                 "(?<DAY>[0-9]+)"
                              "|"                                     // or CCYYMMDD
-                                 "(?<YEAR>[[:Nd:]]{4})"
-                                 "(?<MONTH>[[:Nd:]]{2})"
-                                 "(?<DAY>[[:Nd:]]{2})"
+                                 "(?<YEAR>[0-9]{4})"
+                                 "(?<MONTH>[0-9]{2})"
+                                 "(?<DAY>[0-9]{2})"
                              ")",
 
                              "(?:"                                   // either d-m-y
-                                 "(?<DAY>[[:Nd:]]+)[[:P*:][:Zs:]]+"
-                                 "(?<MONTH>[[:Nd:]]+)[[:P*:][:Zs:]]+"
-                                 "(?<YEAR>[[:Nd:]]+)"
+                                 "(?<DAY>[0-9]+)[-/.' ]+"
+                                 "(?<MONTH>[0-9]+)[-/.' ]+"
+                                 "(?<YEAR>[0-9]+)"
                              "|"                                     // or DDMMCCYY
-                                 "(?<DAY>[[:Nd:]]{2})"
-                                 "(?<MONTH>[[:Nd:]]{2})"
-                                 "(?<YEAR>[[:Nd:]]{4})"
+                                 "(?<DAY>[0-9]{2})"
+                                 "(?<MONTH>[0-9]{2})"
+                                 "(?<YEAR>[0-9]{4})"
                              ")",
 
                              "(?:"                                   // either m-d-y
-                                 "(?<MONTH>[[:Nd:]]+)[[:P*:][:Zs:]]+"
-                                 "(?<DAY>[[:Nd:]]+)[[:P*:][:Zs:]]+"
-                                 "(?<YEAR>[[:Nd:]]+)"
+                                 "(?<MONTH>[0-9]+)[-/.' ]+"
+                                 "(?<DAY>[0-9]+)[-/.' ]+"
+                                 "(?<YEAR>[0-9]+)"
                              "|"                                     // or MMDDCCYY
-                                 "(?<MONTH>[[:Nd:]]{2})"
-                                 "(?<DAY>[[:Nd:]]{2})"
-                                 "(?<YEAR>[[:Nd:]]{4})"
+                                 "(?<MONTH>[0-9]{2})"
+                                 "(?<DAY>[0-9]{2})"
+                                 "(?<YEAR>[0-9]{4})"
                              ")",
 
                              "(?:"                                   // either d-m(-y)
-                                 "(?<DAY>[[:Nd:]]+)[[:P*:][:Zs:]]+"
-                                 "(?<MONTH>[[:Nd:]]+)(?:[[:P*:][:Zs:]]+"
-                                 "(?<YEAR>[[:Nd:]]+))?"
+                                 "(?<DAY>[0-9]+)[-/.' ]+"
+                                 "(?<MONTH>[0-9]+)(?:[-/.' ]+"
+                                 "(?<YEAR>[0-9]+))?"
                              "|"                                     // or DDMM(CCYY)
-                                 "(?<DAY>[[:Nd:]]{2})"
-                                 "(?<MONTH>[[:Nd:]]{2})"
-                                 "(?<YEAR>[[:Nd:]]+)?"
+                                 "(?<DAY>[0-9]{2})"
+                                 "(?<MONTH>[0-9]{2})"
+                                 "(?<YEAR>[0-9]+)?"
                              ")",
 
                              "(?:"                                   // either m-d(-y)
-                                 "(?<MONTH>[[:Nd:]]+)[[:P*:][:Zs:]]+"
-                                 "(?<DAY>[[:Nd:]]+)(?:[[:P*:][:Zs:]]+"
-                                 "(?<YEAR>[[:Nd:]]+))?"
+                                 "(?<MONTH>[0-9]+)[-/.' ]+"
+                                 "(?<DAY>[0-9]+)(?:[-/.' ]+"
+                                 "(?<YEAR>[0-9]+))?"
                              "|"                                     // or MMDD(CCYY)
-                                 "(?<MONTH>[[:Nd:]]{2})"
-                                 "(?<DAY>[[:Nd:]]{2})"
-                                 "(?<YEAR>[[:Nd:]]+)?"
+                                 "(?<MONTH>[0-9]{2})"
+                                 "(?<DAY>[0-9]{2})"
+                                 "(?<YEAR>[0-9]+)?"
                              ")",
 };
 //const int num_currency_formats = 3;
@@ -153,9 +152,9 @@ time64 parse_date (const std::string &date_str, int format)
     struct tm retvalue, test_retvalue; /* The time in a broken-down structure */
     int orig_year = -1, orig_month = -1, orig_day = -1;
 
-    boost::u32regex r = boost::make_u32regex(date_regex[format]);
+    boost::regex r(date_regex[format]);
     boost::smatch what;
-    if(!boost::u32regex_search(date_str.cbegin(), date_str.cend(), what, r))
+    if(!boost::regex_search(date_str.cbegin(), date_str.cend(), what, r))
         return -1;  // regex didn't find a match
 
     // xxx Different behavior from 2.6.x series !

commit e95b1e2c169862d4cfee6188ca7a545edba17bf4
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sat Feb 6 17:41:34 2016 +0100

    Redo parse_date function using boost regular expressions

diff --git a/src/import-export/csv-imp/Makefile.am b/src/import-export/csv-imp/Makefile.am
index 53c3c42..dab3f97 100644
--- a/src/import-export/csv-imp/Makefile.am
+++ b/src/import-export/csv-imp/Makefile.am
@@ -51,7 +51,10 @@ libgncmod_csv_import_la_LIBADD = \
   ${top_builddir}/lib/libc/libc-missing.la \
   ${top_builddir}/src/libqof/qof/libgnc-qof.la \
   ${GOFFICE_LIBS} \
-  ${GLIB_LIBS}
+  ${GLIB_LIBS} \
+  ${BOOST_LDFLAGS} \
+  -lboost_regex \
+  -lboost_locale
 
 AM_CPPFLAGS = \
   -I${top_srcdir}/src \
@@ -67,7 +70,8 @@ AM_CPPFLAGS = \
   -I${top_srcdir}/lib \
   ${GUILE_CFLAGS} \
   ${GLIB_CFLAGS} \
-  $(GOFFICE_CFLAGS)
+  $(GOFFICE_CFLAGS) \
+  ${BOOST_CPPFLAGS}
 
 uidir = $(GNC_UI_DIR)
 ui_DATA = \
diff --git a/src/import-export/csv-imp/gnc-csv-imp-trans.cpp b/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
index f6b746c..acc74be 100644
--- a/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
+++ b/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
@@ -42,6 +42,9 @@ extern "C" {
 #include <math.h>
 }
 
+#include <boost/regex.hpp>
+#include <boost/regex/icu.hpp>
+
 #include "gnc-csv-imp-trans.hpp"
 #include "gnc-csv-tokenizer.hpp"
 #include "gnc-fw-tokenizer.hpp"
@@ -62,6 +65,58 @@ G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT;
 //                                   N_("m-d")
 //                                  };
 //
+/* Regular expressions used to parse dates per date format */
+const char* date_regex[] = {
+                             "(?:"                                   // either y-m-d
+                                 "(?<YEAR>[[:Nd:]]+)[[:P*:][:Zs:]]+"
+                                 "(?<MONTH>[[:Nd:]]+)[[:P*:][:Zs:]]+"
+                                 "(?<DAY>[[:Nd:]]+)"
+                             "|"                                     // or CCYYMMDD
+                                 "(?<YEAR>[[:Nd:]]{4})"
+                                 "(?<MONTH>[[:Nd:]]{2})"
+                                 "(?<DAY>[[:Nd:]]{2})"
+                             ")",
+
+                             "(?:"                                   // either d-m-y
+                                 "(?<DAY>[[:Nd:]]+)[[:P*:][:Zs:]]+"
+                                 "(?<MONTH>[[:Nd:]]+)[[:P*:][:Zs:]]+"
+                                 "(?<YEAR>[[:Nd:]]+)"
+                             "|"                                     // or DDMMCCYY
+                                 "(?<DAY>[[:Nd:]]{2})"
+                                 "(?<MONTH>[[:Nd:]]{2})"
+                                 "(?<YEAR>[[:Nd:]]{4})"
+                             ")",
+
+                             "(?:"                                   // either m-d-y
+                                 "(?<MONTH>[[:Nd:]]+)[[:P*:][:Zs:]]+"
+                                 "(?<DAY>[[:Nd:]]+)[[:P*:][:Zs:]]+"
+                                 "(?<YEAR>[[:Nd:]]+)"
+                             "|"                                     // or MMDDCCYY
+                                 "(?<MONTH>[[:Nd:]]{2})"
+                                 "(?<DAY>[[:Nd:]]{2})"
+                                 "(?<YEAR>[[:Nd:]]{4})"
+                             ")",
+
+                             "(?:"                                   // either d-m(-y)
+                                 "(?<DAY>[[:Nd:]]+)[[:P*:][:Zs:]]+"
+                                 "(?<MONTH>[[:Nd:]]+)(?:[[:P*:][:Zs:]]+"
+                                 "(?<YEAR>[[:Nd:]]+))?"
+                             "|"                                     // or DDMM(CCYY)
+                                 "(?<DAY>[[:Nd:]]{2})"
+                                 "(?<MONTH>[[:Nd:]]{2})"
+                                 "(?<YEAR>[[:Nd:]]+)?"
+                             ")",
+
+                             "(?:"                                   // either m-d(-y)
+                                 "(?<MONTH>[[:Nd:]]+)[[:P*:][:Zs:]]+"
+                                 "(?<DAY>[[:Nd:]]+)(?:[[:P*:][:Zs:]]+"
+                                 "(?<YEAR>[[:Nd:]]+))?"
+                             "|"                                     // or MMDD(CCYY)
+                                 "(?<MONTH>[[:Nd:]]{2})"
+                                 "(?<DAY>[[:Nd:]]{2})"
+                                 "(?<YEAR>[[:Nd:]]+)?"
+                             ")",
+};
 //const int num_currency_formats = 3;
 //const gchar* currency_format_user[] = {N_("Locale"),
 //                                       N_("Period: 123,456.78"),
@@ -84,71 +139,31 @@ G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT;
 //        N_("Other Memo")
 //};
 
-/** Parses a string into a date, given a format. The format must
- * include the year. This function should only be called by
- * parse_date.
+/** Parses a string into a date, given a format. This function
+ * requires only knowing the order in which the year, month and day
+ * appear. For example, 01-02-2003 will be parsed the same way as
+ * 01/02/2003.
  * @param date_str The string containing a date being parsed
  * @param format An index specifying a format in date_format_user
  * @return The parsed value of date_str on success or -1 on failure
  */
-static time64 parse_date_with_year (const char* date_str, int format)
+time64 parse_date (const std::string &date_str, int format)
 {
     time64 rawtime; /* The integer time */
     struct tm retvalue, test_retvalue; /* The time in a broken-down structure */
+    int orig_year = -1, orig_month = -1, orig_day = -1;
 
-    int i, j, mem_length, orig_year = -1, orig_month = -1, orig_day = -1;
-
-    /* Buffer for containing individual parts (e.g. year, month, day) of a date */
-    char date_segment[5];
-
-    /* The compiled regular expression */
-    regex_t preg = {0};
+    boost::u32regex r = boost::make_u32regex(date_regex[format]);
+    boost::smatch what;
+    if(!boost::u32regex_search(date_str.cbegin(), date_str.cend(), what, r))
+        return -1;  // regex didn't find a match
 
-    /* An array containing indices specifying the matched substrings in date_str */
-    regmatch_t pmatch[4] = { {0}, {0}, {0}, {0} };
-
-    /* The regular expression for parsing dates */
-    const char* regex = "^ *([0-9]+) *[-/.'] *([0-9]+) *[-/.'] *([0-9]+).*$|^ *([0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]).*$";
-
-    /* We get our matches using the regular expression. */
-    regcomp (&preg, regex, REG_EXTENDED);
-    regexec (&preg, date_str, 4, pmatch, 0);
-    regfree (&preg);
-
-    /* If there wasn't a match, there was an error. */
-    if (pmatch[0].rm_eo == 0)
+    // xxx Different behavior from 2.6.x series !
+    // If date format without year was selected, the match
+    // should NOT have found a year.
+    if ((format >= 3) && (what.length("YEAR") != 0))
         return -1;
 
-    /* If this is a string without separators ... */
-    if (pmatch[1].rm_so == -1)
-    {
-        /* ... we will fill in the indices based on the user's selection. */
-        int k = 0; /* k traverses date_str by keeping track of where separators "should" be. */
-        j = 1; /* j traverses pmatch. */
-        for (i = 0; date_format_user[format][i]; i++)
-        {
-            char segment_type = date_format_user[format][i];
-            /* Only do something if this is a meaningful character */
-            if (segment_type == 'y' || segment_type == 'm' || segment_type == 'd')
-            {
-                pmatch[j].rm_so = k;
-                switch (segment_type)
-                {
-                case 'm':
-                case 'd':
-                    k += 2;
-                    break;
-
-                case 'y':
-                    k += 4;
-                    break;
-                }
-
-                pmatch[j].rm_eo = k;
-                j++;
-            }
-        }
-    }
 
     /* Put some sane values in retvalue by using a fixed time for
      * the non-year-month-day parts of the date. */
@@ -159,162 +174,35 @@ static time64 parse_date_with_year (const char* date_str, int format)
     retvalue.tm_sec = 0;
     retvalue.tm_isdst = -1;
 
-    /* j traverses pmatch (index 0 contains the entire string, so we
-     * start at index 1 for the first meaningful match). */
-    j = 1;
-    /* Go through the date format and interpret the matches in order of
-     * the sections in the date format. */
-    for (i = 0; date_format_user[format][i]; i++)
-    {
-        char segment_type = date_format_user[format][i];
-        /* Only do something if this is a meaningful character */
-        if (segment_type == 'y' || segment_type == 'm' || segment_type == 'd')
-        {
-            /* Copy the matching substring into date_segment so that we can
-             * convert it into an integer. */
-            mem_length = pmatch[j].rm_eo - pmatch[j].rm_so;
-            memcpy (date_segment, date_str + pmatch[j].rm_so, mem_length);
-            date_segment[mem_length] = '\0';
-
-            /* Set the appropriate member of retvalue. Save the original
-             * values so that we can check if they change when we use gnc_mktime
-             * below. */
-            switch (segment_type)
-            {
-            case 'y':
-                retvalue.tm_year = atoi (date_segment);
-
-                /* Handle two-digit years. */
-                if (retvalue.tm_year < 100)
-                {
-                    /* We allow two-digit years in the range 1969 - 2068. */
-                    if (retvalue.tm_year < 69)
-                        retvalue.tm_year += 100;
-                }
-                else
-                    retvalue.tm_year -= 1900;
-                orig_year = retvalue.tm_year;
-                break;
-
-            case 'm':
-                orig_month = retvalue.tm_mon = atoi (date_segment) - 1;
-                break;
+    retvalue.tm_mday = std::stoi (what.str("DAY"));
+    retvalue.tm_mon = std::stoi (what.str("MONTH")) - 1;
 
-            case 'd':
-                orig_day = retvalue.tm_mday = atoi (date_segment);
-                break;
-            }
-            j++;
-        }
-    }
-    /* Convert back to an integer. If gnc_mktime leaves retvalue unchanged,
-     * everything is okay; otherwise, an error has occurred. */
-    /* We have to use a "test" date value to account for changes in
-     * daylight savings time, which can cause a date change with gnc_mktime
-     * near midnight, causing the code to incorrectly think a date is
-     * incorrect. */
-    test_retvalue = retvalue;
-    gnc_mktime (&test_retvalue);
-    retvalue.tm_isdst = test_retvalue.tm_isdst;
-    rawtime = gnc_mktime (&retvalue);
-    if (retvalue.tm_mday == orig_day &&
-            retvalue.tm_mon == orig_month &&
-            retvalue.tm_year == orig_year)
+    if (format < 3)
     {
-        return rawtime;
-    }
-    else
-    {
-        return -1;
-    }
-}
-
-/** Parses a string into a date, given a format. The format cannot
- * include the year. This function should only be called by
- * parse_date.
- * @param date_str The string containing a date being parsed
- * @param format An index specifying a format in date_format_user
- * @return The parsed value of date_str on success or -1 on failure
- */
-static time64 parse_date_without_year (const char* date_str, int format)
-{
-    time64 rawtime; /* The integer time */
-    struct tm retvalue, test_retvalue; /* The time in a broken-down structure */
-
-    int i, j, mem_length, orig_year = -1, orig_month = -1, orig_day = -1;
-
-    /* Buffer for containing individual parts (e.g. year, month, day) of a date */
-    gchar* date_segment;
-
-    /* The compiled regular expression */
-    regex_t preg = {0};
-
-    /* An array containing indices specifying the matched substrings in date_str */
-    regmatch_t pmatch[3] = { {0}, {0}, {0} };
+        retvalue.tm_year = std::stoi (what.str("YEAR"));
 
-    /* The regular expression for parsing dates */
-    const char* regex = "^ *([0-9]+) *[-/.'] *([0-9]+).*$";
-
-    /* We get our matches using the regular expression. */
-    regcomp (&preg, regex, REG_EXTENDED);
-    regexec (&preg, date_str, 3, pmatch, 0);
-    regfree (&preg);
-
-    /* If there wasn't a match, there was an error. */
-    if (pmatch[0].rm_eo == 0)
-        return -1;
-
-    /* Put some sane values in retvalue by using a fixed time for
-     * the non-year-month-day parts of the date. */
-    gnc_time (&rawtime);
-    gnc_localtime_r (&rawtime, &retvalue);
-    retvalue.tm_hour = 11;
-    retvalue.tm_min = 0;
-    retvalue.tm_sec = 0;
-    retvalue.tm_isdst = -1;
-    orig_year = retvalue.tm_year;
-
-    /* j traverses pmatch (index 0 contains the entire string, so we
-     * start at index 1 for the first meaningful match). */
-    j = 1;
-    /* Go through the date format and interpret the matches in order of
-     * the sections in the date format. */
-    for (i = 0; date_format_user[format][i]; i++)
-    {
-        char segment_type = date_format_user[format][i];
-        /* Only do something if this is a meaningful character */
-        if (segment_type == 'm' || segment_type == 'd')
+        /* Handle two-digit years. */
+        if (retvalue.tm_year < 100)
         {
-            /* Copy the matching substring into date_segment so that we can
-             * convert it into an integer. */
-            mem_length = pmatch[j].rm_eo - pmatch[j].rm_so;
-            date_segment = g_new (gchar, mem_length);
-            memcpy(date_segment, date_str + pmatch[j].rm_so, mem_length);
-            date_segment[mem_length] = '\0';
-
-            /* Set the appropriate member of retvalue. Save the original
-             * values so that we can check if they change when we use gnc_mktime
-             * below. */
-            switch (segment_type)
-            {
-            case 'm':
-                orig_month = retvalue.tm_mon = atoi (date_segment) - 1;
-                break;
-
-            case 'd':
-                orig_day = retvalue.tm_mday = atoi (date_segment);
-                break;
-            }
-            g_free (date_segment);
-            j++;
+            /* We allow two-digit years in the range 1969 - 2068. */
+            if (retvalue.tm_year < 69)
+                retvalue.tm_year += 100;
         }
+        else
+            retvalue.tm_year -= 1900;
     }
+
     /* Convert back to an integer. If gnc_mktime leaves retvalue unchanged,
      * everything is okay; otherwise, an error has occurred. */
     /* We have to use a "test" date value to account for changes in
      * daylight savings time, which can cause a date change with gnc_mktime
      * near midnight, causing the code to incorrectly think a date is
      * incorrect. */
+
+    orig_day   = retvalue.tm_mday;
+    orig_month = retvalue.tm_mon;
+    orig_year  = retvalue.tm_year;
+
     test_retvalue = retvalue;
     gnc_mktime (&test_retvalue);
     retvalue.tm_isdst = test_retvalue.tm_isdst;
@@ -322,29 +210,9 @@ static time64 parse_date_without_year (const char* date_str, int format)
     if (retvalue.tm_mday == orig_day &&
             retvalue.tm_mon == orig_month &&
             retvalue.tm_year == orig_year)
-    {
         return rawtime;
-    }
     else
-    {
         return -1;
-    }
-}
-
-/** Parses a string into a date, given a format. This function
- * requires only knowing the order in which the year, month and day
- * appear. For example, 01-02-2003 will be parsed the same way as
- * 01/02/2003.
- * @param date_str The string containing a date being parsed
- * @param format An index specifying a format in date_format_user
- * @return The parsed value of date_str on success or -1 on failure
- */
-time64 parse_date (const char* date_str, int format)
-{
-    if (strchr (date_format_user[format], 'y'))
-        return parse_date_with_year (date_str, format);
-    else
-        return parse_date_without_year (date_str, format);
 }
 
 /** Constructor for GncCsvParseData.
diff --git a/src/import-export/csv-imp/gnc-csv-imp-trans.hpp b/src/import-export/csv-imp/gnc-csv-imp-trans.hpp
index c3c83a5..ded3596 100644
--- a/src/import-export/csv-imp/gnc-csv-imp-trans.hpp
+++ b/src/import-export/csv-imp/gnc-csv-imp-trans.hpp
@@ -144,6 +144,6 @@ private:
     GncImpFileFormat file_fmt = GncImpFileFormat::UNKNOWN;
 };
 
-time64 parse_date (const char* date_str, int format);
+time64 parse_date (const std::string &date_str, int format);
 
 #endif
diff --git a/src/import-export/csv-imp/test/Makefile.am b/src/import-export/csv-imp/test/Makefile.am
index 1061318..f79e562 100644
--- a/src/import-export/csv-imp/test/Makefile.am
+++ b/src/import-export/csv-imp/test/Makefile.am
@@ -22,7 +22,8 @@ test_csv_impdir = ${top_srcdir}/${MODULEPATH}/test
 #support library.
 test_csv_imp_SOURCES = \
   test-csv-imp.c \
-  utest-gnc-csv-model.c
+  utest-gnc-csv-model.c \
+  utest-gnc-csv-imp-trans.cpp
 
 test_csv_imp_HEADERS =
 
@@ -42,7 +43,8 @@ test_csv_imp_LDADD = \
   ${top_builddir}/src/gnc-module/libgnc-module.la \
   ${top_builddir}/src/libqof/qof/libgnc-qof.la \
   ${top_builddir}/lib/stf/libgnc-stf.la \
-  ${GLIB_LIBS}
+  ${GLIB_LIBS} \
+  $(BOOST_LDFLAGS)
 
 test_csv_imp_CFLAGS = \
 	-DTESTPROG=test_csv-imp \
@@ -66,6 +68,29 @@ test_csv_imp_CFLAGS = \
   ${GTK_CFLAGS} \
   ${GLIB_CFLAGS}
 
+test_csv_imp_CPPFLAGS = \
+	-DTESTPROG=test_csv-imp \
+	${DEFAULT_INCLUDES} \
+	-I$(top_srcdir)/${MODULEPATH}/ \
+  -I${top_srcdir}/src/test-core \
+  -I${top_srcdir}/src \
+  -I${top_srcdir}/src/import-export \
+  -I${top_srcdir}/src/gnome \
+  -I${top_srcdir}/src/register/ledger-core \
+  -I${top_srcdir}/src/register/register-gnome \
+  -I${top_srcdir}/src/register/register-core \
+  -I${top_srcdir}/src/gnome-utils \
+  -I${top_srcdir}/src/app-utils \
+  -I${top_srcdir}/src/engine \
+  -I${top_srcdir}/src/core-utils \
+  -I${top_srcdir}/src/gnc-module \
+  -I${top_srcdir}/src/libqof/qof \
+  -I${top_srcdir}/lib/libc \
+  -I${top_srcdir}/lib \
+  ${GTK_CFLAGS} \
+  ${GLIB_CFLAGS} \
+  $(BOOST_CPPFLAGS)
+
 GNC_TEST_DEPS = \
 --library-dir    ${top_builddir}/${MODULEPATH} \
 --library-dir    ${top_builddir}/src/import-export \
diff --git a/src/import-export/csv-imp/test/test-csv-imp.c b/src/import-export/csv-imp/test/test-csv-imp.c
index 6665de1..42e0cea 100644
--- a/src/import-export/csv-imp/test/test-csv-imp.c
+++ b/src/import-export/csv-imp/test/test-csv-imp.c
@@ -32,6 +32,7 @@
  * each sub-suite; avoids having header files. */
 
 extern GTestSuite *test_suite_gnc_csv_model();
+extern GTestSuite *test_suite_gnc_csv_imp_trans();
 
 int
 main (int   argc,
@@ -55,6 +56,7 @@ main (int   argc,
      * details. Unfortunately, GLib-Testing doesn't provide the automatic
      * registration features of more sophisticated frameworks. */
     test_suite_gnc_csv_model();
+    test_suite_gnc_csv_imp_trans();
 
     return g_test_run();
 }
diff --git a/src/import-export/csv-imp/test/utest-gnc-csv-imp-trans.cpp b/src/import-export/csv-imp/test/utest-gnc-csv-imp-trans.cpp
new file mode 100644
index 0000000..c198b62
--- /dev/null
+++ b/src/import-export/csv-imp/test/utest-gnc-csv-imp-trans.cpp
@@ -0,0 +1,459 @@
+/********************************************************************
+ * utest-gnc-csv-model.c: GLib g_test test suite for gnc-csv-model.c.		    *
+ * Copyright 2015 Geert Janssens <geert.gnucash at kobaltwit.be>		    *
+ *                                                                  *
+ * 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 2 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, you can retrieve it from        *
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html            *
+ * or contact:                                                      *
+ *                                                                  *
+ * Free Software Foundation           Voice:  +1-617-542-5942       *
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
+ ********************************************************************/
+extern "C" {
+#include <config.h>
+#include <string.h>
+#include <glib.h>
+#include <unittest-support.h>
+
+}
+/* Add specific headers for this class */
+#include "import-export/csv-imp/gnc-csv-imp-trans.hpp"
+
+//typedef struct
+//{
+//    GncCsvParseData* parse_data;
+//} Fixture;
+
+typedef struct
+{
+    int          date_fmt;
+    const gchar *date_str;
+    int          exp_year;
+    int          exp_month;
+    int          exp_day;
+} parse_date_data;
+
+//typedef struct
+//{
+//    const gchar *csv_line;
+//    int          num_fields;
+//    const gchar *fields [8];
+//} parse_test_data;
+//
+//static const gchar* samplefile1 = "sample1.csv";
+//
+//static parse_test_data comma_separated [] = {
+//        { "Date,Num,Description,Notes,Account,Deposit,Withdrawal,Balance", 8, { "Date","Num","Description","Notes","Account","Deposit","Withdrawal","Balance" } },
+//        { "05/01/15,45,Acme Inc.,,Miscellaneous,,\"1,100.00\",", 8, { "05/01/15","45","Acme Inc.","","Miscellaneous","","1,100.00","" } },
+//        { "05/01/15,45,Acme Inc.,,Miscellaneous,", 4, { "05/01/15","45","Acme Inc.","",NULL,NULL,NULL,NULL } },
+//        { NULL, 0, { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } },
+//};
+//
+//static parse_test_data semicolon_separated [] = {
+//        { "Date;Num;Description;Notes;Account;Deposit;Withdrawal;Balance", 8, { "Date","Num","Description","Notes","Account","Deposit","Withdrawal","Balance" } },
+//        { "05/01/15;45;Acme Inc.;;Miscellaneous;;\"1,100.00\";", 8, { "05/01/15","45","Acme Inc.","","Miscellaneous","","1,100.00","" } },
+//        { "05/01/15;45;Acme Inc.;;Miscellaneous;", 4, { "05/01/15","45","Acme Inc.","",NULL,NULL,NULL,NULL } },
+//        { NULL, 0, { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } },
+//};
+//
+//static char* get_filepath(const char* filename, gboolean test_existence)
+//{
+//    char *result;
+//
+//    const char *srcdir = g_getenv("SRCDIR");
+//    if (!srcdir)
+//    {
+//        g_test_message("No env variable SRCDIR exists, assuming \".\"\n");
+//        srcdir = ".";
+//    }
+//
+//    result = g_strdup_printf("%s/%s", srcdir, filename);
+//
+//    g_test_message("Using file path %s\n", result);
+//
+//    // Test whether the file really exists
+//    if (test_existence)
+//        g_assert(g_file_test(result, G_FILE_TEST_EXISTS));
+//
+//    return result;
+//}
+//
+//static void
+//setup( Fixture *fixture, gconstpointer pData )
+//{
+//    fixture->parse_data = gnc_csv_new_parse_data ();
+//}
+//
+//static void
+//setup_one_file( Fixture *fixture, gconstpointer pData )
+//{
+//    const gchar *filename = (const gchar*) pData;
+//    char *filepath = get_filepath (filename, TRUE);
+//    GError *the_error = NULL;
+//    int resultcode = 0;
+//
+//    fixture->parse_data = gnc_csv_new_parse_data ();
+//    resultcode = gnc_csv_load_file (fixture->parse_data, filepath,
+//                                    &the_error);
+//    g_assert (resultcode == 0);
+//    g_free(filepath);
+//}
+//
+//static void
+//teardown( Fixture *fixture, gconstpointer pData )
+//{
+//    gnc_csv_parse_data_free (fixture->parse_data);
+//}
+
+static const gchar *suitename = "/import-export/csv-imp/gnc-csv-imp-trans";
+extern "C"  {
+void test_suite_gnc_csv_imp_trans ( void );
+}
+
+/* parse_date
+time64 parse_date (const char* date_str, int format)// C: 14 in 7 SCM: 9 in 2 Local: 1:0:0
+*/
+static void
+test_parse_date (void)
+{
+    time64 rawtime = gnc_time (NULL);
+    struct tm *tm = gnc_gmtime (&rawtime);
+    int curr_year = tm->tm_year;
+
+
+    /* Note: tm_year = year - 1900 and tm_mon = 0-11
+     * I'm writing the expected values as subtractions for easier
+     * comparison with the date string under test
+     */
+    parse_date_data test_dates[] =
+    {
+        // supported combinations  -/.'
+        { 0, "2013-08-01", 2013 - 1900,  8 - 1,  1},
+        { 0,  "2013-8-01", 2013 - 1900,  8 - 1,  1},
+        { 0,  "2013-08-1", 2013 - 1900,  8 - 1,  1},
+        { 0,   "2013-8-1", 2013 - 1900,  8 - 1,  1},
+        { 0,   "13-08-01", 2013 - 1900,  8 - 1,  1},
+        { 0,    "13-8-01", 2013 - 1900,  8 - 1,  1},
+        { 0,    "13-08-1", 2013 - 1900,  8 - 1,  1},
+        { 0,     "13-8-1", 2013 - 1900,  8 - 1,  1},
+        { 0, "2009/11/04", 2009 - 1900, 11 - 1,  4},
+        { 0,  "1985.3.12", 1985 - 1900,  3 - 1, 12},
+        { 0,      "3'6'8", 2003 - 1900,  6 - 1,  8},
+        { 0,   "20130801", 2013 - 1900,  8 - 1,  1},
+        { 1, "01-08-2013", 2013 - 1900,  8 - 1,  1},
+        { 1,  "01-8-2013", 2013 - 1900,  8 - 1,  1},
+        { 1,  "1-08-2013", 2013 - 1900,  8 - 1,  1},
+        { 1,   "1-8-2013", 2013 - 1900,  8 - 1,  1},
+        { 1,   "01-08-13", 2013 - 1900,  8 - 1,  1},
+        { 1,    "01-8-13", 2013 - 1900,  8 - 1,  1},
+        { 1,    "1-08-13", 2013 - 1900,  8 - 1,  1},
+        { 1,     "1-8-13", 2013 - 1900,  8 - 1,  1},
+        { 1, "04/11/2009", 2009 - 1900, 11 - 1,  4},
+        { 1,  "12.3.1985", 1985 - 1900,  3 - 1, 12},
+        { 1,      "8'6'3", 2003 - 1900,  6 - 1,  8},
+        { 1,   "01082013", 2013 - 1900,  8 - 1,  1},
+        { 2, "08-01-2013", 2013 - 1900,  8 - 1,  1},
+        { 2,  "8-01-2013", 2013 - 1900,  8 - 1,  1},
+        { 2,  "08-1-2013", 2013 - 1900,  8 - 1,  1},
+        { 2,   "8-1-2013", 2013 - 1900,  8 - 1,  1},
+        { 2,   "08-01-13", 2013 - 1900,  8 - 1,  1},
+        { 2,    "8-01-13", 2013 - 1900,  8 - 1,  1},
+        { 2,    "08-1-13", 2013 - 1900,  8 - 1,  1},
+        { 2,     "8-1-13", 2013 - 1900,  8 - 1,  1},
+        { 2, "11/04/2009", 2009 - 1900, 11 - 1,  4},
+        { 2,  "3.12.1985", 1985 - 1900,  3 - 1, 12},
+        { 2,      "6'8'3", 2003 - 1900,  6 - 1,  8},
+        { 2,   "08012013", 2013 - 1900,  8 - 1,  1},
+        { 3,      "01-08",   curr_year,  8 - 1,  1},
+        { 3,       "01-8",   curr_year,  8 - 1,  1},
+        { 3,       "1-08",   curr_year,  8 - 1,  1},
+        { 3,        "1-8",   curr_year,  8 - 1,  1},
+        { 3,      "04/11",   curr_year, 11 - 1,  4},
+        { 3,       "12.3",   curr_year,  3 - 1, 12},
+        { 3,        "8'6",   curr_year,  6 - 1,  8},
+        { 3,       "0108",   curr_year,  8 - 1,  1},
+        { 4,      "08-01",   curr_year,  8 - 1,  1},
+        { 4,       "8-01",   curr_year,  8 - 1,  1},
+        { 4,       "08-1",   curr_year,  8 - 1,  1},
+        { 4,        "8-1",   curr_year,  8 - 1,  1},
+        { 4,      "11/04",   curr_year, 11 - 1,  4},
+        { 4,       "3.12",   curr_year,  3 - 1, 12},
+        { 4,        "6'8",   curr_year,  6 - 1,  8},
+        { 4,       "0801",   curr_year,  8 - 1,  1},
+
+        // ambiguous date formats
+        // current parser doesn't know how to disambiguate
+        // and hence refuses to parse
+        // can possibly improved with a smarter parser
+        { 0,     "130801",          -1,     -1, -1},
+        { 1,     "010813",          -1,     -1, -1},
+        { 2,     "080113",          -1,     -1, -1},
+
+        // Combinations that don't make sense
+        // but can still be entered by a user
+        // Should ideally all result in refusal to parse...
+        { 0,      "08-01",          -1,     -1, -1},
+        { 0,       "0801",          -1,     -1, -1},
+        { 1,      "01-08",          -1,     -1, -1},
+        { 1,       "0108",          -1,     -1, -1},
+        { 2,      "08-01",          -1,     -1, -1},
+        { 2,       "0801",          -1,     -1, -1},
+        { 3, "01-08-2013",          -1,     -1, -1},
+        { 3,   "01-08-13",          -1,     -1, -1},
+        { 3,   "08-08-08",          -1,     -1, -1},
+        { 3,   "01082013",          -1,     -1, -1},
+        { 3,     "010813",          -1,     -1, -1},
+        { 3,   "20130108",          -1,     -1, -1},
+        { 4, "08-01-2013",          -1,     -1, -1},
+        { 4,   "08-01-13",          -1,     -1, -1},
+        { 4, "2013-08-01",          -1,     -1, -1},
+        { 4,   "09-08-01",          -1,     -1, -1},
+        { 4,   "08012013",          -1,     -1, -1},
+        { 4,     "080113",          -1,     -1, -1},
+        { 4,   "20130801",          -1,     -1, -1},
+
+        // Sentinel to mark the end of available tests
+        { 0,         NULL,           0,      0,  0},
+
+    };
+    int i = 0;
+
+    gnc_tm_free(tm);
+    while (test_dates[i].date_str)
+    {
+        gboolean success = TRUE;
+        int got_year = 0, got_month = 0, got_day = 0;
+
+        rawtime = parse_date (std::string(test_dates[i].date_str), test_dates[i].date_fmt);
+        if (rawtime == -1)
+            got_year = got_month = got_day = -1;
+        else
+        {
+            tm = gnc_gmtime (&rawtime);
+            got_year = tm->tm_year;
+            got_month = tm->tm_mon;
+            got_day = tm->tm_mday;
+            gnc_tm_free(tm);
+        }
+
+        if ((got_year  != test_dates[i].exp_year) ||
+            (got_month != test_dates[i].exp_month) ||
+            (got_day   != test_dates[i].exp_day))
+        {
+            g_error ("Parse_date failed for date '%s' and format '%d'.\n"
+                            "Expected result: year %d, month %d, day %d\n"
+                            "Obtained result: year %d, month %d, day %d",
+                            test_dates[i].date_str,
+                            test_dates[i].date_fmt,
+                            test_dates[i].exp_year,
+                            test_dates[i].exp_month,
+                            test_dates[i].exp_day,
+                            got_year, got_month, got_day);
+        }
+
+        i++;
+    }
+
+
+}
+/* gnc_csv_new_parse_data
+GncCsvParseData* gnc_csv_new_parse_data (void)// C: 1 in 1  Local: 0:0:0
+*/
+//static void
+//test_gnc_csv_new_parse_data (void)
+//{
+//    GncCsvParseData* parse_data = gnc_csv_new_parse_data ();
+//    g_assert (parse_data != NULL);
+//    g_assert (parse_data->chunk != NULL);
+//    gnc_csv_parse_data_free (parse_data);
+//}
+
+/* gnc_csv_parse_data_free
+void gnc_csv_parse_data_free (GncCsvParseData* parse_data)// C: 3 in 1  Local: 0:0:0
+*/
+// Basic freeing of memory - no test
+
+/* gnc_csv_convert_encoding
+int gnc_csv_convert_encoding (GncCsvParseData* parse_data, const char* encoding,// C: 1  Local: 1:0:0
+*/
+/* static void
+test_gnc_csv_convert_encoding (Fixture *fixture, gconstpointer pData)
+{
+}*/
+/* gnc_csv_load_file
+int gnc_csv_load_file (GncCsvParseData* parse_data, const char* filename,// C: 1  Local: 0:0:0
+*/
+//static void
+//test_gnc_csv_load_file (Fixture *fixture, gconstpointer pData)
+//{
+//
+//    char *file1 = get_filepath ("notexist.csv", FALSE);
+//    char *file2 = get_filepath ("sample1.csv", TRUE);
+//    GError *the_error = NULL;
+//    int resultcode = 0;
+//
+//    /* Test loading of a non-existing file */
+//    resultcode = gnc_csv_load_file (fixture->parse_data, file1,
+//                                    &the_error);
+//    g_assert ((the_error->domain == GNC_CSV_IMP_ERROR) &&
+//              (the_error->code == GNC_CSV_IMP_ERROR_OPEN));
+//
+//    /* Test loading of a valid csv file */
+//    resultcode = gnc_csv_load_file (fixture->parse_data, file2,
+//                                    &the_error);
+//    g_assert (resultcode == 0);
+//}
+/* gnc_csv_parse
+int gnc_csv_parse (GncCsvParseData* parse_data, gboolean guessColTypes, GError** error)// C: 13 in 1  Local: 0:0:0
+*/
+//static void
+//test_gnc_csv_parse_from_file (Fixture *fixture, gconstpointer pData)
+//{
+//    GError *the_error = NULL;
+//    int resultcode = 0;
+//
+//    /* Test basic parsing of the loaded file
+//     * A few fields are sampled in the parsed data. */
+//    resultcode = gnc_csv_parse (fixture->parse_data, TRUE, &the_error);
+//    g_assert (g_strcmp0 ((char*)((GPtrArray*)(fixture->parse_data->orig_lines->pdata[0]))->pdata[0],
+//                         "Date") == 0);
+//    g_assert (g_strcmp0 ((char*)((GPtrArray*)(fixture->parse_data->orig_lines->pdata[1]))->pdata[6],
+//                         "1,100.00") == 0);
+//}
+
+/* Test parsing for several different prepared strings
+ * These tests bypass file loading, rather taking a
+ * prepared set of strings as input. This makes it
+ * easier to add test cases without having to create new test files
+ * each time to load from.
+ * Note this bypasses encoding configuration, which should be tested
+ * independently.
+ */
+
+/* This helper function will run the parse step on the given data
+ * with the parser as configured by the calling test function.
+ * This allows the same code to be used with different csv test strings
+ * and parser option combinations.
+ */
+//static void
+//test_gnc_csv_parse_helper (GncCsvParseData *parse_data, gconstpointer pData)
+//{
+//    parse_test_data *test_data = (parse_test_data *) pData;
+//    GError *the_error = NULL;
+//    int resultcode = 0;
+//    int i = 0;
+//
+//    while (test_data[i].csv_line)
+//    {
+//        int j;
+//        parse_test_data cur_line = test_data[i];
+//
+//        g_test_message("Using string %s\n", cur_line.csv_line);
+//        g_free (parse_data->file_str.begin);
+//        parse_data->file_str.begin = g_strdup (cur_line.csv_line);
+//        parse_data->file_str.end = parse_data->file_str.begin + strlen (parse_data->file_str.begin);
+//        resultcode = gnc_csv_parse (parse_data, TRUE, &the_error);
+//        g_assert (resultcode == 0);
+//        for (j=0; j < cur_line.num_fields; j++)
+//        {
+//            g_assert (g_strcmp0 ((char*)((GPtrArray*)(parse_data->orig_lines->pdata[0]))->pdata[j],
+//                     (cur_line.fields[j])) == 0);
+//        }
+//
+//        i++;
+//    }
+//}
+//
+//static void
+//test_gnc_csv_parse_comma_sep (Fixture *fixture, gconstpointer pData)
+//{
+//    GSList* sep_list = NULL;
+//
+//    sep_list = g_slist_append (sep_list, ",");
+//    stf_parse_options_csv_set_separators (fixture->parse_data->options, NULL, sep_list);
+//    g_slist_free (sep_list);
+//
+//    test_gnc_csv_parse_helper (fixture->parse_data, pData);
+//}
+//
+//static void
+//test_gnc_csv_parse_semicolon_sep (Fixture *fixture, gconstpointer pData)
+//{
+//    GSList* sep_list = NULL;
+//
+//    sep_list = g_slist_append (sep_list, ";");
+//    stf_parse_options_csv_set_separators (fixture->parse_data->options, NULL, sep_list);
+//    g_slist_free (sep_list);
+//
+//    test_gnc_csv_parse_helper (fixture->parse_data, pData);
+//}
+
+/* trans_property_free
+static void trans_property_free (TransProperty* prop)// Local: 2:0:0
+*/
+// Internal helper function - no test
+/* trans_property_set
+static gboolean trans_property_set (TransProperty* prop, char* str)// Local: 1:0:0
+*/
+// Internal helper function - no test
+/* trans_property_list_free
+static void trans_property_list_free (TransPropertyList* list)// Local: 1:0:0
+*/
+// Internal helper function - no test
+/* trans_property_list_add
+static void trans_property_list_add (TransProperty* property)// Local: 1:0:0
+*/
+// Internal helper function - no test
+/* trans_add_split
+static void trans_add_split (Transaction* trans, Account* account, QofBook* book,// Local: 2:0:0
+*/
+// Internal helper function - no test
+/* trans_property_list_verify_essentials
+static gboolean trans_property_list_verify_essentials (TransPropertyList* list, gchar** error)// Local: 1:0:0
+*/
+// Internal helper function - no test
+/* gnc_csv_parse_to_trans
+int gnc_csv_parse_to_trans (GncCsvParseData* parse_data, Account* account,// C: 2 in 1  Local: 0:0:0
+*/
+/* static void
+test_gnc_csv_parse_to_trans (Fixture *fixture, gconstpointer pData)
+{
+}*/
+
+extern "C" {
+void
+test_suite_gnc_csv_imp_trans (void)
+{
+
+// GNC_TEST_ADD (suitename, "parse date with year", Fixture, NULL, setup, test_parse_date_with_year, teardown);
+// GNC_TEST_ADD (suitename, "parse date without year", Fixture, NULL, setup, test_parse_date_without_year, teardown);
+GNC_TEST_ADD_FUNC (suitename, "parse date", test_parse_date);
+//GNC_TEST_ADD_FUNC (suitename, "gnc csv new parse data", test_gnc_csv_new_parse_data);
+// GNC_TEST_ADD (suitename, "gnc csv parse data free", Fixture, NULL, setup, test_gnc_csv_parse_data_free, teardown);
+// GNC_TEST_ADD (suitename, "gnc csv convert encoding", Fixture, NULL, setup, test_gnc_csv_convert_encoding, teardown);
+//GNC_TEST_ADD (suitename, "gnc csv load file", Fixture, NULL, setup, test_gnc_csv_load_file, teardown);
+//GNC_TEST_ADD (suitename, "gnc csv parse from file", Fixture, samplefile1, setup_one_file, test_gnc_csv_parse_from_file, teardown);
+//GNC_TEST_ADD (suitename, "parse comma", Fixture, comma_separated, setup, test_gnc_csv_parse_comma_sep, teardown);
+//GNC_TEST_ADD (suitename, "parse semicolon", Fixture, semicolon_separated, setup, test_gnc_csv_parse_semicolon_sep, teardown);
+// GNC_TEST_ADD (suitename, "trans property free", Fixture, NULL, setup, test_trans_property_free, teardown);
+// GNC_TEST_ADD (suitename, "trans property set", Fixture, NULL, setup, test_trans_property_set, teardown);
+// GNC_TEST_ADD (suitename, "trans property list free", Fixture, NULL, setup, test_trans_property_list_free, teardown);
+// GNC_TEST_ADD (suitename, "trans property list add", Fixture, NULL, setup, test_trans_property_list_add, teardown);
+// GNC_TEST_ADD (suitename, "trans add split", Fixture, NULL, setup, test_trans_add_split, teardown);
+// GNC_TEST_ADD (suitename, "trans property list verify essentials", Fixture, NULL, setup, test_trans_property_list_verify_essentials, teardown);
+// GNC_TEST_ADD (suitename, "gnc csv parse to trans", Fixture, NULL, setup, test_gnc_csv_parse_to_trans, teardown);
+
+}
+}

commit 533b5eb7bea8605998994d29cb8dd287d71fc26f
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Thu Jan 28 17:20:04 2016 +0100

    Convert error_lines GList in a vector of strings
    
    Also keep the errors separate from the original data.
    Use for loop instead of while.

diff --git a/src/import-export/csv-imp/gnc-csv-imp-trans.cpp b/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
index 8a1bf6d..f6b746c 100644
--- a/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
+++ b/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
@@ -355,7 +355,7 @@ GncCsvParseData::GncCsvParseData(GncImpFileFormat format)
     /* All of the data pointers are initially NULL. This is so that, if
      * gnc_csv_parse_data_free is called before all of the data is
      * initialized, only the data that needs to be freed is freed. */
-    error_lines = transactions = NULL;
+    transactions = NULL;
     date_format = -1;
     currency_format = 0;
     start_row = 0;
@@ -372,9 +372,6 @@ GncCsvParseData::~GncCsvParseData()
 {
     /* All non-NULL pointers have been initialized and must be freed. */
 
-    if (error_lines != NULL)
-        g_list_free (error_lines);
-
     if (transactions != NULL)
         g_list_free_full (transactions, g_free);
 }
@@ -996,8 +993,6 @@ int GncCsvParseData::parse_to_trans (Account* account,
                                      gboolean redo_errors)
 {
     gboolean hasBalanceColumn;
-    guint i, j = 0;
-    GList *error_lines_iter = NULL, *begin_error_lines = NULL;
     Account *home_account = NULL;
 
     /* last_transaction points to the last element in
@@ -1006,17 +1001,14 @@ int GncCsvParseData::parse_to_trans (Account* account,
 
     /* Free error_lines and transactions if they
      * already exist. */
-    if (redo_errors) /* If we're redoing errors, we save freeing until the end. */
-        begin_error_lines = error_lines_iter = error_lines;
-    else
+    if (!redo_errors) /* If we're redoing errors, we save freeing until the end. */
     {
-        if (error_lines != NULL)
-            g_list_free(error_lines);
+        line_errors.clear();
+        line_errors.resize(orig_lines.size());
 
         if (transactions != NULL)
             g_list_free (transactions);
     }
-    error_lines = NULL;
 
     if (redo_errors) /* If we're looking only at error data ... */
     {
@@ -1031,17 +1023,9 @@ int GncCsvParseData::parse_to_trans (Account* account,
                 last_transaction = g_list_next (last_transaction);
             }
         }
-        /* ... we use only the lines in error_lines_iter. */
-        if (error_lines_iter == NULL)
-            i = orig_lines.size(); /* Don't go into the for loop. */
-        else
-            i = GPOINTER_TO_INT(error_lines_iter->data);
     }
     else /* Otherwise, we look at all the data. */
     {
-        /* The following while-loop effectively behaves like the following for-loop:
-         * for(i = 0; i < orig_lines->len; i++). */
-        i = start_row;
         last_transaction = NULL;
     }
 
@@ -1049,12 +1033,21 @@ int GncCsvParseData::parse_to_trans (Account* account,
     if (end_row > orig_lines.size())
         end_row = orig_lines.size();
 
-    while (i < end_row)
+    for (uint i = 0; i < end_row; ++i)
     {
+        /* Skip current line if:
+           1. only looking for lines with error AND no error on current line
+           OR
+           2. looking for all lines AND
+              skip_rows is enabled AND
+              current line is an odd line */
+        if ((redo_errors && line_errors[i].empty()) ||
+           (!redo_errors && skip_rows && (i % 2 == 1)))
+            continue;
+
         std::vector<std::string> line = orig_lines[i];
         /* This flag is TRUE if there are any errors in this row. */
         gboolean errors = FALSE;
-        gchar* error_message = NULL;
         TransPropertyList* list;
         GncCsvTransLine* trans_line = NULL;
 
@@ -1063,7 +1056,7 @@ int GncCsvParseData::parse_to_trans (Account* account,
         // If account = NULL, we should have an Account column
         if (home_account == NULL)
         {
-            for (j = 0; j < line.size(); j++)
+            for (uint j = 0; j < line.size(); j++)
             {
                 /* Look for "Account" columns. */
                 if (column_types[j] == GNC_CSV_ACCOUNT)
@@ -1075,7 +1068,7 @@ int GncCsvParseData::parse_to_trans (Account* account,
 
         if (home_account == NULL)
         {
-            error_message = g_strdup_printf (_("Account column could not be understood."));
+            line_errors[i] = _("Account column could not be understood.");
             errors = TRUE;
         }
         else
@@ -1097,8 +1090,10 @@ int GncCsvParseData::parse_to_trans (Account* account,
                     else
                     {
                         errors = TRUE;
-                        error_message = g_strdup_printf (_("%s column could not be understood."),
+                        gchar *error_message = g_strdup_printf (_("%s column could not be understood."),
                                                         _(gnc_csv_column_type_strs[property->type]));
+                        line_errors[i] = error_message;
+                        g_free (error_message);
                         trans_property_free (property);
                         break;
                     }
@@ -1108,30 +1103,21 @@ int GncCsvParseData::parse_to_trans (Account* account,
             /* If we had success, add the transaction to transaction. */
             if (!errors)
             {
+                gchar *error_message = NULL;
                 trans_line = trans_property_list_to_trans (list, &error_message);
-                errors = trans_line == NULL;
+                errors = (trans_line == NULL);
+                if (errors)
+                {
+                    line_errors[i] = error_message;
+                    g_free (error_message);
+                }
             }
             trans_property_list_free (list);
         }
 
-        /* If there were errors, add this line to error_lines. */
-        if (errors)
-        {
-            error_lines = g_list_append (error_lines,
-                                         GINT_TO_POINTER(i));
-            /* If there's already an error message at the end of the line,
-             * we need to replace it with the new one, otherwise append the
-             * new one at the end of the line */
-            if (line.size() > (guint)(orig_row_lengths[i]))
-                line[line.size() - 1] = error_message;
-            else
-            {
-                line.push_back (std::string(error_message));
-            }
-        }
-        else
+        /* If all went well, add this transaction to the list. */
+        if (!errors)
         {
-            /* If all went well, add this transaction to the list. */
             trans_line->line_no = i;
 
             /* We keep the transactions sorted by date. We start at the end
@@ -1169,24 +1155,6 @@ int GncCsvParseData::parse_to_trans (Account* account,
                 transactions = g_list_insert_before (transactions, insertion_spot, trans_line);
             }
         }
-
-        /* Increment to the next row. */
-        if (redo_errors)
-        {
-            /* Move to the next error line in the list. */
-            error_lines_iter = g_list_next (error_lines_iter);
-            if (error_lines_iter == NULL)
-                i = orig_lines.size(); /* Don't continue the for loop. */
-            else
-                i = GPOINTER_TO_INT(error_lines_iter->data);
-        }
-        else
-        {
-            if (skip_rows == FALSE)
-                i++;
-            else
-                i = i + 2;
-        }
     }
 
     /* If we have a balance column, set the appropriate amounts on the transactions. */
@@ -1259,18 +1227,6 @@ int GncCsvParseData::parse_to_trans (Account* account,
         }
     }
 
-    if (redo_errors) /* Now that we're at the end, we do the freeing. */
-        g_list_free (begin_error_lines);
-
-    /* We need to resize column_types since errors may have added columns. */
-    std::vector<std::string>::size_type max_cols = 0;
-    for(std::vector<str_vec>::iterator it = orig_lines.begin(); it != orig_lines.end(); ++it)
-    {
-        if (it->size() > max_cols)
-            max_cols = it->size();
-    }
-    column_types.resize(max_cols, GNC_CSV_NONE);
-
     return 0;
 }
 
diff --git a/src/import-export/csv-imp/gnc-csv-imp-trans.hpp b/src/import-export/csv-imp/gnc-csv-imp-trans.hpp
index 07194d2..c3c83a5 100644
--- a/src/import-export/csv-imp/gnc-csv-imp-trans.hpp
+++ b/src/import-export/csv-imp/gnc-csv-imp-trans.hpp
@@ -128,7 +128,6 @@ public:
     std::unique_ptr<GncTokenizer> tokenizer;    /**< Will handle file loading/encoding conversion/splitting into fields */
     std::vector<str_vec_t> orig_lines;      /**< file_str parsed into a two-dimensional array of strings */
     std::vector<str_vec>::size_type orig_max_row;           /**< Holds the maximum value in orig_row_lengths */
-    GList* error_lines;         /**< List of row numbers in orig_lines that have errors */
     std::vector<GncCsvColumnType> column_types;       /**< Vector of values from the GncCsvColumnType enumeration */
     GList* transactions;        /**< List of GncCsvTransLine*s created using orig_lines and column_types */
     int date_format;            /**< The format of the text in the date columns from date_format_internal. */
@@ -141,6 +140,7 @@ private:
     std::vector<std::vector<str_vec>::size_type>
               orig_row_lengths; /**< The lengths of rows in orig_lines
                                       before error messages are appended */
+    std::vector<std::string> line_errors;
     GncImpFileFormat file_fmt = GncImpFileFormat::UNKNOWN;
 };
 

commit cecfe9ecb871a1e1bb92d4b7812e51d2272c0fa4
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Thu Jan 28 16:21:12 2016 +0100

    Replace stf based file parser with gnc-tokenizer based one

diff --git a/src/import-export/csv-imp/gnc-csv-imp-trans.cpp b/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
index 5c044de..8a1bf6d 100644
--- a/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
+++ b/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
@@ -27,7 +27,6 @@ extern "C" {
 #endif
 
 #include <glib/gi18n.h>
-#include <goffice/go-glib-extras.h>
 
 #include "gnc-csv-account-map.h"
 #include "gnc-ui-util.h"
@@ -44,6 +43,8 @@ extern "C" {
 }
 
 #include "gnc-csv-imp-trans.hpp"
+#include "gnc-csv-tokenizer.hpp"
+#include "gnc-fw-tokenizer.hpp"
 
 GQuark
 gnc_csv_imp_error_quark (void)
@@ -83,17 +84,6 @@ G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT;
 //        N_("Other Memo")
 //};
 
-/** A set of sensible defaults for parsing CSV files.
- * @return StfParseOptions_t* for parsing a file with comma separators
- */
-static StfParseOptions_t* default_parse_options (void)
-{
-    StfParseOptions_t* options = stf_parse_options_new();
-    stf_parse_options_set_type (options, PARSE_TYPE_CSV);
-    stf_parse_options_csv_set_separators (options, ",", NULL);
-    return options;
-}
-
 /** Parses a string into a date, given a format. The format must
  * include the year. This function should only be called by
  * parse_date.
@@ -360,24 +350,20 @@ time64 parse_date (const char* date_str, int format)
 /** Constructor for GncCsvParseData.
  * @return Pointer to a new GncCSvParseData
  */
-GncCsvParseData::GncCsvParseData()
+GncCsvParseData::GncCsvParseData(GncImpFileFormat format)
 {
-    encoding = "UTF-8";
     /* All of the data pointers are initially NULL. This is so that, if
      * gnc_csv_parse_data_free is called before all of the data is
      * initialized, only the data that needs to be freed is freed. */
-    raw_mapping = NULL;
-    raw_str.begin = raw_str.end = file_str.begin = file_str.end = NULL;
-    orig_lines = NULL;
-    orig_row_lengths = NULL;
     error_lines = transactions = NULL;
-    options = default_parse_options();
     date_format = -1;
     currency_format = 0;
-    chunk = g_string_chunk_new(100 * 1024);
     start_row = 0;
     end_row = 1000;
     skip_rows = FALSE;
+
+    file_fmt = format;
+    tokenizer = GncTokenizerFactory(file_fmt);
 }
 
 /** Destructor for GncCsvParseData.
@@ -386,30 +372,40 @@ GncCsvParseData::~GncCsvParseData()
 {
     /* All non-NULL pointers have been initialized and must be freed. */
 
-    if (raw_mapping != NULL)
-    {
-        g_mapped_file_unref (raw_mapping);
-    }
-
-    if (file_str.begin != NULL)
-        g_free (file_str.begin);
-
-    if (orig_lines != NULL)
-        stf_parse_general_free (orig_lines);
-
-    if (orig_row_lengths != NULL)
-        g_array_free (orig_row_lengths, FALSE);
-
-    if (options != NULL)
-        stf_parse_options_free (options);
-
     if (error_lines != NULL)
         g_list_free (error_lines);
 
     if (transactions != NULL)
         g_list_free_full (transactions, g_free);
+}
 
-    g_string_chunk_free (chunk);
+int GncCsvParseData::file_format(GncImpFileFormat format,
+                                  GError** error)
+{
+    if (file_fmt == format)
+        return 0;
+
+    std::string new_encoding = "UTF-8";
+    std::string new_imp_file;
+
+    // Recover common settings from old tokenizer
+    if (tokenizer)
+    {
+        new_encoding = tokenizer->encoding();
+        new_imp_file = tokenizer->current_file();
+    }
+
+    file_fmt = format;
+    tokenizer = GncTokenizerFactory(file_fmt);
+
+    // Set up new tokenizer with common settings
+    // recovered from old tokenizer
+    tokenizer->encoding(new_encoding);
+    return load_file(new_imp_file.c_str(), error);
+}
+GncImpFileFormat GncCsvParseData::file_format()
+{
+    return file_fmt;
 }
 
 /** Converts raw file data using a new encoding. This function must be
@@ -420,32 +416,11 @@ GncCsvParseData::~GncCsvParseData()
  * @param error Will point to an error on failure
  * @return 0 on success, 1 on failure
  */
-int GncCsvParseData::convert_encoding (const char* encoding,
-                                       GError** error)
+void GncCsvParseData::convert_encoding (const std::string& encoding)
 {
-    gsize bytes_read, bytes_written;
-
-    /* If file_str has already been initialized it must be
-     * freed first. (This should always be the case, since
-     * gnc_csv_load_file should always be called before this
-     * function.) */
-    if (file_str.begin != NULL)
-        g_free(file_str.begin);
-
-    /* Do the actual translation to UTF-8. */
-    file_str.begin = g_convert (raw_str.begin,
-                                           raw_str.end - raw_str.begin,
-                                           "UTF-8", encoding, &bytes_read, &bytes_written,
-                                           error);
-    /* Handle errors that occur. */
-    if (file_str.begin == NULL)
-        return 1;
-
-    /* On success, save the ending pointer of the translated data and
-     * the encoding type and return 0. */
-    file_str.end = file_str.begin + bytes_written;
-    encoding = (gchar*)encoding;
-    return 0;
+    // TODO investigate if we can catch conversion errors and report them
+    if (tokenizer)
+        tokenizer->encoding(encoding);
 }
 
 /** Loads a file into a GncCsvParseData. This is the first function
@@ -463,43 +438,21 @@ int GncCsvParseData::convert_encoding (const char* encoding,
 int GncCsvParseData::load_file (const char* filename,
                                 GError** error)
 {
-    const char* guess_enc = NULL;
 
     /* Get the raw data first and handle an error if one occurs. */
-    raw_mapping = g_mapped_file_new (filename, FALSE, error);
-    if (raw_mapping == NULL)
+    try
+    {
+        tokenizer->load_file (filename);
+        return 0;
+    }
+    catch (std::ifstream::failure& ios_err)
     {
         /* TODO Handle file opening errors more specifically,
          * e.g. inexistent file versus no read permission. */
-        raw_str.begin = NULL;
+        PWARN ("Error: %s", ios_err.what());
         g_set_error (error, GNC_CSV_IMP_ERROR, GNC_CSV_IMP_ERROR_OPEN, "%s", _("File opening failed."));
         return 1;
     }
-
-    /* Copy the mapping's contents into parse-data->raw_str. */
-    raw_str.begin = g_mapped_file_get_contents (raw_mapping);
-    raw_str.end = raw_str.begin + g_mapped_file_get_length (raw_mapping);
-
-    /* Make a guess at the encoding of the data. */
-    if (!g_mapped_file_get_length (raw_mapping) == 0)
-        guess_enc = go_guess_encoding ((const char*)(raw_str.begin),
-                                      (size_t)(raw_str.end - raw_str.begin),
-                                      "UTF-8", NULL);
-    if (guess_enc == NULL)
-    {
-        g_set_error (error, GNC_CSV_IMP_ERROR, GNC_CSV_IMP_ERROR_ENCODING, "%s", _("Unknown encoding."));
-        return 1;
-    }
-    /* Convert using the guessed encoding into file_str and
-     * handle any errors that occur. */
-    convert_encoding (guess_enc, error);
-    if (file_str.begin == NULL)
-    {
-        g_set_error (error, GNC_CSV_IMP_ERROR, GNC_CSV_IMP_ERROR_ENCODING, "%s", _("Unknown encoding."));
-        return 1;
-    }
-    else
-        return 0;
 }
 
 /** Parses a file into cells. This requires having an encoding that
@@ -516,82 +469,43 @@ int GncCsvParseData::load_file (const char* filename,
  */
 int GncCsvParseData::parse (gboolean guessColTypes, GError** error)
 {
-    /* max_cols is the number of columns in the row with the most columns. */
-    guint i, max_cols = 0;
+    tokenizer->tokenize();
+    orig_lines.clear();
+    orig_lines = tokenizer->get_tokens();
 
-    if (orig_lines != NULL)
+    /* If it failed, generate an error. */
+    if (orig_lines.size() == 0)
     {
-        stf_parse_general_free (orig_lines);
+        g_set_error (error, GNC_CSV_IMP_ERROR, GNC_CSV_IMP_ERROR_PARSE, "Parsing failed.");
+        return 1;
     }
 
-    /* If everything is fine ... */
-    if (file_str.begin != NULL)
-    {
-        /* Do the actual parsing. */
-        orig_lines = stf_parse_general (options, chunk,
-                                 file_str.begin,
-                                 file_str.end);
-    }
-    /* If we couldn't get the encoding right, we just want an empty array. */
-    else
-    {
-        orig_lines = g_ptr_array_new();
-    }
 
     /* Record the original row lengths of orig_lines. */
-    if (orig_row_lengths != NULL)
-        g_array_free (orig_row_lengths, FALSE);
-
-    orig_row_lengths =
-        g_array_sized_new (FALSE, FALSE, sizeof(int), orig_lines->len);
+    orig_row_lengths.clear();
 
-    g_array_set_size (orig_row_lengths, orig_lines->len);
+    orig_row_lengths.reserve(orig_lines.size());
     orig_max_row = 0;
-    for (i = 0; i < orig_lines->len; i++)
+    for(std::vector<str_vec>::iterator it = orig_lines.begin(); it != orig_lines.end(); ++it)
     {
-        int length = ((GPtrArray*)orig_lines->pdata[i])->len;
-        orig_row_lengths->data[i] = length;
+        std::vector<std::string>::size_type length = it->size();
+        orig_row_lengths.push_back(length);
         if (length > orig_max_row)
             orig_max_row = length;
     }
 
-    /* If it failed, generate an error. */
-    if (orig_lines == NULL)
-    {
-        g_set_error (error, GNC_CSV_IMP_ERROR, GNC_CSV_IMP_ERROR_PARSE, "Parsing failed.");
-        return 1;
-    }
-
-    /* Now that we have data, let's set max_cols. */
-    for (i = 0; i < orig_lines->len; i++)
-    {
-        if (max_cols < ((GPtrArray*)(orig_lines->pdata[i]))->len)
-            max_cols = ((GPtrArray*)(orig_lines->pdata[i]))->len;
-    }
-
     if (guessColTypes)
     {
         /* Free column_types if it's already been created. */
         column_types.clear();
+    }
+    column_types.resize(orig_max_row, GNC_CSV_NONE);
 
-        /* Create column_types and fill it with guesses based
+    if (guessColTypes)
+    {
+        /* Guess column_types based
          * on the contents of each column. */
         /* TODO Make it actually guess. */
-        for (i = 0; i < max_cols; i++)
-        {
-            column_types.push_back(GNC_CSV_NONE);
-        }
-    }
-    else
-    {
-        /* If we don't need to guess column types, we will simply set any
-         * new columns that are created that didn't exist before to "None"
-         * since we don't want gibberish to appear. */
-        i = column_types.size();
-        for (; i < max_cols; i++)
-        {
-            column_types.push_back(GNC_CSV_NONE);
-        }
     }
     return 0;
 }
@@ -656,7 +570,7 @@ static void trans_property_free (TransProperty* prop)
  * @param str The string to be parsed
  * @return TRUE on success, FALSE on failure
  */
-static gboolean trans_property_set (TransProperty* prop, char* str)
+static gboolean trans_property_set (TransProperty* prop, const char* str)
 {
     char *endptr, *possible_currency_symbol, *str_dupe;
     gnc_numeric val;
@@ -1082,7 +996,7 @@ int GncCsvParseData::parse_to_trans (Account* account,
                                      gboolean redo_errors)
 {
     gboolean hasBalanceColumn;
-    guint i, j, max_cols = 0;
+    guint i, j = 0;
     GList *error_lines_iter = NULL, *begin_error_lines = NULL;
     Account *home_account = NULL;
 
@@ -1119,7 +1033,7 @@ int GncCsvParseData::parse_to_trans (Account* account,
         }
         /* ... we use only the lines in error_lines_iter. */
         if (error_lines_iter == NULL)
-            i = orig_lines->len; /* Don't go into the for loop. */
+            i = orig_lines.size(); /* Don't go into the for loop. */
         else
             i = GPOINTER_TO_INT(error_lines_iter->data);
     }
@@ -1132,12 +1046,12 @@ int GncCsvParseData::parse_to_trans (Account* account,
     }
 
     /* set end_row to number of lines */
-    if (end_row > orig_lines->len)
-        end_row = orig_lines->len;
+    if (end_row > orig_lines.size())
+        end_row = orig_lines.size();
 
     while (i < end_row)
     {
-        GPtrArray* line = (GPtrArray*) orig_lines->pdata[i];
+        std::vector<std::string> line = orig_lines[i];
         /* This flag is TRUE if there are any errors in this row. */
         gboolean errors = FALSE;
         gchar* error_message = NULL;
@@ -1149,12 +1063,12 @@ int GncCsvParseData::parse_to_trans (Account* account,
         // If account = NULL, we should have an Account column
         if (home_account == NULL)
         {
-            for (j = 0; j < line->len; j++)
+            for (j = 0; j < line.size(); j++)
             {
                 /* Look for "Account" columns. */
                 if (column_types[j] == GNC_CSV_ACCOUNT)
                 {
-                    home_account = gnc_csv_account_map_search ((gchar*) line->pdata[j]);
+                    home_account = gnc_csv_account_map_search (line[j].c_str());
                 }
             }
         }
@@ -1168,14 +1082,14 @@ int GncCsvParseData::parse_to_trans (Account* account,
         {
             list = trans_property_list_new (home_account, date_format, currency_format);
 
-            for (j = 0; j < line->len; j++)
+            for (j = 0; j < line.size(); j++)
             {
                 /* We do nothing in "None" or "Account" columns. */
                 if ((column_types[j] != GNC_CSV_NONE) && (column_types[j] != GNC_CSV_ACCOUNT))
                 {
                     /* Affect the transaction appropriately. */
                     TransProperty* property = trans_property_new (column_types[j], list);
-                    gboolean succeeded = trans_property_set (property, (gchar *) line->pdata[j]);
+                    gboolean succeeded = trans_property_set (property, line[j].c_str());
 
                     /* TODO Maybe move error handling to within TransPropertyList functions? */
                     if (succeeded)
@@ -1205,16 +1119,14 @@ int GncCsvParseData::parse_to_trans (Account* account,
         {
             error_lines = g_list_append (error_lines,
                                          GINT_TO_POINTER(i));
-            /* If there's already an error message, we need to replace it. */
-            if (line->len > (guint)(orig_row_lengths->data[i]))
-            {
-                g_free(line->pdata[line->len - 1]);
-                line->pdata[line->len - 1] = error_message;
-            }
+            /* If there's already an error message at the end of the line,
+             * we need to replace it with the new one, otherwise append the
+             * new one at the end of the line */
+            if (line.size() > (guint)(orig_row_lengths[i]))
+                line[line.size() - 1] = error_message;
             else
             {
-                /* Put the error message at the end of the line. */
-                g_ptr_array_add (line, error_message);
+                line.push_back (std::string(error_message));
             }
         }
         else
@@ -1264,7 +1176,7 @@ int GncCsvParseData::parse_to_trans (Account* account,
             /* Move to the next error line in the list. */
             error_lines_iter = g_list_next (error_lines_iter);
             if (error_lines_iter == NULL)
-                i = orig_lines->len; /* Don't continue the for loop. */
+                i = orig_lines.size(); /* Don't continue the for loop. */
             else
                 i = GPOINTER_TO_INT(error_lines_iter->data);
         }
@@ -1351,14 +1263,13 @@ int GncCsvParseData::parse_to_trans (Account* account,
         g_list_free (begin_error_lines);
 
     /* We need to resize column_types since errors may have added columns. */
-    for (i = 0; i < orig_lines->len; i++)
+    std::vector<std::string>::size_type max_cols = 0;
+    for(std::vector<str_vec>::iterator it = orig_lines.begin(); it != orig_lines.end(); ++it)
     {
-        if (max_cols < ((GPtrArray*)(orig_lines->pdata[i]))->len)
-            max_cols = ((GPtrArray*)(orig_lines->pdata[i]))->len;
+        if (it->size() > max_cols)
+            max_cols = it->size();
     }
-    i = column_types.size();
-    for (; i < max_cols; i++)
-        column_types.push_back(GNC_CSV_NONE);
+    column_types.resize(max_cols, GNC_CSV_NONE);
 
     return 0;
 }
diff --git a/src/import-export/csv-imp/gnc-csv-imp-trans.hpp b/src/import-export/csv-imp/gnc-csv-imp-trans.hpp
index e4677ac..07194d2 100644
--- a/src/import-export/csv-imp/gnc-csv-imp-trans.hpp
+++ b/src/import-export/csv-imp/gnc-csv-imp-trans.hpp
@@ -34,10 +34,12 @@ extern "C" {
 
 #include "Account.h"
 #include "Transaction.h"
-#include "stf/stf-parse.h"
 }
 
 #include <vector>
+#include <memory>
+
+#include "gnc-tokenizer.hpp"
 
 
 /** Enumeration for column types. These are the different types of
@@ -73,15 +75,6 @@ enum GncCsvErrorType {
     GNC_CSV_IMP_ERROR_PARSE
 };
 
-/** Struct for containing a string. This struct simply contains
- * pointers to the beginning and end of a string. We need this because
- * the STF code that gnc_csv_parse calls requires these pointers. */
-typedef struct
-{
-    char* begin;
-    char* end;
-} GncCsvStr;
-
 /* TODO We now sort transactions by date, not line number, so we
  * should probably get rid of this struct and uses of it. */
 
@@ -112,28 +105,30 @@ extern const gchar* date_format_user[];
 /* This array contains all of the different strings for different column types. */
 extern const gchar* gnc_csv_column_type_strs[];
 
+using str_vec_t = std::vector<std::string> ;
+
 /** Struct containing data for parsing a CSV/Fixed-Width file. */
 class GncCsvParseData
 {
 public:
     // Constructor - Destructor
-    GncCsvParseData();
+    GncCsvParseData(GncImpFileFormat format = GncImpFileFormat::UNKNOWN);
     ~GncCsvParseData();
 
+    int file_format(GncImpFileFormat format, GError** error);
+    GncImpFileFormat file_format();
+
     int load_file (const char* filename, GError** error);
-    int convert_encoding (const char* encoding, GError** error);
+    void convert_encoding (const std::string& encoding);
 
     int parse (gboolean guessColTypes, GError** error);
     int parse_to_trans (Account* account, gboolean redo_errors);
     bool check_for_column_type (int type);
 
-
-    const gchar* encoding;
-    GncCsvStr file_str;         /**< raw_str translated into UTF-8 */
-    GPtrArray* orig_lines;      /**< file_str parsed into a two-dimensional array of strings */
-    int orig_max_row;           /**< Holds the maximum value in orig_row_lengths */
+    std::unique_ptr<GncTokenizer> tokenizer;    /**< Will handle file loading/encoding conversion/splitting into fields */
+    std::vector<str_vec_t> orig_lines;      /**< file_str parsed into a two-dimensional array of strings */
+    std::vector<str_vec>::size_type orig_max_row;           /**< Holds the maximum value in orig_row_lengths */
     GList* error_lines;         /**< List of row numbers in orig_lines that have errors */
-    StfParseOptions_t* options; /**< Options controlling how file_str should be parsed */
     std::vector<GncCsvColumnType> column_types;       /**< Vector of values from the GncCsvColumnType enumeration */
     GList* transactions;        /**< List of GncCsvTransLine*s created using orig_lines and column_types */
     int date_format;            /**< The format of the text in the date columns from date_format_internal. */
@@ -143,11 +138,10 @@ public:
     int currency_format;        /**< The currency format, 0 for locale, 1 for comma dec and 2 for period */
 
 private:
-    GMappedFile* raw_mapping;   /**< The mapping containing raw_str */
-    GncCsvStr raw_str;          /**< Untouched data from the file as a string */
-    GArray* orig_row_lengths;   /**< The lengths of rows in orig_lines
+    std::vector<std::vector<str_vec>::size_type>
+              orig_row_lengths; /**< The lengths of rows in orig_lines
                                       before error messages are appended */
-    GStringChunk* chunk;        /**< A chunk of memory in which the contents of orig_lines is stored */
+    GncImpFileFormat file_fmt = GncImpFileFormat::UNKNOWN;
 };
 
 time64 parse_date (const char* date_str, int format);

commit ce63d8aab0e2a929766b850bb48970fa4f4a6681
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Fri May 8 21:59:00 2015 +0200

    Use vector instead of GArray for column_types class member

diff --git a/src/import-export/csv-imp/gnc-csv-imp-trans.cpp b/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
index dd8299a..5c044de 100644
--- a/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
+++ b/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
@@ -370,7 +370,6 @@ GncCsvParseData::GncCsvParseData()
     raw_str.begin = raw_str.end = file_str.begin = file_str.end = NULL;
     orig_lines = NULL;
     orig_row_lengths = NULL;
-    column_types = NULL;
     error_lines = transactions = NULL;
     options = default_parse_options();
     date_format = -1;
@@ -404,9 +403,6 @@ GncCsvParseData::~GncCsvParseData()
     if (options != NULL)
         stf_parse_options_free (options);
 
-    if (column_types != NULL)
-        g_array_free (column_types, TRUE);
-
     if (error_lines != NULL)
         g_list_free (error_lines);
 
@@ -576,32 +572,25 @@ int GncCsvParseData::parse (gboolean guessColTypes, GError** error)
     if (guessColTypes)
     {
         /* Free column_types if it's already been created. */
-        if (column_types != NULL)
-            g_array_free (column_types, TRUE);
+        column_types.clear();
 
         /* Create column_types and fill it with guesses based
          * on the contents of each column. */
-        column_types = g_array_sized_new (FALSE, FALSE, sizeof(int),
-                                   max_cols);
-        g_array_set_size (column_types, max_cols);
         /* TODO Make it actually guess. */
-        for (i = 0; i < column_types->len; i++)
+        for (i = 0; i < max_cols; i++)
         {
-            column_types->data[i] = GNC_CSV_NONE;
+            column_types.push_back(GNC_CSV_NONE);
         }
     }
     else
     {
         /* If we don't need to guess column types, we will simply set any
          * new columns that are created that didn't exist before to "None"
-         * since we don't want gibberish to appear. Note:
-         * column_types should have already been
-         * initialized, so we don't check for it being NULL. */
-        i = column_types->len;
-        g_array_set_size (column_types, max_cols);
-        for (; i < column_types->len; i++)
+         * since we don't want gibberish to appear. */
+        i = column_types.size();
+        for (; i < max_cols; i++)
         {
-            column_types->data[i] = GNC_CSV_NONE;
+            column_types.push_back(GNC_CSV_NONE);
         }
     }
     return 0;
@@ -1163,7 +1152,7 @@ int GncCsvParseData::parse_to_trans (Account* account,
             for (j = 0; j < line->len; j++)
             {
                 /* Look for "Account" columns. */
-                if (column_types->data[j] == GNC_CSV_ACCOUNT)
+                if (column_types[j] == GNC_CSV_ACCOUNT)
                 {
                     home_account = gnc_csv_account_map_search ((gchar*) line->pdata[j]);
                 }
@@ -1182,10 +1171,10 @@ int GncCsvParseData::parse_to_trans (Account* account,
             for (j = 0; j < line->len; j++)
             {
                 /* We do nothing in "None" or "Account" columns. */
-                if ((column_types->data[j] != GNC_CSV_NONE) && (column_types->data[j] != GNC_CSV_ACCOUNT))
+                if ((column_types[j] != GNC_CSV_NONE) && (column_types[j] != GNC_CSV_ACCOUNT))
                 {
                     /* Affect the transaction appropriately. */
-                    TransProperty* property = trans_property_new (column_types->data[j], list);
+                    TransProperty* property = trans_property_new (column_types[j], list);
                     gboolean succeeded = trans_property_set (property, (gchar *) line->pdata[j]);
 
                     /* TODO Maybe move error handling to within TransPropertyList functions? */
@@ -1290,9 +1279,9 @@ int GncCsvParseData::parse_to_trans (Account* account,
 
     /* If we have a balance column, set the appropriate amounts on the transactions. */
     hasBalanceColumn = FALSE;
-    for (i = 0; i < column_types->len; i++)
+    for (i = 0; i < column_types.size(); i++)
     {
-        if (column_types->data[i] == GNC_CSV_BALANCE)
+        if (column_types[i] == GNC_CSV_BALANCE)
         {
             hasBalanceColumn = TRUE;
             break;
@@ -1367,12 +1356,10 @@ int GncCsvParseData::parse_to_trans (Account* account,
         if (max_cols < ((GPtrArray*)(orig_lines->pdata[i]))->len)
             max_cols = ((GPtrArray*)(orig_lines->pdata[i]))->len;
     }
-    i = column_types->len;
-    column_types = g_array_set_size (column_types, max_cols);
+    i = column_types.size();
     for (; i < max_cols; i++)
-    {
-        column_types->data[i] = GNC_CSV_NONE;
-    }
+        column_types.push_back(GNC_CSV_NONE);
+
     return 0;
 }
 
@@ -1381,11 +1368,11 @@ bool
 GncCsvParseData::check_for_column_type (int type)
 {
     gboolean ret = FALSE;
-    int j, ncols = column_types->len; /* ncols is the number of columns in the data. */
+    int j, ncols = column_types.size(); /* ncols is the number of columns in the data. */
 
     for (j = 0; j < ncols; j++)
     {
-        if (column_types->data[j] == type)
+        if (column_types[j] == type)
             ret = TRUE;
     }
     return ret;
diff --git a/src/import-export/csv-imp/gnc-csv-imp-trans.hpp b/src/import-export/csv-imp/gnc-csv-imp-trans.hpp
index 159c3a0..e4677ac 100644
--- a/src/import-export/csv-imp/gnc-csv-imp-trans.hpp
+++ b/src/import-export/csv-imp/gnc-csv-imp-trans.hpp
@@ -34,9 +34,11 @@ extern "C" {
 
 #include "Account.h"
 #include "Transaction.h"
+#include "stf/stf-parse.h"
 }
 
-#include "stf/stf-parse.h"
+#include <vector>
+
 
 /** Enumeration for column types. These are the different types of
  * columns that can exist in a CSV/Fixed-Width file. There should be
@@ -132,7 +134,7 @@ public:
     int orig_max_row;           /**< Holds the maximum value in orig_row_lengths */
     GList* error_lines;         /**< List of row numbers in orig_lines that have errors */
     StfParseOptions_t* options; /**< Options controlling how file_str should be parsed */
-    GArray* column_types;       /**< Array of values from the GncCsvColumnType enumeration */
+    std::vector<GncCsvColumnType> column_types;       /**< Vector of values from the GncCsvColumnType enumeration */
     GList* transactions;        /**< List of GncCsvTransLine*s created using orig_lines and column_types */
     int date_format;            /**< The format of the text in the date columns from date_format_internal. */
     guint start_row;            /**< The start row to generate transactions from. */

commit 1507ec0c30116228423a6250f13d26962c0096bb
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sat Jan 23 16:43:28 2016 +0100

    Convert GncCsvParseData into a class
    
    This is just an initial conversion. Lots of
    work is still needed to get to proper data encapsulation
    and using modern c++ semantics

diff --git a/src/import-export/csv-imp/gnc-csv-imp-trans.cpp b/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
index ba74c0c..dd8299a 100644
--- a/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
+++ b/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
@@ -360,76 +360,60 @@ time64 parse_date (const char* date_str, int format)
 /** Constructor for GncCsvParseData.
  * @return Pointer to a new GncCSvParseData
  */
-GncCsvParseData* gnc_csv_new_parse_data (void)
+GncCsvParseData::GncCsvParseData()
 {
-    GncCsvParseData* parse_data = g_new(GncCsvParseData, 1);
-    parse_data->encoding = "UTF-8";
+    encoding = "UTF-8";
     /* All of the data pointers are initially NULL. This is so that, if
      * gnc_csv_parse_data_free is called before all of the data is
      * initialized, only the data that needs to be freed is freed. */
-    parse_data->raw_mapping = NULL;
-    parse_data->raw_str.begin = parse_data->raw_str.end
-                                = parse_data->file_str.begin = parse_data->file_str.end = NULL;
-    parse_data->orig_lines = NULL;
-    parse_data->orig_row_lengths = NULL;
-    parse_data->column_types = NULL;
-    parse_data->error_lines = parse_data->transactions = NULL;
-    parse_data->options = default_parse_options();
-    parse_data->date_format = -1;
-    parse_data->currency_format = 0;
-    parse_data->chunk = g_string_chunk_new(100 * 1024);
-    parse_data->start_row = 0;
-    parse_data->end_row = 1000;
-    parse_data->skip_rows = FALSE;
-    return parse_data;
+    raw_mapping = NULL;
+    raw_str.begin = raw_str.end = file_str.begin = file_str.end = NULL;
+    orig_lines = NULL;
+    orig_row_lengths = NULL;
+    column_types = NULL;
+    error_lines = transactions = NULL;
+    options = default_parse_options();
+    date_format = -1;
+    currency_format = 0;
+    chunk = g_string_chunk_new(100 * 1024);
+    start_row = 0;
+    end_row = 1000;
+    skip_rows = FALSE;
 }
 
 /** Destructor for GncCsvParseData.
- * @param parse_data Parse data whose memory will be freed
  */
-void gnc_csv_parse_data_free (GncCsvParseData* parse_data)
+GncCsvParseData::~GncCsvParseData()
 {
     /* All non-NULL pointers have been initialized and must be freed. */
 
-    if (parse_data->raw_mapping != NULL)
+    if (raw_mapping != NULL)
     {
-        g_mapped_file_unref (parse_data->raw_mapping);
+        g_mapped_file_unref (raw_mapping);
     }
 
-    if (parse_data->file_str.begin != NULL)
-        g_free (parse_data->file_str.begin);
+    if (file_str.begin != NULL)
+        g_free (file_str.begin);
 
-    if (parse_data->orig_lines != NULL)
-        stf_parse_general_free (parse_data->orig_lines);
+    if (orig_lines != NULL)
+        stf_parse_general_free (orig_lines);
 
-    if (parse_data->orig_row_lengths != NULL)
-        g_array_free (parse_data->orig_row_lengths, FALSE);
+    if (orig_row_lengths != NULL)
+        g_array_free (orig_row_lengths, FALSE);
 
-    if (parse_data->options != NULL)
-        stf_parse_options_free (parse_data->options);
+    if (options != NULL)
+        stf_parse_options_free (options);
 
-    if (parse_data->column_types != NULL)
-        g_array_free (parse_data->column_types, TRUE);
+    if (column_types != NULL)
+        g_array_free (column_types, TRUE);
 
-    if (parse_data->error_lines != NULL)
-        g_list_free (parse_data->error_lines);
+    if (error_lines != NULL)
+        g_list_free (error_lines);
 
-    if (parse_data->transactions != NULL)
-    {
-        GList* transactions = parse_data->transactions;
-        /* We have to free the GncCsvTransLine's that are at each node in
-         * the list before freeing the entire list. */
-        do
-        {
-            g_free (transactions->data);
-            transactions = g_list_next (transactions);
-        }
-        while (transactions != NULL);
-        g_list_free (parse_data->transactions);
-    }
+    if (transactions != NULL)
+        g_list_free_full (transactions, g_free);
 
-    g_string_chunk_free (parse_data->chunk);
-    g_free (parse_data);
+    g_string_chunk_free (chunk);
 }
 
 /** Converts raw file data using a new encoding. This function must be
@@ -440,31 +424,31 @@ void gnc_csv_parse_data_free (GncCsvParseData* parse_data)
  * @param error Will point to an error on failure
  * @return 0 on success, 1 on failure
  */
-int gnc_csv_convert_encoding (GncCsvParseData* parse_data, const char* encoding,
-                             GError** error)
+int GncCsvParseData::convert_encoding (const char* encoding,
+                                       GError** error)
 {
     gsize bytes_read, bytes_written;
 
-    /* If parse_data->file_str has already been initialized it must be
+    /* If file_str has already been initialized it must be
      * freed first. (This should always be the case, since
      * gnc_csv_load_file should always be called before this
      * function.) */
-    if (parse_data->file_str.begin != NULL)
-        g_free(parse_data->file_str.begin);
+    if (file_str.begin != NULL)
+        g_free(file_str.begin);
 
     /* Do the actual translation to UTF-8. */
-    parse_data->file_str.begin = g_convert (parse_data->raw_str.begin,
-                                           parse_data->raw_str.end - parse_data->raw_str.begin,
+    file_str.begin = g_convert (raw_str.begin,
+                                           raw_str.end - raw_str.begin,
                                            "UTF-8", encoding, &bytes_read, &bytes_written,
                                            error);
     /* Handle errors that occur. */
-    if (parse_data->file_str.begin == NULL)
+    if (file_str.begin == NULL)
         return 1;
 
     /* On success, save the ending pointer of the translated data and
      * the encoding type and return 0. */
-    parse_data->file_str.end = parse_data->file_str.begin + bytes_written;
-    parse_data->encoding = (gchar*)encoding;
+    file_str.end = file_str.begin + bytes_written;
+    encoding = (gchar*)encoding;
     return 0;
 }
 
@@ -480,40 +464,40 @@ int gnc_csv_convert_encoding (GncCsvParseData* parse_data, const char* encoding,
  * @param error Will contain an error if there is a failure
  * @return 0 on success, 1 on failure
  */
-int gnc_csv_load_file (GncCsvParseData* parse_data, const char* filename,
-                      GError** error)
+int GncCsvParseData::load_file (const char* filename,
+                                GError** error)
 {
     const char* guess_enc = NULL;
 
     /* Get the raw data first and handle an error if one occurs. */
-    parse_data->raw_mapping = g_mapped_file_new (filename, FALSE, NULL);
-    if (parse_data->raw_mapping == NULL)
+    raw_mapping = g_mapped_file_new (filename, FALSE, error);
+    if (raw_mapping == NULL)
     {
         /* TODO Handle file opening errors more specifically,
          * e.g. inexistent file versus no read permission. */
-        parse_data->raw_str.begin = NULL;
+        raw_str.begin = NULL;
         g_set_error (error, GNC_CSV_IMP_ERROR, GNC_CSV_IMP_ERROR_OPEN, "%s", _("File opening failed."));
         return 1;
     }
 
     /* Copy the mapping's contents into parse-data->raw_str. */
-    parse_data->raw_str.begin = g_mapped_file_get_contents (parse_data->raw_mapping);
-    parse_data->raw_str.end = parse_data->raw_str.begin + g_mapped_file_get_length (parse_data->raw_mapping);
+    raw_str.begin = g_mapped_file_get_contents (raw_mapping);
+    raw_str.end = raw_str.begin + g_mapped_file_get_length (raw_mapping);
 
     /* Make a guess at the encoding of the data. */
-    if (!g_mapped_file_get_length (parse_data->raw_mapping) == 0)
-        guess_enc = go_guess_encoding ((const char*)(parse_data->raw_str.begin),
-                                      (size_t)(parse_data->raw_str.end - parse_data->raw_str.begin),
+    if (!g_mapped_file_get_length (raw_mapping) == 0)
+        guess_enc = go_guess_encoding ((const char*)(raw_str.begin),
+                                      (size_t)(raw_str.end - raw_str.begin),
                                       "UTF-8", NULL);
     if (guess_enc == NULL)
     {
         g_set_error (error, GNC_CSV_IMP_ERROR, GNC_CSV_IMP_ERROR_ENCODING, "%s", _("Unknown encoding."));
         return 1;
     }
-    /* Convert using the guessed encoding into parse_data->file_str and
+    /* Convert using the guessed encoding into file_str and
      * handle any errors that occur. */
-    gnc_csv_convert_encoding (parse_data, guess_enc, error);
-    if (parse_data->file_str.begin == NULL)
+    convert_encoding (guess_enc, error);
+    if (file_str.begin == NULL)
     {
         g_set_error (error, GNC_CSV_IMP_ERROR, GNC_CSV_IMP_ERROR_ENCODING, "%s", _("Unknown encoding."));
         return 1;
@@ -523,7 +507,7 @@ int gnc_csv_load_file (GncCsvParseData* parse_data, const char* filename,
 }
 
 /** Parses a file into cells. This requires having an encoding that
- * works (see gnc_csv_convert_encoding). parse_data->options should be
+ * works (see gnc_csv_convert_encoding). options should be
  * set according to how the user wants before calling this
  * function. (Note: this function must be called with guessColTypes as
  * TRUE before it is ever called with it as FALSE.) (Note: if
@@ -534,76 +518,76 @@ int gnc_csv_load_file (GncCsvParseData* parse_data, const char* filename,
  * @param error Will contain an error if there is a failure
  * @return 0 on success, 1 on failure
  */
-int gnc_csv_parse (GncCsvParseData* parse_data, gboolean guessColTypes, GError** error)
+int GncCsvParseData::parse (gboolean guessColTypes, GError** error)
 {
     /* max_cols is the number of columns in the row with the most columns. */
     guint i, max_cols = 0;
 
-    if (parse_data->orig_lines != NULL)
+    if (orig_lines != NULL)
     {
-        stf_parse_general_free (parse_data->orig_lines);
+        stf_parse_general_free (orig_lines);
     }
 
     /* If everything is fine ... */
-    if (parse_data->file_str.begin != NULL)
+    if (file_str.begin != NULL)
     {
         /* Do the actual parsing. */
-        parse_data->orig_lines = stf_parse_general (parse_data->options, parse_data->chunk,
-                                 parse_data->file_str.begin,
-                                 parse_data->file_str.end);
+        orig_lines = stf_parse_general (options, chunk,
+                                 file_str.begin,
+                                 file_str.end);
     }
     /* If we couldn't get the encoding right, we just want an empty array. */
     else
     {
-        parse_data->orig_lines = g_ptr_array_new();
+        orig_lines = g_ptr_array_new();
     }
 
-    /* Record the original row lengths of parse_data->orig_lines. */
-    if (parse_data->orig_row_lengths != NULL)
-        g_array_free (parse_data->orig_row_lengths, FALSE);
+    /* Record the original row lengths of orig_lines. */
+    if (orig_row_lengths != NULL)
+        g_array_free (orig_row_lengths, FALSE);
 
-    parse_data->orig_row_lengths =
-        g_array_sized_new (FALSE, FALSE, sizeof(int), parse_data->orig_lines->len);
+    orig_row_lengths =
+        g_array_sized_new (FALSE, FALSE, sizeof(int), orig_lines->len);
 
-    g_array_set_size (parse_data->orig_row_lengths, parse_data->orig_lines->len);
-    parse_data->orig_max_row = 0;
-    for (i = 0; i < parse_data->orig_lines->len; i++)
+    g_array_set_size (orig_row_lengths, orig_lines->len);
+    orig_max_row = 0;
+    for (i = 0; i < orig_lines->len; i++)
     {
-        int length = ((GPtrArray*)parse_data->orig_lines->pdata[i])->len;
-        parse_data->orig_row_lengths->data[i] = length;
-        if (length > parse_data->orig_max_row)
-            parse_data->orig_max_row = length;
+        int length = ((GPtrArray*)orig_lines->pdata[i])->len;
+        orig_row_lengths->data[i] = length;
+        if (length > orig_max_row)
+            orig_max_row = length;
     }
 
     /* If it failed, generate an error. */
-    if (parse_data->orig_lines == NULL)
+    if (orig_lines == NULL)
     {
         g_set_error (error, GNC_CSV_IMP_ERROR, GNC_CSV_IMP_ERROR_PARSE, "Parsing failed.");
         return 1;
     }
 
     /* Now that we have data, let's set max_cols. */
-    for (i = 0; i < parse_data->orig_lines->len; i++)
+    for (i = 0; i < orig_lines->len; i++)
     {
-        if (max_cols < ((GPtrArray*)(parse_data->orig_lines->pdata[i]))->len)
-            max_cols = ((GPtrArray*)(parse_data->orig_lines->pdata[i]))->len;
+        if (max_cols < ((GPtrArray*)(orig_lines->pdata[i]))->len)
+            max_cols = ((GPtrArray*)(orig_lines->pdata[i]))->len;
     }
 
     if (guessColTypes)
     {
-        /* Free parse_data->column_types if it's already been created. */
-        if (parse_data->column_types != NULL)
-            g_array_free (parse_data->column_types, TRUE);
+        /* Free column_types if it's already been created. */
+        if (column_types != NULL)
+            g_array_free (column_types, TRUE);
 
-        /* Create parse_data->column_types and fill it with guesses based
+        /* Create column_types and fill it with guesses based
          * on the contents of each column. */
-        parse_data->column_types = g_array_sized_new (FALSE, FALSE, sizeof(int),
+        column_types = g_array_sized_new (FALSE, FALSE, sizeof(int),
                                    max_cols);
-        g_array_set_size (parse_data->column_types, max_cols);
+        g_array_set_size (column_types, max_cols);
         /* TODO Make it actually guess. */
-        for (i = 0; i < parse_data->column_types->len; i++)
+        for (i = 0; i < column_types->len; i++)
         {
-            parse_data->column_types->data[i] = GNC_CSV_NONE;
+            column_types->data[i] = GNC_CSV_NONE;
         }
     }
     else
@@ -611,13 +595,13 @@ int gnc_csv_parse (GncCsvParseData* parse_data, gboolean guessColTypes, GError**
         /* If we don't need to guess column types, we will simply set any
          * new columns that are created that didn't exist before to "None"
          * since we don't want gibberish to appear. Note:
-         * parse_data->column_types should have already been
+         * column_types should have already been
          * initialized, so we don't check for it being NULL. */
-        i = parse_data->column_types->len;
-        g_array_set_size (parse_data->column_types, max_cols);
-        for (; i < parse_data->column_types->len; i++)
+        i = column_types->len;
+        g_array_set_size (column_types, max_cols);
+        for (; i < column_types->len; i++)
         {
-            parse_data->column_types->data[i] = GNC_CSV_NONE;
+            column_types->data[i] = GNC_CSV_NONE;
         }
     }
     return 0;
@@ -1096,8 +1080,8 @@ static GncCsvTransLine* trans_property_list_to_trans (TransPropertyList* list, g
 }
 
 /** Creates a list of transactions from parsed data. Transactions that
- * could be created from rows are placed in parse_data->transactions;
- * rows that fail are placed in parse_data->error_lines. (Note: there
+ * could be created from rows are placed in transactions;
+ * rows that fail are placed in error_lines. (Note: there
  * is no way for this function to "fail," i.e. it only returns 0, so
  * it may be changed to a void function in the future.)
  * @param parse_data Data that is being parsed
@@ -1105,67 +1089,66 @@ static GncCsvTransLine* trans_property_list_to_trans (TransPropertyList* list, g
  * @param redo_errors TRUE to convert only error data, FALSE for all data
  * @return 0 on success, 1 on failure
  */
-int gnc_csv_parse_to_trans (GncCsvParseData* parse_data, Account* account,
-                           gboolean redo_errors)
+int GncCsvParseData::parse_to_trans (Account* account,
+                                     gboolean redo_errors)
 {
     gboolean hasBalanceColumn;
     guint i, j, max_cols = 0;
-    GArray* column_types = parse_data->column_types;
-    GList *error_lines = NULL, *begin_error_lines = NULL;
+    GList *error_lines_iter = NULL, *begin_error_lines = NULL;
     Account *home_account = NULL;
 
     /* last_transaction points to the last element in
-     * parse_data->transactions, or NULL if it's empty. */
+     * transactions, or NULL if it's empty. */
     GList* last_transaction = NULL;
 
-    /* Free parse_data->error_lines and parse_data->transactions if they
+    /* Free error_lines and transactions if they
      * already exist. */
     if (redo_errors) /* If we're redoing errors, we save freeing until the end. */
-        begin_error_lines = error_lines = parse_data->error_lines;
+        begin_error_lines = error_lines_iter = error_lines;
     else
     {
-        if (parse_data->error_lines != NULL)
-            g_list_free(parse_data->error_lines);
+        if (error_lines != NULL)
+            g_list_free(error_lines);
 
-        if (parse_data->transactions != NULL)
-            g_list_free (parse_data->transactions);
+        if (transactions != NULL)
+            g_list_free (transactions);
     }
-    parse_data->error_lines = NULL;
+    error_lines = NULL;
 
     if (redo_errors) /* If we're looking only at error data ... */
     {
-        if (parse_data->transactions == NULL)
+        if (transactions == NULL)
             last_transaction = NULL;
         else
         {
             /* Move last_transaction to the end. */
-            last_transaction = parse_data->transactions;
+            last_transaction = transactions;
             while (g_list_next (last_transaction) != NULL)
             {
                 last_transaction = g_list_next (last_transaction);
             }
         }
-        /* ... we use only the lines in error_lines. */
-        if (error_lines == NULL)
-            i = parse_data->orig_lines->len; /* Don't go into the for loop. */
+        /* ... we use only the lines in error_lines_iter. */
+        if (error_lines_iter == NULL)
+            i = orig_lines->len; /* Don't go into the for loop. */
         else
-            i = GPOINTER_TO_INT(error_lines->data);
+            i = GPOINTER_TO_INT(error_lines_iter->data);
     }
     else /* Otherwise, we look at all the data. */
     {
         /* The following while-loop effectively behaves like the following for-loop:
-         * for(i = 0; i < parse_data->orig_lines->len; i++). */
-        i = parse_data->start_row;
+         * for(i = 0; i < orig_lines->len; i++). */
+        i = start_row;
         last_transaction = NULL;
     }
 
-    /* set parse_data->end_row to number of lines */
-    if (parse_data->end_row > parse_data->orig_lines->len)
-        parse_data->end_row = parse_data->orig_lines->len;
+    /* set end_row to number of lines */
+    if (end_row > orig_lines->len)
+        end_row = orig_lines->len;
 
-    while (i < parse_data->end_row)
+    while (i < end_row)
     {
-        GPtrArray* line = (GPtrArray*) parse_data->orig_lines->pdata[i];
+        GPtrArray* line = (GPtrArray*) orig_lines->pdata[i];
         /* This flag is TRUE if there are any errors in this row. */
         gboolean errors = FALSE;
         gchar* error_message = NULL;
@@ -1194,7 +1177,7 @@ int gnc_csv_parse_to_trans (GncCsvParseData* parse_data, Account* account,
         }
         else
         {
-            list = trans_property_list_new (home_account, parse_data->date_format, parse_data->currency_format);
+            list = trans_property_list_new (home_account, date_format, currency_format);
 
             for (j = 0; j < line->len; j++)
             {
@@ -1219,7 +1202,7 @@ int gnc_csv_parse_to_trans (GncCsvParseData* parse_data, Account* account,
                 }
             }
 
-            /* If we had success, add the transaction to parse_data->transaction. */
+            /* If we had success, add the transaction to transaction. */
             if (!errors)
             {
                 trans_line = trans_property_list_to_trans (list, &error_message);
@@ -1228,13 +1211,13 @@ int gnc_csv_parse_to_trans (GncCsvParseData* parse_data, Account* account,
             trans_property_list_free (list);
         }
 
-        /* If there were errors, add this line to parse_data->error_lines. */
+        /* If there were errors, add this line to error_lines. */
         if (errors)
         {
-            parse_data->error_lines = g_list_append (parse_data->error_lines,
-                                                    GINT_TO_POINTER(i));
+            error_lines = g_list_append (error_lines,
+                                         GINT_TO_POINTER(i));
             /* If there's already an error message, we need to replace it. */
-            if (line->len > (guint)(parse_data->orig_row_lengths->data[i]))
+            if (line->len > (guint)(orig_row_lengths->data[i]))
             {
                 g_free(line->pdata[line->len - 1]);
                 line->pdata[line->len - 1] = error_message;
@@ -1259,10 +1242,10 @@ int gnc_csv_parse_to_trans (GncCsvParseData* parse_data, Account* account,
             if (last_transaction == NULL ||
                     xaccTransGetDate (((GncCsvTransLine*)(last_transaction->data))->trans) <= xaccTransGetDate (trans_line->trans))
             {
-                parse_data->transactions = g_list_append (parse_data->transactions, trans_line);
+                transactions = g_list_append (transactions, trans_line);
                 /* If this is the first transaction, we need to get last_transaction on track. */
                 if (last_transaction == NULL)
-                    last_transaction = parse_data->transactions;
+                    last_transaction = transactions;
                 else /* Otherwise, we can just continue. */
                     last_transaction = g_list_next (last_transaction);
             }
@@ -1278,11 +1261,11 @@ int gnc_csv_parse_to_trans (GncCsvParseData* parse_data, Account* account,
                 /* Move insertion_spot one location forward since we have to
                  * use the g_list_insert_before function. */
                 if (insertion_spot == NULL) /* We need to handle the case of inserting at the beginning of the list. */
-                    insertion_spot = parse_data->transactions;
+                    insertion_spot = transactions;
                 else
                     insertion_spot = g_list_next (insertion_spot);
 
-                parse_data->transactions = g_list_insert_before (parse_data->transactions, insertion_spot, trans_line);
+                transactions = g_list_insert_before (transactions, insertion_spot, trans_line);
             }
         }
 
@@ -1290,15 +1273,15 @@ int gnc_csv_parse_to_trans (GncCsvParseData* parse_data, Account* account,
         if (redo_errors)
         {
             /* Move to the next error line in the list. */
-            error_lines = g_list_next (error_lines);
-            if (error_lines == NULL)
-                i = parse_data->orig_lines->len; /* Don't continue the for loop. */
+            error_lines_iter = g_list_next (error_lines_iter);
+            if (error_lines_iter == NULL)
+                i = orig_lines->len; /* Don't continue the for loop. */
             else
-                i = GPOINTER_TO_INT(error_lines->data);
+                i = GPOINTER_TO_INT(error_lines_iter->data);
         }
         else
         {
-            if (parse_data->skip_rows == FALSE)
+            if (skip_rows == FALSE)
                 i++;
             else
                 i = i + 2;
@@ -1307,9 +1290,9 @@ int gnc_csv_parse_to_trans (GncCsvParseData* parse_data, Account* account,
 
     /* If we have a balance column, set the appropriate amounts on the transactions. */
     hasBalanceColumn = FALSE;
-    for (i = 0; i < parse_data->column_types->len; i++)
+    for (i = 0; i < column_types->len; i++)
     {
-        if (parse_data->column_types->data[i] == GNC_CSV_BALANCE)
+        if (column_types->data[i] == GNC_CSV_BALANCE)
         {
             hasBalanceColumn = TRUE;
             break;
@@ -1320,7 +1303,7 @@ int gnc_csv_parse_to_trans (GncCsvParseData* parse_data, Account* account,
     {
         Split      *split, *osplit;
         gnc_numeric balance_offset;
-        GList      *transactions = parse_data->transactions;
+        GList* tx_iter = transactions;
 
         if (account != NULL)
             home_account = account;
@@ -1331,10 +1314,9 @@ int gnc_csv_parse_to_trans (GncCsvParseData* parse_data, Account* account,
          * any given transaction. */
         balance_offset = double_to_gnc_numeric (0.0, xaccAccountGetCommoditySCU (home_account),
                                      GNC_HOW_RND_ROUND_HALF_UP);
-
-        while (transactions != NULL)
+        while (tx_iter != NULL)
         {
-            GncCsvTransLine* trans_line = (GncCsvTransLine*)transactions->data;
+            GncCsvTransLine* trans_line = (GncCsvTransLine*)tx_iter->data;
             if (trans_line->balance_set)
             {
                 time64 date = xaccTransGetDate (trans_line->trans);
@@ -1372,33 +1354,32 @@ int gnc_csv_parse_to_trans (GncCsvParseData* parse_data, Account* account,
                                                  xaccAccountGetCommoditySCU (home_account),
                                                  GNC_HOW_RND_ROUND_HALF_UP);
             }
-            transactions = g_list_next (transactions);
+            tx_iter = g_list_next (tx_iter);
         }
     }
 
     if (redo_errors) /* Now that we're at the end, we do the freeing. */
         g_list_free (begin_error_lines);
 
-    /* We need to resize parse_data->column_types since errors may have added columns. */
-    for (i = 0; i < parse_data->orig_lines->len; i++)
+    /* We need to resize column_types since errors may have added columns. */
+    for (i = 0; i < orig_lines->len; i++)
     {
-        if (max_cols < ((GPtrArray*)(parse_data->orig_lines->pdata[i]))->len)
-            max_cols = ((GPtrArray*)(parse_data->orig_lines->pdata[i]))->len;
+        if (max_cols < ((GPtrArray*)(orig_lines->pdata[i]))->len)
+            max_cols = ((GPtrArray*)(orig_lines->pdata[i]))->len;
     }
-    i = parse_data->column_types->len;
-    parse_data->column_types = g_array_set_size (parse_data->column_types, max_cols);
+    i = column_types->len;
+    column_types = g_array_set_size (column_types, max_cols);
     for (; i < max_cols; i++)
     {
-        parse_data->column_types->data[i] = GNC_CSV_NONE;
+        column_types->data[i] = GNC_CSV_NONE;
     }
     return 0;
 }
 
 
-gboolean
-gnc_csv_parse_check_for_column_type (GncCsvParseData* parse_data, gint type)
+bool
+GncCsvParseData::check_for_column_type (int type)
 {
-    GArray* column_types = parse_data->column_types;
     gboolean ret = FALSE;
     int j, ncols = column_types->len; /* ncols is the number of columns in the data. */
 
diff --git a/src/import-export/csv-imp/gnc-csv-imp-trans.hpp b/src/import-export/csv-imp/gnc-csv-imp-trans.hpp
index f620742..159c3a0 100644
--- a/src/import-export/csv-imp/gnc-csv-imp-trans.hpp
+++ b/src/import-export/csv-imp/gnc-csv-imp-trans.hpp
@@ -111,43 +111,43 @@ extern const gchar* date_format_user[];
 extern const gchar* gnc_csv_column_type_strs[];
 
 /** Struct containing data for parsing a CSV/Fixed-Width file. */
-typedef struct
+class GncCsvParseData
 {
+public:
+    // Constructor - Destructor
+    GncCsvParseData();
+    ~GncCsvParseData();
+
+    int load_file (const char* filename, GError** error);
+    int convert_encoding (const char* encoding, GError** error);
+
+    int parse (gboolean guessColTypes, GError** error);
+    int parse_to_trans (Account* account, gboolean redo_errors);
+    bool check_for_column_type (int type);
+
+
     const gchar* encoding;
-    GMappedFile* raw_mapping;   /**< The mapping containing raw_str */
-    GncCsvStr raw_str;          /**< Untouched data from the file as a string */
     GncCsvStr file_str;         /**< raw_str translated into UTF-8 */
     GPtrArray* orig_lines;      /**< file_str parsed into a two-dimensional array of strings */
-    GArray* orig_row_lengths;   /**< The lengths of rows in orig_lines
-                                      before error messages are appended */
     int orig_max_row;           /**< Holds the maximum value in orig_row_lengths */
-    GStringChunk* chunk;        /**< A chunk of memory in which the contents of orig_lines is stored */
+    GList* error_lines;         /**< List of row numbers in orig_lines that have errors */
     StfParseOptions_t* options; /**< Options controlling how file_str should be parsed */
     GArray* column_types;       /**< Array of values from the GncCsvColumnType enumeration */
-    GList* error_lines;         /**< List of row numbers in orig_lines that have errors */
     GList* transactions;        /**< List of GncCsvTransLine*s created using orig_lines and column_types */
     int date_format;            /**< The format of the text in the date columns from date_format_internal. */
     guint start_row;            /**< The start row to generate transactions from. */
     guint end_row;              /**< The end row to generate transactions from. */
     gboolean skip_rows;         /**< Skip Alternate Rows from start row. */
     int currency_format;        /**< The currency format, 0 for locale, 1 for comma dec and 2 for period */
-} GncCsvParseData;
 
-GncCsvParseData* gnc_csv_new_parse_data (void);
-
-void gnc_csv_parse_data_free (GncCsvParseData* parse_data);
-
-int gnc_csv_load_file (GncCsvParseData* parse_data, const char* filename,
-                      GError** error);
-
-int gnc_csv_convert_encoding (GncCsvParseData* parse_data, const char* encoding, GError** error);
-
-int gnc_csv_parse (GncCsvParseData* parse_data, gboolean guessColTypes, GError** error);
-
-int gnc_csv_parse_to_trans (GncCsvParseData* parse_data, Account* account, gboolean redo_errors);
+private:
+    GMappedFile* raw_mapping;   /**< The mapping containing raw_str */
+    GncCsvStr raw_str;          /**< Untouched data from the file as a string */
+    GArray* orig_row_lengths;   /**< The lengths of rows in orig_lines
+                                      before error messages are appended */
+    GStringChunk* chunk;        /**< A chunk of memory in which the contents of orig_lines is stored */
+};
 
 time64 parse_date (const char* date_str, int format);
 
-gboolean gnc_csv_parse_check_for_column_type (GncCsvParseData* parse_data, gint type);
-
 #endif

commit 3a0f0dff8eb9757fb605ce9b52655a7a03b69db3
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sat Jan 23 15:53:11 2016 +0100

    csv-imp - copy gnc-csv-model and make it build as c++
    
    This will be the start of a GncCsvImpTrans class that will
    manage transaction imports from csv files

diff --git a/po/POTFILES.in b/po/POTFILES.in
index 7ad6b3d..7525756 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -435,6 +435,7 @@ src/import-export/csv-imp/csv-account-import.c
 src/import-export/csv-imp/csv-fixed-trans-import.c
 src/import-export/csv-imp/gnc-csv-account-map.c
 src/import-export/csv-imp/gnc-csv-gnumeric-popup.c
+src/import-export/csv-imp/gnc-csv-imp-trans.cpp
 src/import-export/csv-imp/gnc-csv-model.c
 src/import-export/csv-imp/gnc-csv-tokenizer.cpp
 src/import-export/csv-imp/gnc-csv-trans-settings.c
diff --git a/src/app-utils/gnc-ui-util.c b/src/app-utils/gnc-ui-util.c
index 3b988e6..eb0db3d 100644
--- a/src/app-utils/gnc-ui-util.c
+++ b/src/app-utils/gnc-ui-util.c
@@ -1960,7 +1960,7 @@ A: Because scanf and printf use different symbols for 64-bit numbers.
 gboolean
 xaccParseAmountExtended (const char * in_str, gboolean monetary,
                          gunichar negative_sign, gunichar decimal_point,
-                         gunichar group_separator, char *group, char *ignore_list,
+                         gunichar group_separator, const char *group, const char *ignore_list,
                          gnc_numeric *result, char **endstr)
 {
     gboolean is_negative;
diff --git a/src/app-utils/gnc-ui-util.h b/src/app-utils/gnc-ui-util.h
index 4f61609..a598641 100644
--- a/src/app-utils/gnc-ui-util.h
+++ b/src/app-utils/gnc-ui-util.h
@@ -322,7 +322,7 @@ gboolean xaccParseAmount (const char * in_str, gboolean monetary,
 gboolean
 xaccParseAmountExtended (const char * in_str, gboolean monetary,
                          gunichar negative_sign, gunichar decimal_point,
-                         gunichar group_separator, char *group, char *ignore_list,
+                         gunichar group_separator, const char *group, const char *ignore_list,
                          gnc_numeric *result, char **endstr);
 
 /* Initialization ***************************************************/
diff --git a/src/import-export/csv-imp/CMakeLists.txt b/src/import-export/csv-imp/CMakeLists.txt
index b2b9bb2..026b4bb 100644
--- a/src/import-export/csv-imp/CMakeLists.txt
+++ b/src/import-export/csv-imp/CMakeLists.txt
@@ -12,6 +12,7 @@ SET(csv_import_SOURCES
   gnc-csv-account-map.c
   gnc-csv-model.c
   gnc-csv-gnumeric-popup.c
+  gnc-csv-imp-trans.cpp
   gnc-csv-tokenizer.cpp
   gnc-csv-trans-settings.c
   gnc-dummy-tokenizer.cpp
@@ -36,6 +37,7 @@ SET(csv_import_noinst_HEADERS
   gnc-csv-account-map.h
   gnc-csv-model.h
   gnc-csv-gnumeric-popup.h
+  gnc-csv-imp-trans.hpp
   gnc-csv-tokenizer.hpp
   gnc-csv-trans-settings.h
   gnc-dummy-tokenizer.hpp
diff --git a/src/import-export/csv-imp/Makefile.am b/src/import-export/csv-imp/Makefile.am
index 13c579f..53c3c42 100644
--- a/src/import-export/csv-imp/Makefile.am
+++ b/src/import-export/csv-imp/Makefile.am
@@ -12,6 +12,7 @@ libgncmod_csv_import_la_SOURCES = \
   csv-fixed-trans-import.c \
   gnc-csv-account-map.c \
   gnc-csv-model.c \
+  gnc-csv-imp-trans.cpp \
   gnc-csv-tokenizer.cpp \
   gnc-csv-gnumeric-popup.c \
   gnc-dummy-tokenizer.cpp \
@@ -28,6 +29,7 @@ noinst_HEADERS = \
   csv-fixed-trans-import.h \
   gnc-csv-account-map.h \
   gnc-csv-model.h \
+  gnc-csv-imp-trans.hpp \
   gnc-csv-tokenizer.hpp \
   gnc-csv-gnumeric-popup.h \
   gnc-dummy-tokenizer.hpp \
diff --git a/src/import-export/csv-imp/gnc-csv-imp-trans.cpp b/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
new file mode 100644
index 0000000..ba74c0c
--- /dev/null
+++ b/src/import-export/csv-imp/gnc-csv-imp-trans.cpp
@@ -0,0 +1,1411 @@
+/********************************************************************\
+ * gnc-csv-imp-trans.cpp - import transactions from csv files       *
+ *                                                                  *
+ * 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 2 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, contact:                        *
+ *                                                                  *
+ * Free Software Foundation           Voice:  +1-617-542-5942       *
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
+ *                                                                  *
+\********************************************************************/
+
+extern "C" {
+#include <platform.h>
+#if PLATFORM(WINDOWS)
+#include <windows.h>
+#endif
+
+#include <glib/gi18n.h>
+#include <goffice/go-glib-extras.h>
+
+#include "gnc-csv-account-map.h"
+#include "gnc-ui-util.h"
+#include "engine-helpers.h"
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <regex.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <math.h>
+}
+
+#include "gnc-csv-imp-trans.hpp"
+
+GQuark
+gnc_csv_imp_error_quark (void)
+{
+  return g_quark_from_static_string ("g-csv-imp-error-quark");
+}
+
+G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT;
+
+//const int num_date_formats = 5;
+//const gchar* date_format_user[] = {N_("y-m-d"),
+//                                   N_("d-m-y"),
+//                                   N_("m-d-y"),
+//                                   N_("d-m"),
+//                                   N_("m-d")
+//                                  };
+//
+//const int num_currency_formats = 3;
+//const gchar* currency_format_user[] = {N_("Locale"),
+//                                       N_("Period: 123,456.78"),
+//                                       N_("Comma: 123.456,78")
+//                                      };
+//
+///* This array contains all of the different strings for different column types. */
+//const gchar* gnc_csv_column_type_strs[GNC_CSV_NUM_COL_TYPES] = {
+//        N_("None"),
+//        N_("Date"),
+//        N_("Num"),
+//        N_("Description"),
+//        N_("Notes"),
+//        N_("Account"),
+//        N_("Deposit"),
+//        N_("Withdrawal"),
+//        N_("Balance"),
+//        N_("Memo"),
+//        N_("Other Account"),
+//        N_("Other Memo")
+//};
+
+/** A set of sensible defaults for parsing CSV files.
+ * @return StfParseOptions_t* for parsing a file with comma separators
+ */
+static StfParseOptions_t* default_parse_options (void)
+{
+    StfParseOptions_t* options = stf_parse_options_new();
+    stf_parse_options_set_type (options, PARSE_TYPE_CSV);
+    stf_parse_options_csv_set_separators (options, ",", NULL);
+    return options;
+}
+
+/** Parses a string into a date, given a format. The format must
+ * include the year. This function should only be called by
+ * parse_date.
+ * @param date_str The string containing a date being parsed
+ * @param format An index specifying a format in date_format_user
+ * @return The parsed value of date_str on success or -1 on failure
+ */
+static time64 parse_date_with_year (const char* date_str, int format)
+{
+    time64 rawtime; /* The integer time */
+    struct tm retvalue, test_retvalue; /* The time in a broken-down structure */
+
+    int i, j, mem_length, orig_year = -1, orig_month = -1, orig_day = -1;
+
+    /* Buffer for containing individual parts (e.g. year, month, day) of a date */
+    char date_segment[5];
+
+    /* The compiled regular expression */
+    regex_t preg = {0};
+
+    /* An array containing indices specifying the matched substrings in date_str */
+    regmatch_t pmatch[4] = { {0}, {0}, {0}, {0} };
+
+    /* The regular expression for parsing dates */
+    const char* regex = "^ *([0-9]+) *[-/.'] *([0-9]+) *[-/.'] *([0-9]+).*$|^ *([0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]).*$";
+
+    /* We get our matches using the regular expression. */
+    regcomp (&preg, regex, REG_EXTENDED);
+    regexec (&preg, date_str, 4, pmatch, 0);
+    regfree (&preg);
+
+    /* If there wasn't a match, there was an error. */
+    if (pmatch[0].rm_eo == 0)
+        return -1;
+
+    /* If this is a string without separators ... */
+    if (pmatch[1].rm_so == -1)
+    {
+        /* ... we will fill in the indices based on the user's selection. */
+        int k = 0; /* k traverses date_str by keeping track of where separators "should" be. */
+        j = 1; /* j traverses pmatch. */
+        for (i = 0; date_format_user[format][i]; i++)
+        {
+            char segment_type = date_format_user[format][i];
+            /* Only do something if this is a meaningful character */
+            if (segment_type == 'y' || segment_type == 'm' || segment_type == 'd')
+            {
+                pmatch[j].rm_so = k;
+                switch (segment_type)
+                {
+                case 'm':
+                case 'd':
+                    k += 2;
+                    break;
+
+                case 'y':
+                    k += 4;
+                    break;
+                }
+
+                pmatch[j].rm_eo = k;
+                j++;
+            }
+        }
+    }
+
+    /* Put some sane values in retvalue by using a fixed time for
+     * the non-year-month-day parts of the date. */
+    gnc_time (&rawtime);
+    gnc_localtime_r (&rawtime, &retvalue);
+    retvalue.tm_hour = 11;
+    retvalue.tm_min = 0;
+    retvalue.tm_sec = 0;
+    retvalue.tm_isdst = -1;
+
+    /* j traverses pmatch (index 0 contains the entire string, so we
+     * start at index 1 for the first meaningful match). */
+    j = 1;
+    /* Go through the date format and interpret the matches in order of
+     * the sections in the date format. */
+    for (i = 0; date_format_user[format][i]; i++)
+    {
+        char segment_type = date_format_user[format][i];
+        /* Only do something if this is a meaningful character */
+        if (segment_type == 'y' || segment_type == 'm' || segment_type == 'd')
+        {
+            /* Copy the matching substring into date_segment so that we can
+             * convert it into an integer. */
+            mem_length = pmatch[j].rm_eo - pmatch[j].rm_so;
+            memcpy (date_segment, date_str + pmatch[j].rm_so, mem_length);
+            date_segment[mem_length] = '\0';
+
+            /* Set the appropriate member of retvalue. Save the original
+             * values so that we can check if they change when we use gnc_mktime
+             * below. */
+            switch (segment_type)
+            {
+            case 'y':
+                retvalue.tm_year = atoi (date_segment);
+
+                /* Handle two-digit years. */
+                if (retvalue.tm_year < 100)
+                {
+                    /* We allow two-digit years in the range 1969 - 2068. */
+                    if (retvalue.tm_year < 69)
+                        retvalue.tm_year += 100;
+                }
+                else
+                    retvalue.tm_year -= 1900;
+                orig_year = retvalue.tm_year;
+                break;
+
+            case 'm':
+                orig_month = retvalue.tm_mon = atoi (date_segment) - 1;
+                break;
+
+            case 'd':
+                orig_day = retvalue.tm_mday = atoi (date_segment);
+                break;
+            }
+            j++;
+        }
+    }
+    /* Convert back to an integer. If gnc_mktime leaves retvalue unchanged,
+     * everything is okay; otherwise, an error has occurred. */
+    /* We have to use a "test" date value to account for changes in
+     * daylight savings time, which can cause a date change with gnc_mktime
+     * near midnight, causing the code to incorrectly think a date is
+     * incorrect. */
+    test_retvalue = retvalue;
+    gnc_mktime (&test_retvalue);
+    retvalue.tm_isdst = test_retvalue.tm_isdst;
+    rawtime = gnc_mktime (&retvalue);
+    if (retvalue.tm_mday == orig_day &&
+            retvalue.tm_mon == orig_month &&
+            retvalue.tm_year == orig_year)
+    {
+        return rawtime;
+    }
+    else
+    {
+        return -1;
+    }
+}
+
+/** Parses a string into a date, given a format. The format cannot
+ * include the year. This function should only be called by
+ * parse_date.
+ * @param date_str The string containing a date being parsed
+ * @param format An index specifying a format in date_format_user
+ * @return The parsed value of date_str on success or -1 on failure
+ */
+static time64 parse_date_without_year (const char* date_str, int format)
+{
+    time64 rawtime; /* The integer time */
+    struct tm retvalue, test_retvalue; /* The time in a broken-down structure */
+
+    int i, j, mem_length, orig_year = -1, orig_month = -1, orig_day = -1;
+
+    /* Buffer for containing individual parts (e.g. year, month, day) of a date */
+    gchar* date_segment;
+
+    /* The compiled regular expression */
+    regex_t preg = {0};
+
+    /* An array containing indices specifying the matched substrings in date_str */
+    regmatch_t pmatch[3] = { {0}, {0}, {0} };
+
+    /* The regular expression for parsing dates */
+    const char* regex = "^ *([0-9]+) *[-/.'] *([0-9]+).*$";
+
+    /* We get our matches using the regular expression. */
+    regcomp (&preg, regex, REG_EXTENDED);
+    regexec (&preg, date_str, 3, pmatch, 0);
+    regfree (&preg);
+
+    /* If there wasn't a match, there was an error. */
+    if (pmatch[0].rm_eo == 0)
+        return -1;
+
+    /* Put some sane values in retvalue by using a fixed time for
+     * the non-year-month-day parts of the date. */
+    gnc_time (&rawtime);
+    gnc_localtime_r (&rawtime, &retvalue);
+    retvalue.tm_hour = 11;
+    retvalue.tm_min = 0;
+    retvalue.tm_sec = 0;
+    retvalue.tm_isdst = -1;
+    orig_year = retvalue.tm_year;
+
+    /* j traverses pmatch (index 0 contains the entire string, so we
+     * start at index 1 for the first meaningful match). */
+    j = 1;
+    /* Go through the date format and interpret the matches in order of
+     * the sections in the date format. */
+    for (i = 0; date_format_user[format][i]; i++)
+    {
+        char segment_type = date_format_user[format][i];
+        /* Only do something if this is a meaningful character */
+        if (segment_type == 'm' || segment_type == 'd')
+        {
+            /* Copy the matching substring into date_segment so that we can
+             * convert it into an integer. */
+            mem_length = pmatch[j].rm_eo - pmatch[j].rm_so;
+            date_segment = g_new (gchar, mem_length);
+            memcpy(date_segment, date_str + pmatch[j].rm_so, mem_length);
+            date_segment[mem_length] = '\0';
+
+            /* Set the appropriate member of retvalue. Save the original
+             * values so that we can check if they change when we use gnc_mktime
+             * below. */
+            switch (segment_type)
+            {
+            case 'm':
+                orig_month = retvalue.tm_mon = atoi (date_segment) - 1;
+                break;
+
+            case 'd':
+                orig_day = retvalue.tm_mday = atoi (date_segment);
+                break;
+            }
+            g_free (date_segment);
+            j++;
+        }
+    }
+    /* Convert back to an integer. If gnc_mktime leaves retvalue unchanged,
+     * everything is okay; otherwise, an error has occurred. */
+    /* We have to use a "test" date value to account for changes in
+     * daylight savings time, which can cause a date change with gnc_mktime
+     * near midnight, causing the code to incorrectly think a date is
+     * incorrect. */
+    test_retvalue = retvalue;
+    gnc_mktime (&test_retvalue);
+    retvalue.tm_isdst = test_retvalue.tm_isdst;
+    rawtime = gnc_mktime (&retvalue);
+    if (retvalue.tm_mday == orig_day &&
+            retvalue.tm_mon == orig_month &&
+            retvalue.tm_year == orig_year)
+    {
+        return rawtime;
+    }
+    else
+    {
+        return -1;
+    }
+}
+
+/** Parses a string into a date, given a format. This function
+ * requires only knowing the order in which the year, month and day
+ * appear. For example, 01-02-2003 will be parsed the same way as
+ * 01/02/2003.
+ * @param date_str The string containing a date being parsed
+ * @param format An index specifying a format in date_format_user
+ * @return The parsed value of date_str on success or -1 on failure
+ */
+time64 parse_date (const char* date_str, int format)
+{
+    if (strchr (date_format_user[format], 'y'))
+        return parse_date_with_year (date_str, format);
+    else
+        return parse_date_without_year (date_str, format);
+}
+
+/** Constructor for GncCsvParseData.
+ * @return Pointer to a new GncCSvParseData
+ */
+GncCsvParseData* gnc_csv_new_parse_data (void)
+{
+    GncCsvParseData* parse_data = g_new(GncCsvParseData, 1);
+    parse_data->encoding = "UTF-8";
+    /* All of the data pointers are initially NULL. This is so that, if
+     * gnc_csv_parse_data_free is called before all of the data is
+     * initialized, only the data that needs to be freed is freed. */
+    parse_data->raw_mapping = NULL;
+    parse_data->raw_str.begin = parse_data->raw_str.end
+                                = parse_data->file_str.begin = parse_data->file_str.end = NULL;
+    parse_data->orig_lines = NULL;
+    parse_data->orig_row_lengths = NULL;
+    parse_data->column_types = NULL;
+    parse_data->error_lines = parse_data->transactions = NULL;
+    parse_data->options = default_parse_options();
+    parse_data->date_format = -1;
+    parse_data->currency_format = 0;
+    parse_data->chunk = g_string_chunk_new(100 * 1024);
+    parse_data->start_row = 0;
+    parse_data->end_row = 1000;
+    parse_data->skip_rows = FALSE;
+    return parse_data;
+}
+
+/** Destructor for GncCsvParseData.
+ * @param parse_data Parse data whose memory will be freed
+ */
+void gnc_csv_parse_data_free (GncCsvParseData* parse_data)
+{
+    /* All non-NULL pointers have been initialized and must be freed. */
+
+    if (parse_data->raw_mapping != NULL)
+    {
+        g_mapped_file_unref (parse_data->raw_mapping);
+    }
+
+    if (parse_data->file_str.begin != NULL)
+        g_free (parse_data->file_str.begin);
+
+    if (parse_data->orig_lines != NULL)
+        stf_parse_general_free (parse_data->orig_lines);
+
+    if (parse_data->orig_row_lengths != NULL)
+        g_array_free (parse_data->orig_row_lengths, FALSE);
+
+    if (parse_data->options != NULL)
+        stf_parse_options_free (parse_data->options);
+
+    if (parse_data->column_types != NULL)
+        g_array_free (parse_data->column_types, TRUE);
+
+    if (parse_data->error_lines != NULL)
+        g_list_free (parse_data->error_lines);
+
+    if (parse_data->transactions != NULL)
+    {
+        GList* transactions = parse_data->transactions;
+        /* We have to free the GncCsvTransLine's that are at each node in
+         * the list before freeing the entire list. */
+        do
+        {
+            g_free (transactions->data);
+            transactions = g_list_next (transactions);
+        }
+        while (transactions != NULL);
+        g_list_free (parse_data->transactions);
+    }
+
+    g_string_chunk_free (parse_data->chunk);
+    g_free (parse_data);
+}
+
+/** Converts raw file data using a new encoding. This function must be
+ * called after gnc_csv_load_file only if gnc_csv_load_file guessed
+ * the wrong encoding.
+ * @param parse_data Data that is being parsed
+ * @param encoding Encoding that data should be translated using
+ * @param error Will point to an error on failure
+ * @return 0 on success, 1 on failure
+ */
+int gnc_csv_convert_encoding (GncCsvParseData* parse_data, const char* encoding,
+                             GError** error)
+{
+    gsize bytes_read, bytes_written;
+
+    /* If parse_data->file_str has already been initialized it must be
+     * freed first. (This should always be the case, since
+     * gnc_csv_load_file should always be called before this
+     * function.) */
+    if (parse_data->file_str.begin != NULL)
+        g_free(parse_data->file_str.begin);
+
+    /* Do the actual translation to UTF-8. */
+    parse_data->file_str.begin = g_convert (parse_data->raw_str.begin,
+                                           parse_data->raw_str.end - parse_data->raw_str.begin,
+                                           "UTF-8", encoding, &bytes_read, &bytes_written,
+                                           error);
+    /* Handle errors that occur. */
+    if (parse_data->file_str.begin == NULL)
+        return 1;
+
+    /* On success, save the ending pointer of the translated data and
+     * the encoding type and return 0. */
+    parse_data->file_str.end = parse_data->file_str.begin + bytes_written;
+    parse_data->encoding = (gchar*)encoding;
+    return 0;
+}
+
+/** Loads a file into a GncCsvParseData. This is the first function
+ * that must be called after creating a new GncCsvParseData. If this
+ * fails because the file couldn't be opened, no more functions can be
+ * called on the parse data until this succeeds (or until it fails
+ * because of an encoding guess error). If it fails because the
+ * encoding could not be guessed, gnc_csv_convert_encoding must be
+ * called until it succeeds.
+ * @param parse_data Data that is being parsed
+ * @param filename Name of the file that should be opened
+ * @param error Will contain an error if there is a failure
+ * @return 0 on success, 1 on failure
+ */
+int gnc_csv_load_file (GncCsvParseData* parse_data, const char* filename,
+                      GError** error)
+{
+    const char* guess_enc = NULL;
+
+    /* Get the raw data first and handle an error if one occurs. */
+    parse_data->raw_mapping = g_mapped_file_new (filename, FALSE, NULL);
+    if (parse_data->raw_mapping == NULL)
+    {
+        /* TODO Handle file opening errors more specifically,
+         * e.g. inexistent file versus no read permission. */
+        parse_data->raw_str.begin = NULL;
+        g_set_error (error, GNC_CSV_IMP_ERROR, GNC_CSV_IMP_ERROR_OPEN, "%s", _("File opening failed."));
+        return 1;
+    }
+
+    /* Copy the mapping's contents into parse-data->raw_str. */
+    parse_data->raw_str.begin = g_mapped_file_get_contents (parse_data->raw_mapping);
+    parse_data->raw_str.end = parse_data->raw_str.begin + g_mapped_file_get_length (parse_data->raw_mapping);
+
+    /* Make a guess at the encoding of the data. */
+    if (!g_mapped_file_get_length (parse_data->raw_mapping) == 0)
+        guess_enc = go_guess_encoding ((const char*)(parse_data->raw_str.begin),
+                                      (size_t)(parse_data->raw_str.end - parse_data->raw_str.begin),
+                                      "UTF-8", NULL);
+    if (guess_enc == NULL)
+    {
+        g_set_error (error, GNC_CSV_IMP_ERROR, GNC_CSV_IMP_ERROR_ENCODING, "%s", _("Unknown encoding."));
+        return 1;
+    }
+    /* Convert using the guessed encoding into parse_data->file_str and
+     * handle any errors that occur. */
+    gnc_csv_convert_encoding (parse_data, guess_enc, error);
+    if (parse_data->file_str.begin == NULL)
+    {
+        g_set_error (error, GNC_CSV_IMP_ERROR, GNC_CSV_IMP_ERROR_ENCODING, "%s", _("Unknown encoding."));
+        return 1;
+    }
+    else
+        return 0;
+}
+
+/** Parses a file into cells. This requires having an encoding that
+ * works (see gnc_csv_convert_encoding). parse_data->options should be
+ * set according to how the user wants before calling this
+ * function. (Note: this function must be called with guessColTypes as
+ * TRUE before it is ever called with it as FALSE.) (Note: if
+ * guessColTypes is TRUE, all the column types will be GNC_CSV_NONE
+ * right now.)
+ * @param parse_data Data that is being parsed
+ * @param guessColTypes TRUE to guess what the types of columns are based on the cell contents
+ * @param error Will contain an error if there is a failure
+ * @return 0 on success, 1 on failure
+ */
+int gnc_csv_parse (GncCsvParseData* parse_data, gboolean guessColTypes, GError** error)
+{
+    /* max_cols is the number of columns in the row with the most columns. */
+    guint i, max_cols = 0;
+
+    if (parse_data->orig_lines != NULL)
+    {
+        stf_parse_general_free (parse_data->orig_lines);
+    }
+
+    /* If everything is fine ... */
+    if (parse_data->file_str.begin != NULL)
+    {
+        /* Do the actual parsing. */
+        parse_data->orig_lines = stf_parse_general (parse_data->options, parse_data->chunk,
+                                 parse_data->file_str.begin,
+                                 parse_data->file_str.end);
+    }
+    /* If we couldn't get the encoding right, we just want an empty array. */
+    else
+    {
+        parse_data->orig_lines = g_ptr_array_new();
+    }
+
+    /* Record the original row lengths of parse_data->orig_lines. */
+    if (parse_data->orig_row_lengths != NULL)
+        g_array_free (parse_data->orig_row_lengths, FALSE);
+
+    parse_data->orig_row_lengths =
+        g_array_sized_new (FALSE, FALSE, sizeof(int), parse_data->orig_lines->len);
+
+    g_array_set_size (parse_data->orig_row_lengths, parse_data->orig_lines->len);
+    parse_data->orig_max_row = 0;
+    for (i = 0; i < parse_data->orig_lines->len; i++)
+    {
+        int length = ((GPtrArray*)parse_data->orig_lines->pdata[i])->len;
+        parse_data->orig_row_lengths->data[i] = length;
+        if (length > parse_data->orig_max_row)
+            parse_data->orig_max_row = length;
+    }
+
+    /* If it failed, generate an error. */
+    if (parse_data->orig_lines == NULL)
+    {
+        g_set_error (error, GNC_CSV_IMP_ERROR, GNC_CSV_IMP_ERROR_PARSE, "Parsing failed.");
+        return 1;
+    }
+
+    /* Now that we have data, let's set max_cols. */
+    for (i = 0; i < parse_data->orig_lines->len; i++)
+    {
+        if (max_cols < ((GPtrArray*)(parse_data->orig_lines->pdata[i]))->len)
+            max_cols = ((GPtrArray*)(parse_data->orig_lines->pdata[i]))->len;
+    }
+
+    if (guessColTypes)
+    {
+        /* Free parse_data->column_types if it's already been created. */
+        if (parse_data->column_types != NULL)
+            g_array_free (parse_data->column_types, TRUE);
+
+        /* Create parse_data->column_types and fill it with guesses based
+         * on the contents of each column. */
+        parse_data->column_types = g_array_sized_new (FALSE, FALSE, sizeof(int),
+                                   max_cols);
+        g_array_set_size (parse_data->column_types, max_cols);
+        /* TODO Make it actually guess. */
+        for (i = 0; i < parse_data->column_types->len; i++)
+        {
+            parse_data->column_types->data[i] = GNC_CSV_NONE;
+        }
+    }
+    else
+    {
+        /* If we don't need to guess column types, we will simply set any
+         * new columns that are created that didn't exist before to "None"
+         * since we don't want gibberish to appear. Note:
+         * parse_data->column_types should have already been
+         * initialized, so we don't check for it being NULL. */
+        i = parse_data->column_types->len;
+        g_array_set_size (parse_data->column_types, max_cols);
+        for (; i < parse_data->column_types->len; i++)
+        {
+            parse_data->column_types->data[i] = GNC_CSV_NONE;
+        }
+    }
+    return 0;
+}
+
+/** A struct containing TransProperties that all describe a single transaction. */
+typedef struct
+{
+    int date_format; /**< The format for parsing dates */
+    int currency_format; /**< The format for currency */
+    Account* account; /**< The account the transaction belongs to */
+    GList* properties; /**< List of TransProperties */
+} TransPropertyList;
+
+/** A struct encapsulating a property of a transaction. */
+typedef struct
+{
+    int type;                /**< A value from the GncCsvColumnType enum except
+                               * GNC_CSV_NONE and GNC_CSV_NUM_COL_TYPES */
+    void* value;             /**< Pointer to the data that will be used to configure a transaction */
+    TransPropertyList* list; /**< The list the property belongs to */
+} TransProperty;
+
+/** Constructor for TransProperty.
+ * @param type The type of the new property (see TransProperty.type for possible values)
+ */
+static TransProperty* trans_property_new (int type, TransPropertyList* list)
+{
+    TransProperty* prop = g_new (TransProperty, 1);
+    prop->type = type;
+    prop->list = list;
+    prop->value = NULL;
+    return prop;
+}
+
+/** Destructor for TransProperty.
+ * @param prop The property to be freed
+ */
+static void trans_property_free (TransProperty* prop)
+{
+    switch (prop->type)
+    {
+        /* The types for "Date" and "Balance" (time64 and gnc_numeric,
+         * respectively) are typically not pointed to, we have to free
+         * them, unlike types like char* ("Description"). */
+    case GNC_CSV_DATE:
+    case GNC_CSV_BALANCE:
+    case GNC_CSV_DEPOSIT:
+    case GNC_CSV_WITHDRAWAL:
+        if (prop->value != NULL)
+            g_free(prop->value);
+        break;
+    default:
+       break;
+    }
+    g_free (prop);
+}
+
+/** Sets the value of the property by parsing str. Note: this should
+ * only be called once on an instance of TransProperty, as calling it
+ * more than once can cause memory leaks.
+ * @param prop The property being set
+ * @param str The string to be parsed
+ * @return TRUE on success, FALSE on failure
+ */
+static gboolean trans_property_set (TransProperty* prop, char* str)
+{
+    char *endptr, *possible_currency_symbol, *str_dupe;
+    gnc_numeric val;
+    int reti;
+    regex_t regex;
+    switch (prop->type)
+    {
+    case GNC_CSV_DATE:
+        prop->value = g_new(time64, 1);
+        *((time64*)(prop->value)) = parse_date(str, prop->list->date_format);
+        return *((time64*)(prop->value)) != -1;
+
+    case GNC_CSV_DESCRIPTION:
+    case GNC_CSV_NOTES:
+    case GNC_CSV_MEMO:
+    case GNC_CSV_OMEMO:
+    case GNC_CSV_NUM:
+        prop->value = g_strdup (str);
+        return TRUE;
+
+    case GNC_CSV_OACCOUNT:
+        prop->value = gnc_csv_account_map_search (str);
+        return TRUE;
+
+    case GNC_CSV_BALANCE:
+    case GNC_CSV_DEPOSIT:
+    case GNC_CSV_WITHDRAWAL:
+        str_dupe = g_strdup (str); /* First, we make a copy so we can't mess up real data. */
+        /* If a cell is empty or just spaces make its value = "0" */
+        reti = regcomp(&regex, "[0-9]", 0);
+        reti = regexec(&regex, str_dupe, 0, NULL, 0);
+        if (reti == REG_NOMATCH)
+        {
+            g_free (str_dupe);
+            str_dupe = g_strdup ("0");
+        }
+        /* Go through str_dupe looking for currency symbols. */
+        for (possible_currency_symbol = str_dupe; *possible_currency_symbol;
+                possible_currency_symbol = g_utf8_next_char (possible_currency_symbol))
+        {
+            if (g_unichar_type (g_utf8_get_char (possible_currency_symbol)) == G_UNICODE_CURRENCY_SYMBOL)
+            {
+                /* If we find a currency symbol, save the position just ahead
+                 * of the currency symbol (next_symbol), and find the null
+                 * terminator of the string (last_symbol). */
+                char *next_symbol = g_utf8_next_char (possible_currency_symbol), *last_symbol = next_symbol;
+                while (*last_symbol)
+                    last_symbol = g_utf8_next_char (last_symbol);
+
+                /* Move all of the string (including the null byte, which is
+                 * why we have +1 in the size parameter) following the
+                 * currency symbol back one character, thereby overwriting the
+                 * currency symbol. */
+                memmove (possible_currency_symbol, next_symbol, last_symbol - next_symbol + 1);
+                break;
+            }
+        }
+
+        /* Currency format */
+        switch (prop->list->currency_format)
+        {
+        case 0:
+            /* Currency locale */
+            if (!(xaccParseAmount (str_dupe, TRUE, &val, &endptr)))
+            {
+                g_free (str_dupe);
+                return FALSE;
+            }
+            break;
+        case 1:
+            /* Currency decimal period */
+            if (!(xaccParseAmountExtended (str_dupe, TRUE, '-', '.', ',', "\003\003", "$+", &val, &endptr)))
+            {
+                g_free (str_dupe);
+                return FALSE;
+            }
+            break;
+        case 2:
+            /* Currency decimal comma */
+            if (!(xaccParseAmountExtended (str_dupe, TRUE, '-', ',', '.', "\003\003", "$+", &val, &endptr)))
+            {
+                g_free (str_dupe);
+                return FALSE;
+            }
+            break;
+        }
+
+        prop->value = g_new (gnc_numeric, 1);
+        *((gnc_numeric*)(prop->value)) = val;
+        g_free (str_dupe);
+        return TRUE;
+
+    default:
+        break;
+    }
+    return FALSE; /* We should never actually get here. */
+}
+
+/** Constructor for TransPropertyList.
+ * @param account The account with which transactions should be built
+ * @param date_format An index from date_format_user for how date properties should be parsed
+ * @return A pointer to a new TransPropertyList
+ */
+static TransPropertyList* trans_property_list_new (Account* account, int date_format, int currency_format)
+{
+    TransPropertyList* list = g_new (TransPropertyList, 1);
+    list->account = account;
+    list->date_format = date_format;
+    list->currency_format = currency_format;
+    list->properties = NULL;
+    return list;
+}
+
+/** Destructor for TransPropertyList.
+ * @param list The list to be freed
+ */
+static void trans_property_list_free (TransPropertyList* list)
+{
+    /* Free all of the properties in this list before freeing the list itself. */
+    GList* properties_begin = list->properties;
+    while (list->properties != NULL)
+    {
+        trans_property_free ((TransProperty*)(list->properties->data));
+        list->properties = g_list_next (list->properties);
+    }
+    g_list_free (properties_begin);
+    g_free (list);
+}
+
+/** Adds a property to the list it's linked with.
+ * (The TransPropertyList is not passed as a parameter because the property is
+ * associated with a list when it's constructed.)
+ * @param property The property to be added to its list
+ */
+static void trans_property_list_add (TransProperty* property)
+{
+    property->list->properties = g_list_append (property->list->properties, property);
+}
+
+/** Adds a split to a transaction.
+ * @param trans The transaction to add a split to
+ * @param account The account used for the split
+ * @param book The book where the split should be stored
+ * @param amount The amount of the split
+ */
+static void trans_add_split (Transaction* trans, Account* account, QofBook* book,
+                            gnc_numeric amount, const char *num, const char *memo)
+{
+    Split* split = xaccMallocSplit (book);
+    xaccSplitSetAccount (split, account);
+    xaccSplitSetParent (split, trans);
+    xaccSplitSetAmount (split, amount);
+    xaccSplitSetValue (split, amount);
+    xaccSplitSetMemo (split, memo);
+    /* set tran-num and/or split-action per book option */
+    gnc_set_num_action (trans, split, num, NULL);
+}
+
+/** Adds a other split to a transaction.
+ * @param trans The transaction to add a split to
+ * @param account The account used for the other split
+ * @param book The book where the split should be stored
+ * @param amount The amount of the split
+ */
+static void trans_add_osplit (Transaction* trans, Account* account, QofBook* book,
+                            gnc_numeric amount, const char *num, const char *memo)
+{
+    Split *osplit = xaccMallocSplit (book);
+    xaccSplitSetAccount (osplit, account);
+    xaccSplitSetParent (osplit, trans);
+    xaccSplitSetAmount (osplit, amount);
+    xaccSplitSetValue (osplit, gnc_numeric_neg (amount));
+    xaccSplitSetMemo (osplit, memo);
+}
+
+/** Tests a TransPropertyList for having enough essential properties.
+ * Essential properties are "Date" and one of the following: "Balance", "Deposit", or
+ * "Withdrawal".
+ * @param list The list we are checking
+ * @param error Contains an error message on failure
+ * @return TRUE if there are enough essentials; FALSE otherwise
+ */
+static gboolean trans_property_list_verify_essentials (TransPropertyList* list, gchar** error)
+{
+    int i;
+    /* possible_errors lists the ways in which a list can fail this test. */
+    enum PossibleErrorTypes {NO_DATE, NO_AMOUNT, NUM_OF_POSSIBLE_ERRORS};
+    const gchar* possible_errors[NUM_OF_POSSIBLE_ERRORS] =
+    {
+        N_("No date column."),
+        N_("No balance, deposit, or withdrawal column.")
+    };
+    int possible_error_lengths[NUM_OF_POSSIBLE_ERRORS] = {0};
+    GList *properties_begin = list->properties, *errors_list = NULL;
+
+    /* Go through each of the properties and erase possible errors. */
+    while (list->properties)
+    {
+        switch (((TransProperty*)(list->properties->data))->type)
+        {
+        case GNC_CSV_DATE:
+            possible_errors[NO_DATE] = NULL;
+            break;
+
+        case GNC_CSV_BALANCE:
+        case GNC_CSV_DEPOSIT:
+        case GNC_CSV_WITHDRAWAL:
+            possible_errors[NO_AMOUNT] = NULL;
+            break;
+        default:
+            break;
+        }
+        list->properties = g_list_next (list->properties);
+    }
+    list->properties = properties_begin;
+
+    /* Accumulate a list of the actual errors. */
+    for (i = 0; i < NUM_OF_POSSIBLE_ERRORS; i++)
+    {
+        if (possible_errors[i] != NULL)
+        {
+            errors_list = g_list_append (errors_list, GINT_TO_POINTER(i));
+            /* Since we added an error, we want to also store its length for
+             * when we construct the full error string. */
+            possible_error_lengths[i] = strlen (_(possible_errors[i]));
+        }
+    }
+
+    /* If there are no errors, we can quit now. */
+    if (errors_list == NULL)
+        return TRUE;
+    else
+    {
+        /* full_error_size is the full length of the error message. */
+        int full_error_size = 0, string_length = 0;
+        GList* errors_list_begin = errors_list;
+        gchar *error_message, *error_message_begin;
+
+        /* Find the value for full_error_size. */
+        while (errors_list)
+        {
+            /* We add an extra 1 to account for spaces in between messages. */
+            full_error_size += possible_error_lengths[GPOINTER_TO_INT(errors_list->data)] + 1;
+            errors_list = g_list_next (errors_list);
+        }
+        errors_list = errors_list_begin;
+
+        /* Append the error messages one after another. */
+        error_message = error_message_begin = g_new (gchar, full_error_size);
+        while (errors_list)
+        {
+            i = GPOINTER_TO_INT(errors_list->data);
+            string_length = possible_error_lengths[i];
+
+            /* Copy the error message and put a space after it. */
+            strncpy(error_message, _(possible_errors[i]), string_length);
+            error_message += string_length;
+            *error_message = ' ';
+            error_message++;
+
+            errors_list = g_list_next (errors_list);
+        }
+        *error_message = '\0'; /* Replace the last space with the null byte. */
+        g_list_free (errors_list_begin);
+
+        *error = error_message_begin;
+        return FALSE;
+    }
+}
+
+/** Create a Transaction from a TransPropertyList.
+ * @param list The list of properties
+ * @param error Contains an error on failure
+ * @return On success, a GncCsvTransLine; on failure, the trans pointer is NULL
+ */
+static GncCsvTransLine* trans_property_list_to_trans (TransPropertyList* list, gchar** error)
+{
+    GncCsvTransLine* trans_line = g_new (GncCsvTransLine, 1);
+    GList* properties_begin = list->properties;
+    QofBook* book = gnc_account_get_book (list->account);
+    gnc_commodity* currency = xaccAccountGetCommodity (list->account);
+    gnc_numeric amount = double_to_gnc_numeric (0.0, xaccAccountGetCommoditySCU (list->account),
+                         GNC_HOW_RND_ROUND_HALF_UP);
+    gchar *num = NULL;
+    gchar *memo = NULL;
+    gchar *omemo = NULL;
+    Account *oaccount = NULL;
+
+    /* This flag is set to TRUE if we can use the "Deposit" or "Withdrawal" column. */
+    gboolean amount_set = FALSE;
+
+    /* The balance is 0 by default. */
+    trans_line->balance_set = FALSE;
+    trans_line->balance = amount;
+    trans_line->num = NULL;
+
+    /* We make the line_no -1 just to mark that it hasn't been set. We
+     * may get rid of line_no soon anyway, so it's not particularly
+     * important. */
+    trans_line->line_no = -1;
+
+    /* Make sure this is a transaction with all the columns we need. */
+    if (!trans_property_list_verify_essentials (list, error))
+    {
+        g_free(trans_line);
+        return NULL;
+    }
+
+    trans_line->trans = xaccMallocTransaction (book);
+    xaccTransBeginEdit (trans_line->trans);
+    xaccTransSetCurrency (trans_line->trans, currency);
+
+    /* Go through each of the properties and edit the transaction accordingly. */
+    list->properties = properties_begin;
+    while (list->properties != NULL)
+    {
+        TransProperty* prop = (TransProperty*)(list->properties->data);
+        switch (prop->type)
+        {
+        case GNC_CSV_DATE:
+            xaccTransSetDatePostedSecsNormalized (trans_line->trans, *((time64*)(prop->value)));
+            break;
+
+        case GNC_CSV_DESCRIPTION:
+            xaccTransSetDescription (trans_line->trans, (char*)(prop->value));
+            break;
+
+        case GNC_CSV_NOTES:
+            xaccTransSetNotes (trans_line->trans, (char*)(prop->value));
+            break;
+
+        case GNC_CSV_OACCOUNT:
+            oaccount = ((Account*)(prop->value));
+            break;
+
+        case GNC_CSV_MEMO:
+            memo = g_strdup ((char*)(prop->value));
+            break;
+
+        case GNC_CSV_OMEMO:
+            omemo = g_strdup ((char*)(prop->value));
+            break;
+
+        case GNC_CSV_NUM:
+            /* the 'num' is saved and passed to 'trans_add_split' below where
+             * 'gnc_set_num_action' is used to set tran-num and/or split-action
+             * per book option */
+            num = g_strdup ((char*)(prop->value));
+            /* the 'num' is also saved and used in 'gnc_csv_parse_to_trans' when
+             * it calls 'trans_add_split' after deleting the splits added below
+             * when a balance is used by the user */
+            trans_line->num = g_strdup ((char*)(prop->value));
+            break;
+
+        case GNC_CSV_DEPOSIT: /* Add deposits to the existing amount. */
+            if (prop->value != NULL)
+            {
+                amount = gnc_numeric_add (*((gnc_numeric*)(prop->value)),
+                                         amount,
+                                         xaccAccountGetCommoditySCU (list->account),
+                                         GNC_HOW_RND_ROUND_HALF_UP);
+                amount_set = TRUE;
+                /* We will use the "Deposit" and "Withdrawal" columns in preference to "Balance". */
+                trans_line->balance_set = FALSE;
+            }
+            break;
+
+        case GNC_CSV_WITHDRAWAL: /* Withdrawals are just negative deposits. */
+            if (prop->value != NULL)
+            {
+                amount = gnc_numeric_add (gnc_numeric_neg(*((gnc_numeric*)(prop->value))),
+                                         amount,
+                                         xaccAccountGetCommoditySCU (list->account),
+                                         GNC_HOW_RND_ROUND_HALF_UP);
+                amount_set = TRUE;
+                /* We will use the "Deposit" and "Withdrawal" columns in preference to "Balance". */
+                trans_line->balance_set = FALSE;
+            }
+            break;
+
+        case GNC_CSV_BALANCE: /* The balance gets stored in a separate field in trans_line. */
+            /* We will use the "Deposit" and "Withdrawal" columns in preference to "Balance". */
+            if (!amount_set && prop->value != NULL)
+            {
+                /* This gets put into the actual transaction at the end of gnc_csv_parse_to_trans. */
+                trans_line->balance = *((gnc_numeric*)(prop->value));
+                trans_line->balance_set = TRUE;
+            }
+            break;
+        default:
+            break;
+        }
+        list->properties = g_list_next (list->properties);
+    }
+
+    /* Add a split with the cumulative amount value. */
+    trans_add_split (trans_line->trans, list->account, book, amount, num, memo);
+
+    if (oaccount)
+        trans_add_osplit (trans_line->trans, oaccount, book, amount, num, omemo);
+
+    if (num)
+        g_free (num);
+    if (memo)
+        g_free (memo);
+    if (omemo)
+        g_free (omemo);
+
+    return trans_line;
+}
+
+/** Creates a list of transactions from parsed data. Transactions that
+ * could be created from rows are placed in parse_data->transactions;
+ * rows that fail are placed in parse_data->error_lines. (Note: there
+ * is no way for this function to "fail," i.e. it only returns 0, so
+ * it may be changed to a void function in the future.)
+ * @param parse_data Data that is being parsed
+ * @param account Account with which transactions are created
+ * @param redo_errors TRUE to convert only error data, FALSE for all data
+ * @return 0 on success, 1 on failure
+ */
+int gnc_csv_parse_to_trans (GncCsvParseData* parse_data, Account* account,
+                           gboolean redo_errors)
+{
+    gboolean hasBalanceColumn;
+    guint i, j, max_cols = 0;
+    GArray* column_types = parse_data->column_types;
+    GList *error_lines = NULL, *begin_error_lines = NULL;
+    Account *home_account = NULL;
+
+    /* last_transaction points to the last element in
+     * parse_data->transactions, or NULL if it's empty. */
+    GList* last_transaction = NULL;
+
+    /* Free parse_data->error_lines and parse_data->transactions if they
+     * already exist. */
+    if (redo_errors) /* If we're redoing errors, we save freeing until the end. */
+        begin_error_lines = error_lines = parse_data->error_lines;
+    else
+    {
+        if (parse_data->error_lines != NULL)
+            g_list_free(parse_data->error_lines);
+
+        if (parse_data->transactions != NULL)
+            g_list_free (parse_data->transactions);
+    }
+    parse_data->error_lines = NULL;
+
+    if (redo_errors) /* If we're looking only at error data ... */
+    {
+        if (parse_data->transactions == NULL)
+            last_transaction = NULL;
+        else
+        {
+            /* Move last_transaction to the end. */
+            last_transaction = parse_data->transactions;
+            while (g_list_next (last_transaction) != NULL)
+            {
+                last_transaction = g_list_next (last_transaction);
+            }
+        }
+        /* ... we use only the lines in error_lines. */
+        if (error_lines == NULL)
+            i = parse_data->orig_lines->len; /* Don't go into the for loop. */
+        else
+            i = GPOINTER_TO_INT(error_lines->data);
+    }
+    else /* Otherwise, we look at all the data. */
+    {
+        /* The following while-loop effectively behaves like the following for-loop:
+         * for(i = 0; i < parse_data->orig_lines->len; i++). */
+        i = parse_data->start_row;
+        last_transaction = NULL;
+    }
+
+    /* set parse_data->end_row to number of lines */
+    if (parse_data->end_row > parse_data->orig_lines->len)
+        parse_data->end_row = parse_data->orig_lines->len;
+
+    while (i < parse_data->end_row)
+    {
+        GPtrArray* line = (GPtrArray*) parse_data->orig_lines->pdata[i];
+        /* This flag is TRUE if there are any errors in this row. */
+        gboolean errors = FALSE;
+        gchar* error_message = NULL;
+        TransPropertyList* list;
+        GncCsvTransLine* trans_line = NULL;
+
+        home_account = account;
+
+        // If account = NULL, we should have an Account column
+        if (home_account == NULL)
+        {
+            for (j = 0; j < line->len; j++)
+            {
+                /* Look for "Account" columns. */
+                if (column_types->data[j] == GNC_CSV_ACCOUNT)
+                {
+                    home_account = gnc_csv_account_map_search ((gchar*) line->pdata[j]);
+                }
+            }
+        }
+
+        if (home_account == NULL)
+        {
+            error_message = g_strdup_printf (_("Account column could not be understood."));
+            errors = TRUE;
+        }
+        else
+        {
+            list = trans_property_list_new (home_account, parse_data->date_format, parse_data->currency_format);
+
+            for (j = 0; j < line->len; j++)
+            {
+                /* We do nothing in "None" or "Account" columns. */
+                if ((column_types->data[j] != GNC_CSV_NONE) && (column_types->data[j] != GNC_CSV_ACCOUNT))
+                {
+                    /* Affect the transaction appropriately. */
+                    TransProperty* property = trans_property_new (column_types->data[j], list);
+                    gboolean succeeded = trans_property_set (property, (gchar *) line->pdata[j]);
+
+                    /* TODO Maybe move error handling to within TransPropertyList functions? */
+                    if (succeeded)
+                        trans_property_list_add (property);
+                    else
+                    {
+                        errors = TRUE;
+                        error_message = g_strdup_printf (_("%s column could not be understood."),
+                                                        _(gnc_csv_column_type_strs[property->type]));
+                        trans_property_free (property);
+                        break;
+                    }
+                }
+            }
+
+            /* If we had success, add the transaction to parse_data->transaction. */
+            if (!errors)
+            {
+                trans_line = trans_property_list_to_trans (list, &error_message);
+                errors = trans_line == NULL;
+            }
+            trans_property_list_free (list);
+        }
+
+        /* If there were errors, add this line to parse_data->error_lines. */
+        if (errors)
+        {
+            parse_data->error_lines = g_list_append (parse_data->error_lines,
+                                                    GINT_TO_POINTER(i));
+            /* If there's already an error message, we need to replace it. */
+            if (line->len > (guint)(parse_data->orig_row_lengths->data[i]))
+            {
+                g_free(line->pdata[line->len - 1]);
+                line->pdata[line->len - 1] = error_message;
+            }
+            else
+            {
+                /* Put the error message at the end of the line. */
+                g_ptr_array_add (line, error_message);
+            }
+        }
+        else
+        {
+            /* If all went well, add this transaction to the list. */
+            trans_line->line_no = i;
+
+            /* We keep the transactions sorted by date. We start at the end
+             * of the list and go backward, simply because the file itself
+             * is probably also sorted by date (but we need to handle the
+             * exception anyway). */
+
+            /* If we can just put it at the end, do so and increment last_transaction. */
+            if (last_transaction == NULL ||
+                    xaccTransGetDate (((GncCsvTransLine*)(last_transaction->data))->trans) <= xaccTransGetDate (trans_line->trans))
+            {
+                parse_data->transactions = g_list_append (parse_data->transactions, trans_line);
+                /* If this is the first transaction, we need to get last_transaction on track. */
+                if (last_transaction == NULL)
+                    last_transaction = parse_data->transactions;
+                else /* Otherwise, we can just continue. */
+                    last_transaction = g_list_next (last_transaction);
+            }
+            /* Otherwise, search backward for the correct spot. */
+            else
+            {
+                GList* insertion_spot = last_transaction;
+                while (insertion_spot != NULL &&
+                        xaccTransGetDate (((GncCsvTransLine*)(insertion_spot->data))->trans) > xaccTransGetDate (trans_line->trans))
+                {
+                    insertion_spot = g_list_previous (insertion_spot);
+                }
+                /* Move insertion_spot one location forward since we have to
+                 * use the g_list_insert_before function. */
+                if (insertion_spot == NULL) /* We need to handle the case of inserting at the beginning of the list. */
+                    insertion_spot = parse_data->transactions;
+                else
+                    insertion_spot = g_list_next (insertion_spot);
+
+                parse_data->transactions = g_list_insert_before (parse_data->transactions, insertion_spot, trans_line);
+            }
+        }
+
+        /* Increment to the next row. */
+        if (redo_errors)
+        {
+            /* Move to the next error line in the list. */
+            error_lines = g_list_next (error_lines);
+            if (error_lines == NULL)
+                i = parse_data->orig_lines->len; /* Don't continue the for loop. */
+            else
+                i = GPOINTER_TO_INT(error_lines->data);
+        }
+        else
+        {
+            if (parse_data->skip_rows == FALSE)
+                i++;
+            else
+                i = i + 2;
+        }
+    }
+
+    /* If we have a balance column, set the appropriate amounts on the transactions. */
+    hasBalanceColumn = FALSE;
+    for (i = 0; i < parse_data->column_types->len; i++)
+    {
+        if (parse_data->column_types->data[i] == GNC_CSV_BALANCE)
+        {
+            hasBalanceColumn = TRUE;
+            break;
+        }
+    }
+
+    if (hasBalanceColumn) // This is only used if we have one home account
+    {
+        Split      *split, *osplit;
+        gnc_numeric balance_offset;
+        GList      *transactions = parse_data->transactions;
+
+        if (account != NULL)
+            home_account = account;
+
+        /* balance_offset is how much the balance currently in the account
+         * differs from what it will be after the transactions are
+         * imported. This will be sum of all the previous transactions for
+         * any given transaction. */
+        balance_offset = double_to_gnc_numeric (0.0, xaccAccountGetCommoditySCU (home_account),
+                                     GNC_HOW_RND_ROUND_HALF_UP);
+
+        while (transactions != NULL)
+        {
+            GncCsvTransLine* trans_line = (GncCsvTransLine*)transactions->data;
+            if (trans_line->balance_set)
+            {
+                time64 date = xaccTransGetDate (trans_line->trans);
+                /* Find what the balance should be by adding the offset to the actual balance. */
+                gnc_numeric existing_balance = gnc_numeric_add (balance_offset,
+                                               xaccAccountGetBalanceAsOfDate (home_account, date),
+                                               xaccAccountGetCommoditySCU (home_account),
+                                               GNC_HOW_RND_ROUND_HALF_UP);
+
+                /* The amount of the transaction is the difference between the new and existing balance. */
+                gnc_numeric amount = gnc_numeric_sub (trans_line->balance,
+                                                     existing_balance,
+                                                     xaccAccountGetCommoditySCU (home_account),
+                                                     GNC_HOW_RND_ROUND_HALF_UP);
+
+                // Find home account split
+                split  = xaccTransFindSplitByAccount (trans_line->trans, home_account);
+                xaccSplitSetAmount (split, amount);
+                xaccSplitSetValue (split, amount);
+
+                // If we have two splits, change other side
+                if (xaccTransCountSplits (trans_line->trans) == 2)
+                {
+                    osplit = xaccSplitGetOtherSplit (split);
+                    xaccSplitSetAmount (split, amount);
+                    xaccSplitSetValue (split, gnc_numeric_neg (amount));
+                }
+
+                if (trans_line->num)
+                    g_free (trans_line->num);
+
+                /* This new transaction needs to be added to the balance offset. */
+                balance_offset = gnc_numeric_add (balance_offset,
+                                                 amount,
+                                                 xaccAccountGetCommoditySCU (home_account),
+                                                 GNC_HOW_RND_ROUND_HALF_UP);
+            }
+            transactions = g_list_next (transactions);
+        }
+    }
+
+    if (redo_errors) /* Now that we're at the end, we do the freeing. */
+        g_list_free (begin_error_lines);
+
+    /* We need to resize parse_data->column_types since errors may have added columns. */
+    for (i = 0; i < parse_data->orig_lines->len; i++)
+    {
+        if (max_cols < ((GPtrArray*)(parse_data->orig_lines->pdata[i]))->len)
+            max_cols = ((GPtrArray*)(parse_data->orig_lines->pdata[i]))->len;
+    }
+    i = parse_data->column_types->len;
+    parse_data->column_types = g_array_set_size (parse_data->column_types, max_cols);
+    for (; i < max_cols; i++)
+    {
+        parse_data->column_types->data[i] = GNC_CSV_NONE;
+    }
+    return 0;
+}
+
+
+gboolean
+gnc_csv_parse_check_for_column_type (GncCsvParseData* parse_data, gint type)
+{
+    GArray* column_types = parse_data->column_types;
+    gboolean ret = FALSE;
+    int j, ncols = column_types->len; /* ncols is the number of columns in the data. */
+
+    for (j = 0; j < ncols; j++)
+    {
+        if (column_types->data[j] == type)
+            ret = TRUE;
+    }
+    return ret;
+}
diff --git a/src/import-export/csv-imp/gnc-csv-imp-trans.hpp b/src/import-export/csv-imp/gnc-csv-imp-trans.hpp
new file mode 100644
index 0000000..f620742
--- /dev/null
+++ b/src/import-export/csv-imp/gnc-csv-imp-trans.hpp
@@ -0,0 +1,153 @@
+/********************************************************************\
+ * gnc-csv-imp-trans.hpp - import transactions from csv files       *
+ *                                                                  *
+ * 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 2 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, contact:                        *
+ *                                                                  *
+ * Free Software Foundation           Voice:  +1-617-542-5942       *
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
+\********************************************************************/
+
+/** @file
+     @brief Class to import transactions from CSV files
+     *
+     gnc-csv-imp-trans.hpp
+     @author Copyright (c) 2015 Geert Janssens <geert at kobaltwit.be>
+ */
+
+#ifndef GNC_CSV_IMP_TRANS_HPP
+#define GNC_CSV_IMP_TRANS_HPP
+
+extern "C" {
+#include "config.h"
+
+#include "Account.h"
+#include "Transaction.h"
+}
+
+#include "stf/stf-parse.h"
+
+/** Enumeration for column types. These are the different types of
+ * columns that can exist in a CSV/Fixed-Width file. There should be
+ * no two columns with the same type except for the GNC_CSV_NONE
+ * type. */
+enum GncCsvColumnType {
+    GNC_CSV_NONE,
+    GNC_CSV_DATE,
+    GNC_CSV_NUM,
+    GNC_CSV_DESCRIPTION,
+    GNC_CSV_NOTES,
+    GNC_CSV_ACCOUNT,
+    GNC_CSV_DEPOSIT,
+    GNC_CSV_WITHDRAWAL,
+    GNC_CSV_BALANCE,
+    GNC_CSV_MEMO,
+    GNC_CSV_OACCOUNT,
+    GNC_CSV_OMEMO,
+    GNC_CSV_NUM_COL_TYPES
+};
+
+/** Error domain for the csv importer. */
+#define GNC_CSV_IMP_ERROR gnc_csv_imp_error_quark ()
+GQuark gnc_csv_imp_error_quark (void);
+
+/** Enumeration for error types. These are the different types of
+ * errors that various functions used for the CSV/Fixed-Width importer
+ * can have. */
+enum GncCsvErrorType {
+    GNC_CSV_IMP_ERROR_OPEN,
+    GNC_CSV_IMP_ERROR_ENCODING,
+    GNC_CSV_IMP_ERROR_PARSE
+};
+
+/** Struct for containing a string. This struct simply contains
+ * pointers to the beginning and end of a string. We need this because
+ * the STF code that gnc_csv_parse calls requires these pointers. */
+typedef struct
+{
+    char* begin;
+    char* end;
+} GncCsvStr;
+
+/* TODO We now sort transactions by date, not line number, so we
+ * should probably get rid of this struct and uses of it. */
+
+/** Struct pairing a transaction with a line number. This struct is
+ * used to keep the transactions in order. When rows are separated
+ * into "valid" and "error" lists (in case some of the rows have cells
+ * that are unparseable), we want the user to still be able to
+ * "correct" the error list. If we keep the line numbers of valid
+ * transactions, we can then put transactions created from the newly
+ * corrected rows into the right places. */
+typedef struct
+{
+    int line_no;
+    Transaction* trans;
+    gnc_numeric balance;  /**< The (supposed) balance after this transaction takes place */
+    gboolean balance_set; /**< TRUE if balance has been set from user data, FALSE otherwise */
+    gchar *num;           /**< Saves the 'num'for use if balance has been set from user data */
+} GncCsvTransLine;
+
+/* A set of currency formats that the user sees. */
+extern const int num_currency_formats;
+extern const gchar* currency_format_user[];
+
+/* A set of date formats that the user sees. */
+extern const int num_date_formats;
+extern const gchar* date_format_user[];
+
+/* This array contains all of the different strings for different column types. */
+extern const gchar* gnc_csv_column_type_strs[];
+
+/** Struct containing data for parsing a CSV/Fixed-Width file. */
+typedef struct
+{
+    const gchar* encoding;
+    GMappedFile* raw_mapping;   /**< The mapping containing raw_str */
+    GncCsvStr raw_str;          /**< Untouched data from the file as a string */
+    GncCsvStr file_str;         /**< raw_str translated into UTF-8 */
+    GPtrArray* orig_lines;      /**< file_str parsed into a two-dimensional array of strings */
+    GArray* orig_row_lengths;   /**< The lengths of rows in orig_lines
+                                      before error messages are appended */
+    int orig_max_row;           /**< Holds the maximum value in orig_row_lengths */
+    GStringChunk* chunk;        /**< A chunk of memory in which the contents of orig_lines is stored */
+    StfParseOptions_t* options; /**< Options controlling how file_str should be parsed */
+    GArray* column_types;       /**< Array of values from the GncCsvColumnType enumeration */
+    GList* error_lines;         /**< List of row numbers in orig_lines that have errors */
+    GList* transactions;        /**< List of GncCsvTransLine*s created using orig_lines and column_types */
+    int date_format;            /**< The format of the text in the date columns from date_format_internal. */
+    guint start_row;            /**< The start row to generate transactions from. */
+    guint end_row;              /**< The end row to generate transactions from. */
+    gboolean skip_rows;         /**< Skip Alternate Rows from start row. */
+    int currency_format;        /**< The currency format, 0 for locale, 1 for comma dec and 2 for period */
+} GncCsvParseData;
+
+GncCsvParseData* gnc_csv_new_parse_data (void);
+
+void gnc_csv_parse_data_free (GncCsvParseData* parse_data);
+
+int gnc_csv_load_file (GncCsvParseData* parse_data, const char* filename,
+                      GError** error);
+
+int gnc_csv_convert_encoding (GncCsvParseData* parse_data, const char* encoding, GError** error);
+
+int gnc_csv_parse (GncCsvParseData* parse_data, gboolean guessColTypes, GError** error);
+
+int gnc_csv_parse_to_trans (GncCsvParseData* parse_data, Account* account, gboolean redo_errors);
+
+time64 parse_date (const char* date_str, int format);
+
+gboolean gnc_csv_parse_check_for_column_type (GncCsvParseData* parse_data, gint type);
+
+#endif

commit 9ff993bbffa31b161bd79aac6f4840e54ac6f507
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Tue Jun 7 21:59:28 2016 +0200

    Add dummy tokenizer to be used when file format isn't known yet

diff --git a/src/import-export/csv-imp/CMakeLists.txt b/src/import-export/csv-imp/CMakeLists.txt
index 88323ee..b2b9bb2 100644
--- a/src/import-export/csv-imp/CMakeLists.txt
+++ b/src/import-export/csv-imp/CMakeLists.txt
@@ -14,6 +14,7 @@ SET(csv_import_SOURCES
   gnc-csv-gnumeric-popup.c
   gnc-csv-tokenizer.cpp
   gnc-csv-trans-settings.c
+  gnc-dummy-tokenizer.cpp
   gnc-fw-tokenizer.cpp
   gnc-tokenizer.cpp
   ${CMAKE_SOURCE_DIR}/lib/stf/stf-parse.c
@@ -37,6 +38,7 @@ SET(csv_import_noinst_HEADERS
   gnc-csv-gnumeric-popup.h
   gnc-csv-tokenizer.hpp
   gnc-csv-trans-settings.h
+  gnc-dummy-tokenizer.hpp
   gnc-fw-tokenizer.hpp
   gnc-tokenizer.hpp
   ${CMAKE_SOURCE_DIR}/lib/stf/stf-parse.h
diff --git a/src/import-export/csv-imp/Makefile.am b/src/import-export/csv-imp/Makefile.am
index 3e709e9..13c579f 100644
--- a/src/import-export/csv-imp/Makefile.am
+++ b/src/import-export/csv-imp/Makefile.am
@@ -14,6 +14,7 @@ libgncmod_csv_import_la_SOURCES = \
   gnc-csv-model.c \
   gnc-csv-tokenizer.cpp \
   gnc-csv-gnumeric-popup.c \
+  gnc-dummy-tokenizer.cpp \
   gnc-fw-tokenizer.cpp \
   gnc-tokenizer.cpp \
   gnc-csv-trans-settings.c
@@ -29,6 +30,7 @@ noinst_HEADERS = \
   gnc-csv-model.h \
   gnc-csv-tokenizer.hpp \
   gnc-csv-gnumeric-popup.h \
+  gnc-dummy-tokenizer.hpp \
   gnc-fw-tokenizer.hpp \
   gnc-tokenizer.hpp \
   gnc-csv-trans-settings.h
diff --git a/src/import-export/csv-imp/gnc-dummy-tokenizer.cpp b/src/import-export/csv-imp/gnc-dummy-tokenizer.cpp
new file mode 100644
index 0000000..c6cb395
--- /dev/null
+++ b/src/import-export/csv-imp/gnc-dummy-tokenizer.cpp
@@ -0,0 +1,31 @@
+#include "gnc-dummy-tokenizer.hpp"
+
+#include <iostream>
+#include <fstream>      // fstream
+#include <vector>
+#include <string>
+#include <algorithm>    // copy
+#include <iterator>     // ostream_operator
+
+#include <boost/locale.hpp>
+
+
+int GncDummyTokenizer::tokenize()
+{
+    std::vector<std::string> vec;
+    std::string line;
+
+    tokenized_contents.clear();
+    std::istringstream in_stream(utf8_contents);
+
+    while (std::getline (in_stream, line))
+    {
+        vec.push_back (line);
+        tokenized_contents.push_back(vec);
+
+        line.clear();
+        vec.clear();
+    }
+
+    return 0;
+}
diff --git a/src/import-export/csv-imp/gnc-dummy-tokenizer.hpp b/src/import-export/csv-imp/gnc-dummy-tokenizer.hpp
new file mode 100644
index 0000000..4c3034a
--- /dev/null
+++ b/src/import-export/csv-imp/gnc-dummy-tokenizer.hpp
@@ -0,0 +1,62 @@
+/********************************************************************\
+ * gnc-dummy-tokenizer.hpp - takes a file and converts it into a       *
+ *                        two-dimensional vector of strings (table) *
+ *                        splitting the contents on fixed width     *
+ *                        positions                                 *
+ *                                                                  *
+ * 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 2 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, contact:                        *
+ *                                                                  *
+ * Free Software Foundation           Voice:  +1-617-542-5942       *
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
+\********************************************************************/
+
+/** @file
+     @brief Dummy converter class to convert a file
+     into vector of string vectors. Each string vector has only one element,
+     the contents of one line of the file.
+     This is just a dummy that can be used as long as the file format isn't
+     specified yet by the user.
+     *
+     gnc-dummy-tokenizer.hpp
+     @author Copyright (c) 2016 Geert Janssens <geert at kobaltwit.be>
+ */
+
+#ifndef GNC_DUMMY_TOKENIZER_HPP
+#define GNC_DUMMY_TOKENIZER_HPP
+
+extern "C" {
+#include "config.h"
+}
+
+#include <iostream>
+#include <fstream>      // fstream
+#include <vector>
+#include <string>
+#include "gnc-tokenizer.hpp"
+
+class GncDummyTokenizer : public GncTokenizer
+{
+public:
+    GncDummyTokenizer() = default;                                 // default constructor
+    GncDummyTokenizer(const GncDummyTokenizer&) = default;            // copy constructor
+    GncDummyTokenizer& operator=(const GncDummyTokenizer&) = default; // copy assignment
+    GncDummyTokenizer(GncDummyTokenizer&&) = default;                 // move constructor
+    GncDummyTokenizer& operator=(GncDummyTokenizer&&) = default;      // move assignment
+    ~GncDummyTokenizer() = default;                                // destructor
+
+    int  tokenize() override;
+};
+
+#endif
diff --git a/src/import-export/csv-imp/gnc-tokenizer.cpp b/src/import-export/csv-imp/gnc-tokenizer.cpp
index 2a3d24f..73963cd 100644
--- a/src/import-export/csv-imp/gnc-tokenizer.cpp
+++ b/src/import-export/csv-imp/gnc-tokenizer.cpp
@@ -1,5 +1,6 @@
 #include "gnc-tokenizer.hpp"
 #include "gnc-csv-tokenizer.hpp"
+#include "gnc-dummy-tokenizer.hpp"
 #include "gnc-fw-tokenizer.hpp"
 
 #include <iostream>
@@ -28,6 +29,7 @@ std::unique_ptr<GncTokenizer> GncTokenizerFactory(GncImpFileFormat fmt)
         tok.reset(new GncFwTokenizer());
         break;
     default:
+        tok.reset(new GncDummyTokenizer());
         break;
     }
 

commit c6043ccc235de08db79fd595c53e61c68badc2f0
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sun Jun 12 12:27:08 2016 +0200

    Add functions to manipulate fixed width column positions

diff --git a/src/import-export/csv-imp/gnc-fw-tokenizer.cpp b/src/import-export/csv-imp/gnc-fw-tokenizer.cpp
index 5fa0714..4a3ce42 100644
--- a/src/import-export/csv-imp/gnc-fw-tokenizer.cpp
+++ b/src/import-export/csv-imp/gnc-fw-tokenizer.cpp
@@ -17,6 +17,165 @@ GncFwTokenizer::columns(const std::vector<uint>& cols)
 }
 
 
+bool GncFwTokenizer::col_can_add (uint col_end)
+{
+    if (col_end < 0 || col_end > longest_line)
+        return false;
+    else
+        return true;
+}
+
+void GncFwTokenizer::col_add (uint col_end)
+{
+    if (col_can_add (col_end))
+    {
+        for (auto col_it = col_vec.begin(); col_it != col_vec.end(); col_it++)
+        {
+            if (*col_it == col_end)
+                return; // don't add same column end twice in the column list
+            if (*col_it > col_end)
+                col_vec.insert (col_it, col_end);
+        }
+
+        // If we got here that means the requested col_end is beyond the currently
+        // inserted columns, so append it
+        col_vec.push_back (col_end);
+    }
+}
+
+bool GncFwTokenizer::col_can_delete (uint col_num)
+{
+    auto last_col = col_vec.size() - 1;
+    if (col_num < 0 || col_num > last_col)
+        return false;
+    else
+        return true;
+}
+
+void GncFwTokenizer::col_delete (uint col_num)
+{
+    if (col_can_delete (col_num))
+        col_vec.erase (col_vec.begin() + col_num);
+}
+
+bool GncFwTokenizer::col_can_narrow (uint col_num)
+{
+    auto last_col = col_vec.size() - 1;
+    int col_start, next_col_start;
+
+    if (col_num > last_col)
+        return false;
+
+    col_start = (col_num == 0) ? 0 : col_vec[col_num - 1];
+    next_col_start = col_vec[col_num];
+
+    if (next_col_start - 1 <= col_start)
+        return false;
+    else
+        return true;
+}
+
+void GncFwTokenizer::col_narrow (uint col_num)
+{
+    if (col_can_narrow (col_num))
+        col_vec[col_num]--;
+}
+
+bool GncFwTokenizer::col_can_widen (uint col_num)
+{
+    auto last_col = col_vec.size() - 1;
+    int col_end, next_col_end;
+
+    if (col_num > last_col)
+        return false;
+
+    col_end = col_vec[col_num];
+    next_col_end = (col_num == last_col - 1)
+                    ? longest_line
+                    : col_vec[col_num + 1];
+
+    if (col_end + 1 >= next_col_end)
+        return false;
+    else
+        return true;
+}
+
+void GncFwTokenizer::col_widen (uint col_num)
+{
+    if (col_can_widen (col_num))
+        col_vec[col_num]++;
+}
+
+bool GncFwTokenizer::col_can_split (uint col_num, uint position)
+{
+    auto last_col = col_vec.size() - 1;
+    if (col_num > last_col)
+        return false;
+
+    uint col_start = (col_num == 0) ? 0 : col_vec[col_num - 1];
+    uint col_end = col_vec[col_num];
+    if (position <= col_start || position >= col_end)
+        return false;
+    else
+        return true;
+}
+
+void GncFwTokenizer::col_split (uint col_num, uint position)
+{
+    if (col_can_split (col_num, position))
+    {
+        uint col_start = (col_num == 0) ? 0 : col_vec[col_num - 1];;
+        col_vec.insert (col_vec.begin() + col_num, col_start + position);
+    }
+}
+
+
+std::string GncFwTokenizer::cols_to_string()
+{
+    std::ostringstream colstream;
+    for (auto col_end : col_vec)
+        colstream<<col_end<<",";
+    std::string colstr = colstream.str();
+    if (!colstr.empty())
+        colstr.pop_back(); // Drop last ","
+    return colstr;
+}
+
+void GncFwTokenizer::cols_from_string(const std::string& col_str)
+{
+    // Clear existing columns first
+    columns();
+
+    std::istringstream in_stream(col_str);
+    std::string col_end_str;
+    while (std::getline (in_stream, col_end_str, ','))
+    {
+        if (col_end_str.empty())
+            continue;  // Skip empty column positions
+
+        uint charindex = std::stoi (col_end_str);
+        col_add (charindex);
+    }
+}
+
+
+void GncFwTokenizer::load_file(const std::string& path)
+{
+    GncTokenizer::load_file(path);
+
+    std::string line;
+    longest_line = 0;
+    std::istringstream in_stream(utf8_contents);
+    while (std::getline (in_stream, line))
+    {
+        if (line.size() > longest_line)
+            longest_line = line.size();
+
+        line.clear();
+    }
+}
+
+
 int GncFwTokenizer::tokenize()
 {
     typedef boost::tokenizer< boost::offset_separator > Tokenizer;
diff --git a/src/import-export/csv-imp/gnc-fw-tokenizer.hpp b/src/import-export/csv-imp/gnc-fw-tokenizer.hpp
index d713740..523f63b 100644
--- a/src/import-export/csv-imp/gnc-fw-tokenizer.hpp
+++ b/src/import-export/csv-imp/gnc-fw-tokenizer.hpp
@@ -57,11 +57,31 @@ public:
     GncFwTokenizer& operator=(GncFwTokenizer&&) = default;      // move assignment
     ~GncFwTokenizer() = default;                                // destructor
 
-    void columns(const std::vector<uint>& cols);
+    void columns(const std::vector<uint>& cols = std::vector<uint>());
+    uint get_column (uint num);
+
+    // Column manipulators
+    bool col_can_add (uint col_end);
+    void col_add (uint col_end);
+    bool col_can_delete (uint col_num);
+    void col_delete (uint col_num);
+    bool col_can_narrow (uint col_num);
+    void col_narrow (uint col_num);
+    bool col_can_widen (uint col_num);
+    void col_widen (uint col_num);
+    bool col_can_split (uint col_num, uint position);
+    void col_split (uint col_num, uint position);
+
+    std::string cols_to_string();
+    void cols_from_string(const std::string& col_str);
+
+    void load_file (const std::string& path);
     int  tokenize() override;
 
+
 private:
     std::vector<uint> col_vec;
+    uint longest_line;
 };
 
 #endif

commit 48cfbc23d4f35da060d06dd6eeaa93700909dbc9
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Thu Jan 28 12:30:29 2016 +0100

    Add fixed width file parser based on boost::tokenize
    
    Effective c++ notes:
    - explicitly declare special member functions
    - explicitly declare overriding functions as override

diff --git a/po/POTFILES.in b/po/POTFILES.in
index d0f324c..7ad6b3d 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -438,6 +438,7 @@ src/import-export/csv-imp/gnc-csv-gnumeric-popup.c
 src/import-export/csv-imp/gnc-csv-model.c
 src/import-export/csv-imp/gnc-csv-tokenizer.cpp
 src/import-export/csv-imp/gnc-csv-trans-settings.c
+src/import-export/csv-imp/gnc-fw-tokenizer.cpp
 src/import-export/csv-imp/gncmod-csv-import.c
 src/import-export/csv-imp/gnc-plugin-csv-import.c
 src/import-export/csv-imp/gnc-tokenizer.cpp
diff --git a/src/import-export/csv-imp/CMakeLists.txt b/src/import-export/csv-imp/CMakeLists.txt
index 0db68d8..88323ee 100644
--- a/src/import-export/csv-imp/CMakeLists.txt
+++ b/src/import-export/csv-imp/CMakeLists.txt
@@ -14,6 +14,7 @@ SET(csv_import_SOURCES
   gnc-csv-gnumeric-popup.c
   gnc-csv-tokenizer.cpp
   gnc-csv-trans-settings.c
+  gnc-fw-tokenizer.cpp
   gnc-tokenizer.cpp
   ${CMAKE_SOURCE_DIR}/lib/stf/stf-parse.c
   ${CMAKE_SOURCE_DIR}/lib/goffice/go-charmap-sel.c
@@ -36,6 +37,7 @@ SET(csv_import_noinst_HEADERS
   gnc-csv-gnumeric-popup.h
   gnc-csv-tokenizer.hpp
   gnc-csv-trans-settings.h
+  gnc-fw-tokenizer.hpp
   gnc-tokenizer.hpp
   ${CMAKE_SOURCE_DIR}/lib/stf/stf-parse.h
   ${CMAKE_SOURCE_DIR}/lib/goffice/go-charmap-sel.h
diff --git a/src/import-export/csv-imp/Makefile.am b/src/import-export/csv-imp/Makefile.am
index bcb949a..3e709e9 100644
--- a/src/import-export/csv-imp/Makefile.am
+++ b/src/import-export/csv-imp/Makefile.am
@@ -14,6 +14,7 @@ libgncmod_csv_import_la_SOURCES = \
   gnc-csv-model.c \
   gnc-csv-tokenizer.cpp \
   gnc-csv-gnumeric-popup.c \
+  gnc-fw-tokenizer.cpp \
   gnc-tokenizer.cpp \
   gnc-csv-trans-settings.c
 
@@ -28,6 +29,7 @@ noinst_HEADERS = \
   gnc-csv-model.h \
   gnc-csv-tokenizer.hpp \
   gnc-csv-gnumeric-popup.h \
+  gnc-fw-tokenizer.hpp \
   gnc-tokenizer.hpp \
   gnc-csv-trans-settings.h
 
diff --git a/src/import-export/csv-imp/gnc-fw-tokenizer.cpp b/src/import-export/csv-imp/gnc-fw-tokenizer.cpp
new file mode 100644
index 0000000..5fa0714
--- /dev/null
+++ b/src/import-export/csv-imp/gnc-fw-tokenizer.cpp
@@ -0,0 +1,48 @@
+#include "gnc-fw-tokenizer.hpp"
+
+#include <iostream>
+#include <fstream>      // fstream
+#include <vector>
+#include <string>
+#include <algorithm>    // copy
+#include <iterator>     // ostream_operator
+
+#include <boost/tokenizer.hpp>
+#include <boost/locale.hpp>
+
+void
+GncFwTokenizer::columns(const std::vector<uint>& cols)
+{
+    col_vec = cols;
+}
+
+
+int GncFwTokenizer::tokenize()
+{
+    typedef boost::tokenizer< boost::offset_separator > Tokenizer;
+
+    boost::offset_separator sep(col_vec.begin(), col_vec.end(), false);
+
+    std::vector<std::string> vec;
+    std::string line;
+    std::string buffer;
+
+    tokenized_contents.clear();
+    std::istringstream in_stream(utf8_contents);
+
+    while (std::getline (in_stream, line))
+    {
+        Tokenizer tok(line, sep);
+        vec.assign(tok.begin(),tok.end());
+
+        line.clear(); // clear here, next check could fail
+
+        // example checking
+        // for correctly parsed 3 fields per record
+        if (vec.size() < 3) continue;
+
+        tokenized_contents.push_back(vec);
+    }
+
+    return 0;
+}
diff --git a/src/import-export/csv-imp/gnc-fw-tokenizer.hpp b/src/import-export/csv-imp/gnc-fw-tokenizer.hpp
new file mode 100644
index 0000000..d713740
--- /dev/null
+++ b/src/import-export/csv-imp/gnc-fw-tokenizer.hpp
@@ -0,0 +1,67 @@
+/********************************************************************\
+ * gnc-fw-tokenizer.hpp - takes a file and converts it into a       *
+ *                        two-dimensional vector of strings (table) *
+ *                        splitting the contents on fixed width     *
+ *                        positions                                 *
+ *                                                                  *
+ * 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 2 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, contact:                        *
+ *                                                                  *
+ * Free Software Foundation           Voice:  +1-617-542-5942       *
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
+\********************************************************************/
+
+/** @file
+     @brief Class convert a file with fixed with delimited contents
+     into vector of string vectors.
+     One can define the widths of each column to use to split each line
+     into multiple fields.
+     However, no gnucash specific interpretation is done yet, that's up
+     to the code using this class.
+     *
+     gnc-fw-tokenizer.hpp
+     @author Copyright (c) 2015 Geert Janssens <geert at kobaltwit.be>
+ */
+
+#ifndef GNC_FW_TOKENIZER_HPP
+#define GNC_FW_TOKENIZER_HPP
+
+extern "C" {
+#include "config.h"
+}
+
+#include <iostream>
+#include <fstream>      // fstream
+#include <vector>
+#include <string>
+#include "gnc-tokenizer.hpp"
+
+class GncFwTokenizer : public GncTokenizer
+{
+public:
+    GncFwTokenizer() = default;                                 // default constructor
+    GncFwTokenizer(const GncFwTokenizer&) = default;            // copy constructor
+    GncFwTokenizer& operator=(const GncFwTokenizer&) = default; // copy assignment
+    GncFwTokenizer(GncFwTokenizer&&) = default;                 // move constructor
+    GncFwTokenizer& operator=(GncFwTokenizer&&) = default;      // move assignment
+    ~GncFwTokenizer() = default;                                // destructor
+
+    void columns(const std::vector<uint>& cols);
+    int  tokenize() override;
+
+private:
+    std::vector<uint> col_vec;
+};
+
+#endif
diff --git a/src/import-export/csv-imp/gnc-tokenizer.cpp b/src/import-export/csv-imp/gnc-tokenizer.cpp
index 3ffaf98..2a3d24f 100644
--- a/src/import-export/csv-imp/gnc-tokenizer.cpp
+++ b/src/import-export/csv-imp/gnc-tokenizer.cpp
@@ -1,5 +1,6 @@
 #include "gnc-tokenizer.hpp"
 #include "gnc-csv-tokenizer.hpp"
+#include "gnc-fw-tokenizer.hpp"
 
 #include <iostream>
 #include <fstream>      // fstream
@@ -23,6 +24,9 @@ std::unique_ptr<GncTokenizer> GncTokenizerFactory(GncImpFileFormat fmt)
     case GncImpFileFormat::CSV:
         tok.reset(new GncCsvTokenizer());
         break;
+    case GncImpFileFormat::FIXED_WIDTH:
+        tok.reset(new GncFwTokenizer());
+        break;
     default:
         break;
     }
diff --git a/src/import-export/csv-imp/gnc-tokenizer.hpp b/src/import-export/csv-imp/gnc-tokenizer.hpp
index 6a5d611..8562815 100644
--- a/src/import-export/csv-imp/gnc-tokenizer.hpp
+++ b/src/import-export/csv-imp/gnc-tokenizer.hpp
@@ -51,6 +51,7 @@ using str_vec = std::vector<std::string>;
 enum class GncImpFileFormat {
     UNKNOWN,
     CSV,
+    FIXED_WIDTH
 };
 
 

commit efcd266971b8d2ed59c5f09ac7d1081a0b23b4e5
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Tue Jan 26 09:48:29 2016 +0100

    Add csv file parser based on boost::tokenize
    
    Effective c++ notes:
    - explicitly declare special member functions
    - explicitly declare overriding functions as override

diff --git a/po/POTFILES.in b/po/POTFILES.in
index 6538e69..d0f324c 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -436,6 +436,7 @@ src/import-export/csv-imp/csv-fixed-trans-import.c
 src/import-export/csv-imp/gnc-csv-account-map.c
 src/import-export/csv-imp/gnc-csv-gnumeric-popup.c
 src/import-export/csv-imp/gnc-csv-model.c
+src/import-export/csv-imp/gnc-csv-tokenizer.cpp
 src/import-export/csv-imp/gnc-csv-trans-settings.c
 src/import-export/csv-imp/gncmod-csv-import.c
 src/import-export/csv-imp/gnc-plugin-csv-import.c
diff --git a/src/import-export/csv-imp/CMakeLists.txt b/src/import-export/csv-imp/CMakeLists.txt
index c078821..0db68d8 100644
--- a/src/import-export/csv-imp/CMakeLists.txt
+++ b/src/import-export/csv-imp/CMakeLists.txt
@@ -12,6 +12,7 @@ SET(csv_import_SOURCES
   gnc-csv-account-map.c
   gnc-csv-model.c
   gnc-csv-gnumeric-popup.c
+  gnc-csv-tokenizer.cpp
   gnc-csv-trans-settings.c
   gnc-tokenizer.cpp
   ${CMAKE_SOURCE_DIR}/lib/stf/stf-parse.c
@@ -33,6 +34,7 @@ SET(csv_import_noinst_HEADERS
   gnc-csv-account-map.h
   gnc-csv-model.h
   gnc-csv-gnumeric-popup.h
+  gnc-csv-tokenizer.hpp
   gnc-csv-trans-settings.h
   gnc-tokenizer.hpp
   ${CMAKE_SOURCE_DIR}/lib/stf/stf-parse.h
diff --git a/src/import-export/csv-imp/Makefile.am b/src/import-export/csv-imp/Makefile.am
index 22c0fb8..bcb949a 100644
--- a/src/import-export/csv-imp/Makefile.am
+++ b/src/import-export/csv-imp/Makefile.am
@@ -12,6 +12,7 @@ libgncmod_csv_import_la_SOURCES = \
   csv-fixed-trans-import.c \
   gnc-csv-account-map.c \
   gnc-csv-model.c \
+  gnc-csv-tokenizer.cpp \
   gnc-csv-gnumeric-popup.c \
   gnc-tokenizer.cpp \
   gnc-csv-trans-settings.c
@@ -25,6 +26,7 @@ noinst_HEADERS = \
   csv-fixed-trans-import.h \
   gnc-csv-account-map.h \
   gnc-csv-model.h \
+  gnc-csv-tokenizer.hpp \
   gnc-csv-gnumeric-popup.h \
   gnc-tokenizer.hpp \
   gnc-csv-trans-settings.h
diff --git a/src/import-export/csv-imp/gnc-csv-tokenizer.cpp b/src/import-export/csv-imp/gnc-csv-tokenizer.cpp
new file mode 100644
index 0000000..79e2319
--- /dev/null
+++ b/src/import-export/csv-imp/gnc-csv-tokenizer.cpp
@@ -0,0 +1,71 @@
+#include "gnc-csv-tokenizer.hpp"
+
+#include <iostream>
+#include <fstream>      // fstream
+#include <vector>
+#include <string>
+#include <algorithm>    // copy
+#include <iterator>     // ostream_operator
+
+#include <boost/tokenizer.hpp>
+#include <boost/locale.hpp>
+
+void
+GncCsvTokenizer::set_separators(const std::string& separators)
+{
+    sep_str = separators;
+}
+
+
+int GncCsvTokenizer::tokenize()
+{
+    typedef boost::tokenizer< boost::escaped_list_separator<char> > Tokenizer;
+
+    boost::escaped_list_separator<char> sep("\\", sep_str, "\"");
+
+    std::vector<std::string> vec;
+    std::string line;
+    std::string buffer;
+
+    bool inside_quotes(false);
+    size_t last_quote(0);
+
+    tokenized_contents.clear();
+    std::istringstream in_stream(utf8_contents);
+
+    while (std::getline (in_stream, buffer))
+    {
+        // --- deal with line breaks in quoted strings
+        last_quote = buffer.find_first_of('"');
+        while (last_quote != std::string::npos)
+        {
+            if (last_quote == 0) // Test separately because last_quote - 1 would be out of range
+                inside_quotes = !inside_quotes;
+            else if (buffer[ last_quote - 1 ] != '\\')
+                inside_quotes = !inside_quotes;
+
+            last_quote = buffer.find_first_of('"',last_quote+1);
+        }
+
+        line.append(buffer);
+        if (inside_quotes)
+        {
+            line.append("\n");
+            continue;
+        }
+        // ---
+
+        Tokenizer tok(line, sep);
+        vec.assign(tok.begin(),tok.end());
+
+        line.clear(); // clear here, next check could fail
+
+        // example checking
+        // for correctly parsed 3 fields per record
+        if (vec.size() < 3) continue;
+
+        tokenized_contents.push_back(vec);
+    }
+
+    return 0;
+}
diff --git a/src/import-export/csv-imp/gnc-csv-tokenizer.hpp b/src/import-export/csv-imp/gnc-csv-tokenizer.hpp
new file mode 100644
index 0000000..636ed7d
--- /dev/null
+++ b/src/import-export/csv-imp/gnc-csv-tokenizer.hpp
@@ -0,0 +1,64 @@
+/********************************************************************\
+ * gnc-csv-tokenizer.hpp - takes a csv file and converts it into a  *
+ *                         two-dimensional vector of strings (table)*
+ *                                                                  *
+ * 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 2 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, contact:                        *
+ *                                                                  *
+ * Free Software Foundation           Voice:  +1-617-542-5942       *
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
+\********************************************************************/
+
+/** @file
+     @brief Class to convert a csv file into vector of string vectors.
+     One can define the separator characters to use to split each line
+     into multiple fields. Quote characters will be removed.
+     However, no gnucash specific interpretation is done yet, that's up
+     to the code using this class.
+     *
+     gnc-csv-tokenizer.hpp
+     @author Copyright (c) 2015 Geert Janssens <geert at kobaltwit.be>
+ */
+
+#ifndef GNC_CSV_TOKENIZER_HPP
+#define GNC_CSV_TOKENIZER_HPP
+
+extern "C" {
+#include "config.h"
+}
+
+#include <iostream>
+#include <fstream>      // fstream
+#include <vector>
+#include <string>
+#include "gnc-tokenizer.hpp"
+
+class GncCsvTokenizer : public GncTokenizer
+{
+public:
+    GncCsvTokenizer() = default;                                  // default constructor
+    GncCsvTokenizer(const GncCsvTokenizer&) = default;            // copy constructor
+    GncCsvTokenizer& operator=(const GncCsvTokenizer&) = default; // copy assignment
+    GncCsvTokenizer(GncCsvTokenizer&&) = default;                 // move constructor
+    GncCsvTokenizer& operator=(GncCsvTokenizer&&) = default;      // move assignment
+    ~GncCsvTokenizer() = default;                                 // destructor
+
+    void set_separators(const std::string& separators);
+    int  tokenize() override;
+
+private:
+    std::string sep_str = ",";
+};
+
+#endif
diff --git a/src/import-export/csv-imp/gnc-tokenizer.cpp b/src/import-export/csv-imp/gnc-tokenizer.cpp
index 302d393..3ffaf98 100644
--- a/src/import-export/csv-imp/gnc-tokenizer.cpp
+++ b/src/import-export/csv-imp/gnc-tokenizer.cpp
@@ -1,4 +1,5 @@
 #include "gnc-tokenizer.hpp"
+#include "gnc-csv-tokenizer.hpp"
 
 #include <iostream>
 #include <fstream>      // fstream
@@ -19,6 +20,9 @@ std::unique_ptr<GncTokenizer> GncTokenizerFactory(GncImpFileFormat fmt)
     std::unique_ptr<GncTokenizer> tok(nullptr);
     switch (fmt)
     {
+    case GncImpFileFormat::CSV:
+        tok.reset(new GncCsvTokenizer());
+        break;
     default:
         break;
     }
diff --git a/src/import-export/csv-imp/gnc-tokenizer.hpp b/src/import-export/csv-imp/gnc-tokenizer.hpp
index 037f6fd..6a5d611 100644
--- a/src/import-export/csv-imp/gnc-tokenizer.hpp
+++ b/src/import-export/csv-imp/gnc-tokenizer.hpp
@@ -50,6 +50,7 @@ using str_vec = std::vector<std::string>;
 /** Enumeration for file formats supported by this importer. */
 enum class GncImpFileFormat {
     UNKNOWN,
+    CSV,
 };
 
 

commit ed7b863d8fe974b822db5e0fed7547997326f92a
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Thu Jan 28 11:18:20 2016 +0100

    Introduce base class for parsing importable files
    
    This base class takes care of the common things like
    file opening and character set conversion.
    The actual interpretation of the contents will be
    delegated to specialized child classes.
    
    Effective c++ note: explicitly declare special member functions

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 85d930b..733cb16 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -422,7 +422,7 @@ SET (Boost_FIND_QUIETLY ON)
 IF (NOT DEFINED ${BOOST_ROOT})
   SET(BOOST_ROOT $ENV{BOOST_ROOT})
 ENDIF()
-FIND_PACKAGE (Boost 1.54.0 REQUIRED COMPONENTS date_time regex)
+FIND_PACKAGE (Boost 1.54.0 REQUIRED COMPONENTS date_time regex locale)
 
 IF (Boost_FOUND)
   include_directories(${Boost_INCLUDE_DIRS})
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 34359fe..6538e69 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -439,6 +439,7 @@ src/import-export/csv-imp/gnc-csv-model.c
 src/import-export/csv-imp/gnc-csv-trans-settings.c
 src/import-export/csv-imp/gncmod-csv-import.c
 src/import-export/csv-imp/gnc-plugin-csv-import.c
+src/import-export/csv-imp/gnc-tokenizer.cpp
 [type: gettext/gsettings]src/import-export/csv-imp/gschemas/org.gnucash.dialogs.import.csv.gschema.xml.in.in
 src/import-export/dialog-import.glade
 src/import-export/gncmod-generic-import.c
diff --git a/src/import-export/csv-imp/CMakeLists.txt b/src/import-export/csv-imp/CMakeLists.txt
index 9ece8d0..c078821 100644
--- a/src/import-export/csv-imp/CMakeLists.txt
+++ b/src/import-export/csv-imp/CMakeLists.txt
@@ -13,6 +13,7 @@ SET(csv_import_SOURCES
   gnc-csv-model.c
   gnc-csv-gnumeric-popup.c
   gnc-csv-trans-settings.c
+  gnc-tokenizer.cpp
   ${CMAKE_SOURCE_DIR}/lib/stf/stf-parse.c
   ${CMAKE_SOURCE_DIR}/lib/goffice/go-charmap-sel.c
   ${CMAKE_SOURCE_DIR}/lib/goffice/go-optionmenu.c
@@ -33,6 +34,7 @@ SET(csv_import_noinst_HEADERS
   gnc-csv-model.h
   gnc-csv-gnumeric-popup.h
   gnc-csv-trans-settings.h
+  gnc-tokenizer.hpp
   ${CMAKE_SOURCE_DIR}/lib/stf/stf-parse.h
   ${CMAKE_SOURCE_DIR}/lib/goffice/go-charmap-sel.h
   ${CMAKE_SOURCE_DIR}/lib/goffice/go-optionmenu.h
@@ -41,7 +43,7 @@ SET(csv_import_noinst_HEADERS
 
 ADD_LIBRARY(gncmod-csv-import ${csv_import_noinst_HEADERS} ${csv_import_SOURCES})
 
-TARGET_LINK_LIBRARIES(gncmod-csv-import ${GOFFICE_LDFLAGS} gncmod-generic-import gncmod-gnome-utils
+TARGET_LINK_LIBRARIES(gncmod-csv-import ${GOFFICE_LDFLAGS} ${Boost_LIBRARIES} gncmod-generic-import gncmod-gnome-utils
                  gncmod-app-utils gncmod-engine gnc-core-utils gnc-module)
 
 
diff --git a/src/import-export/csv-imp/Makefile.am b/src/import-export/csv-imp/Makefile.am
index 9400ce8..22c0fb8 100644
--- a/src/import-export/csv-imp/Makefile.am
+++ b/src/import-export/csv-imp/Makefile.am
@@ -13,6 +13,7 @@ libgncmod_csv_import_la_SOURCES = \
   gnc-csv-account-map.c \
   gnc-csv-model.c \
   gnc-csv-gnumeric-popup.c \
+  gnc-tokenizer.cpp \
   gnc-csv-trans-settings.c
 
 noinst_HEADERS = \
@@ -25,6 +26,7 @@ noinst_HEADERS = \
   gnc-csv-account-map.h \
   gnc-csv-model.h \
   gnc-csv-gnumeric-popup.h \
+  gnc-tokenizer.hpp \
   gnc-csv-trans-settings.h
 
 libgncmod_csv_import_la_LDFLAGS = -avoid-version
diff --git a/src/import-export/csv-imp/gnc-tokenizer.cpp b/src/import-export/csv-imp/gnc-tokenizer.cpp
new file mode 100644
index 0000000..302d393
--- /dev/null
+++ b/src/import-export/csv-imp/gnc-tokenizer.cpp
@@ -0,0 +1,90 @@
+#include "gnc-tokenizer.hpp"
+
+#include <iostream>
+#include <fstream>      // fstream
+#include <vector>
+#include <string>
+#include <algorithm>    // copy
+#include <iterator>     // ostream_operator
+#include <memory>
+
+#include <boost/locale.hpp>
+
+extern "C" {
+#include <goffice/go-glib-extras.h>
+}
+
+std::unique_ptr<GncTokenizer> GncTokenizerFactory(GncImpFileFormat fmt)
+{
+    std::unique_ptr<GncTokenizer> tok(nullptr);
+    switch (fmt)
+    {
+    default:
+        break;
+    }
+
+    return tok;
+}
+
+void
+GncTokenizer::load_file(const std::string& path)
+{
+    if (path.empty())
+        return;
+
+    imp_file_str = path;
+
+    std::ifstream in;
+    in.exceptions ( std::ifstream::failbit | std::ifstream::badbit );
+    in.open (imp_file_str.c_str(), std::ios::in | std::ios::binary);
+
+    raw_contents.clear();
+    in.seekg(0, std::ios::end);
+    raw_contents.resize(in.tellg());
+    in.seekg(0, std::ios::beg);
+    in.read(&raw_contents[0], raw_contents.size());
+    in.close();
+
+    // Guess encoding, user can override if needed later on.
+    const char *guessed_enc = NULL;
+    guessed_enc = go_guess_encoding (raw_contents.c_str(),
+                                     raw_contents.length(),
+                                     enc_str.empty() ? "UTF-8" : enc_str.c_str(),
+                                     NULL);
+    if (guessed_enc)
+        this->encoding(guessed_enc);
+    else
+        enc_str.clear();
+
+}
+
+std::string
+GncTokenizer::current_file()
+{
+    return imp_file_str;
+}
+
+void
+GncTokenizer::encoding(const std::string& encoding)
+{
+    enc_str = encoding;
+    utf8_contents = boost::locale::conv::to_utf<char>(raw_contents, enc_str);
+}
+
+std::string
+GncTokenizer::encoding()
+{
+    return enc_str;
+}
+
+
+int GncTokenizer::tokenize()
+{
+	return 0;
+}
+
+
+std::vector<str_vec> GncTokenizer::get_tokens()
+{
+    return tokenized_contents;
+}
diff --git a/src/import-export/csv-imp/gnc-tokenizer.hpp b/src/import-export/csv-imp/gnc-tokenizer.hpp
new file mode 100644
index 0000000..037f6fd
--- /dev/null
+++ b/src/import-export/csv-imp/gnc-tokenizer.hpp
@@ -0,0 +1,87 @@
+/********************************************************************\
+ * gnc-tokenizer.hpp - base class for converting a text file into a *
+ *                     two-dimensional vector of strings (table)    *
+ *                                                                  *
+ * 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 2 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, contact:                        *
+ *                                                                  *
+ * Free Software Foundation           Voice:  +1-617-542-5942       *
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
+\********************************************************************/
+
+/** @file
+     @brief Class convert a file into vector of string vectors.
+     This is a generic base class that holds the functionality common
+     to different specializations (eg a csv file parser, a fixed-width
+     file parser,...)
+     The child classes have to override the tokenize function to
+     create a full tokenizer class.
+     *
+     gnc-tokenizer.hpp
+     @author Copyright (c) 2015 Geert Janssens <geert at kobaltwit.be>
+ */
+
+#ifndef GNC_TOKENIZER_HPP
+#define GNC_TOKENIZER_HPP
+
+extern "C" {
+#include "config.h"
+}
+
+#include <iostream>
+#include <fstream>      // fstream
+#include <vector>
+#include <string>
+#include <memory>
+
+using str_vec = std::vector<std::string>;
+
+/** Enumeration for file formats supported by this importer. */
+enum class GncImpFileFormat {
+    UNKNOWN,
+};
+
+
+class GncTokenizer
+{
+public:
+    GncTokenizer() = default;                               // default constructor
+    GncTokenizer(const GncTokenizer&) = default;            // copy constructor
+    GncTokenizer& operator=(const GncTokenizer&) = default; // copy assignment
+    GncTokenizer(GncTokenizer&&) = default;                 // move constructor
+    GncTokenizer& operator=(GncTokenizer&&) = default;      // move assignment
+    virtual ~GncTokenizer() = default;                      // destructor
+
+    void load_file(const std::string& path);
+    std::string current_file();
+    void encoding(const std::string& encoding);
+    std::string encoding();
+    virtual int  tokenize();
+    std::vector<str_vec> get_tokens();
+    
+protected:
+    std::string utf8_contents;
+    std::vector<str_vec> tokenized_contents;
+
+private:
+    std::string imp_file_str;
+    std::string raw_contents;
+    std::string enc_str;
+};
+
+
+// Function to instantiate specializations of the GncTokenizer
+std::unique_ptr<GncTokenizer> GncTokenizerFactory(GncImpFileFormat fmt);
+
+#endif

commit 83da518763782f84e069bd23f2a2efe64f1fc51a
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Thu Jan 28 10:25:53 2016 +0100

    Whitespace and spelling corrections

diff --git a/lib/goffice/go-charmap-sel.c b/lib/goffice/go-charmap-sel.c
index 33eb744..7d6e244 100644
--- a/lib/goffice/go-charmap-sel.c
+++ b/lib/goffice/go-charmap-sel.c
@@ -37,181 +37,215 @@
 
 /* ------------------------------------------------------------------------- */
 
-typedef enum {
-	LG_ARABIC,
-	LG_BALTIC,
-	LG_CENTRAL_EUROPEAN,
-	LG_CHINESE,
-	LG_CYRILLIC,
-	LG_GREEK,
-	LG_HEBREW,
-	LG_INDIAN,
-	LG_JAPANESE,
-	LG_KOREAN,
-	LG_TURKISH,
-	LG_UNICODE,
-	LG_VIETNAMESE,
-	LG_WESTERN,
-	LG_OTHER,
-	LG_LAST
+typedef enum
+{
+    LG_ARABIC,
+    LG_BALTIC,
+    LG_CENTRAL_EUROPEAN,
+    LG_CHINESE,
+    LG_CYRILLIC,
+    LG_GREEK,
+    LG_HEBREW,
+    LG_INDIAN,
+    LG_JAPANESE,
+    LG_KOREAN,
+    LG_TURKISH,
+    LG_UNICODE,
+    LG_VIETNAMESE,
+    LG_WESTERN,
+    LG_OTHER,
+    LG_LAST
 } LanguageGroup;
 
-typedef struct {
-        char const *group_name;
-	LanguageGroup const lgroup;
-	/* Generated stuff follows.  */
-	char *collate_key;
+typedef struct
+{
+    char const *group_name;
+    LanguageGroup const lgroup;
+    /* Generated stuff follows.  */
+    char *collate_key;
 } LGroupInfo;
 
-static LGroupInfo lgroups[] = {
-	{N_("Arabic"), LG_ARABIC},
-	{N_("Baltic"), LG_BALTIC},
-	{N_("Central European"), LG_CENTRAL_EUROPEAN},
-	{N_("Chinese"), LG_CHINESE},
-	{N_("Cyrillic"), LG_CYRILLIC},
-	{N_("Greek"), LG_GREEK},
-	{N_("Hebrew"), LG_HEBREW},
-	{N_("Indian"), LG_INDIAN},
-	{N_("Japanese"), LG_JAPANESE},
-	{N_("Korean"), LG_KOREAN},
-	{N_("Turkish"), LG_TURKISH},
-	{N_("Unicode"), LG_UNICODE},
-	{N_("Vietnamese"), LG_VIETNAMESE},
-	{N_("Western"), LG_WESTERN},
-	{N_("Other"), LG_OTHER},
-	{NULL, LG_LAST}
-};
-
-static int
-lgroups_order (const void *_a, const void *_b)
+static LGroupInfo lgroups[] =
 {
-	const LGroupInfo *a = (const LGroupInfo *)_a;
-	const LGroupInfo *b = (const LGroupInfo *)_b;
+{ N_("Arabic"), LG_ARABIC },
+{ N_("Baltic"), LG_BALTIC },
+{ N_("Central European"), LG_CENTRAL_EUROPEAN },
+{ N_("Chinese"), LG_CHINESE },
+{ N_("Cyrillic"), LG_CYRILLIC },
+{ N_("Greek"), LG_GREEK },
+{ N_("Hebrew"), LG_HEBREW },
+{ N_("Indian"), LG_INDIAN },
+{ N_("Japanese"), LG_JAPANESE },
+{ N_("Korean"), LG_KOREAN },
+{ N_("Turkish"), LG_TURKISH },
+{ N_("Unicode"), LG_UNICODE },
+{ N_("Vietnamese"), LG_VIETNAMESE },
+{ N_("Western"), LG_WESTERN },
+{ N_("Other"), LG_OTHER },
+{ NULL, LG_LAST } };
+
+static int lgroups_order(const void *_a, const void *_b)
+{
+    const LGroupInfo *a = (const LGroupInfo *) _a;
+    const LGroupInfo *b = (const LGroupInfo *) _b;
 
-	return strcmp (a->collate_key, b->collate_key);
+    return strcmp(a->collate_key, b->collate_key);
 }
 
 /* ------------------------------------------------------------------------- */
 
-typedef enum {
-     CI_MINOR,
-     CI_MAJOR
+typedef enum
+{
+    CI_MINOR, CI_MAJOR
 } CharsetImportance;
 
-typedef struct {
-	gchar const *charset_title;
-	gchar const *aliases;
-	LanguageGroup const lgroup;
-	CharsetImportance const imp;
-	/* Generated stuff follows.  */
-	char *collate_key;
-	char *to_utf8_iconv_name, *from_utf8_iconv_name;
+typedef struct
+{
+    gchar const *charset_title;
+    gchar const *aliases;
+    LanguageGroup const lgroup;
+    CharsetImportance const imp;
+    /* Generated stuff follows.  */
+    char *collate_key;
+    char *to_utf8_iconv_name, *from_utf8_iconv_name;
 } CharsetInfo;
 
-static CharsetInfo charset_trans_array[] = {
-	{N_("Arabic (IBM-864)"),                  "IBM864",                LG_ARABIC, CI_MINOR},
-	{N_("Arabic (IBM-864-I)"),                "IBM864i",               LG_ARABIC, CI_MINOR},
-	{N_("Arabic (ISO-8859-6)"),               "ISO-8859-6",            LG_ARABIC, CI_MINOR},
-	{N_("Arabic (ISO-8859-6-E)"),             "ISO-8859-6-E",          LG_ARABIC, CI_MINOR},
-
-	{N_("Arabic (ISO-8859-6-I)"),             "ISO-8859-6-I",          LG_ARABIC, CI_MINOR},
-	{N_("Arabic (MacArabic)"),                "x-mac-arabic",          LG_ARABIC, CI_MINOR},
-	{N_("Arabic (Windows-1256)"),             "windows-1256",          LG_ARABIC, CI_MINOR},
-	{N_("Armenian (ARMSCII-8)"),              "armscii-8", 	           LG_OTHER, CI_MINOR},
-	{N_("Baltic (ISO-8859-13)"),              "ISO-8859-13",           LG_BALTIC, CI_MINOR},
-	{N_("Baltic (ISO-8859-4)"),               "ISO-8859-4",            LG_BALTIC, CI_MINOR},
-	{N_("Baltic (Windows-1257)"),             "windows-1257",          LG_BALTIC, CI_MINOR},
-	{N_("Celtic (ISO-8859-14)"),              "ISO-8859-14",           LG_OTHER, CI_MINOR},
-	{N_("Central European (IBM-852)"),        "IBM852",                LG_CENTRAL_EUROPEAN, CI_MINOR},
-	{N_("Central European (ISO-8859-2)"),     "ISO-8859-2",	           LG_CENTRAL_EUROPEAN, CI_MINOR},
-	{N_("Central European (MacCE)"),          "x-mac-ce",              LG_CENTRAL_EUROPEAN, CI_MINOR},
-	{N_("Central European (Windows-1250)"),   "windows-1250",          LG_CENTRAL_EUROPEAN, CI_MINOR},
-	{N_("Chinese Simplified (GB18030)"),      "gb18030",               LG_CHINESE, CI_MINOR},
-	{N_("Chinese Simplified (GB2312)"),       "GB2312",                LG_CHINESE, CI_MINOR},
-	{N_("Chinese Simplified (GBK)"),          "x-gbk",                 LG_CHINESE, CI_MINOR},
-	{N_("Chinese Simplified (HZ)"),           "HZ-GB-2312",	           LG_CHINESE, CI_MINOR},
-	{N_("Chinese Simplified (Windows-936)"),  "windows-936",           LG_CHINESE, CI_MINOR},
-	{N_("Chinese Traditional (Big5)"),        "Big5",                  LG_CHINESE, CI_MINOR},
-	{N_("Chinese Traditional (Big5-HKSCS)"),  "Big5-HKSCS",	           LG_CHINESE, CI_MINOR},
-	{N_("Chinese Traditional (EUC-TW)"),      "x-euc-tw",              LG_CHINESE, CI_MINOR},
-	{N_("Croatian (MacCroatian)"),            "x-mac-croatian",        LG_CENTRAL_EUROPEAN, CI_MINOR},
-	{N_("Cyrillic (IBM-855)"),                "IBM855",                LG_CYRILLIC, CI_MINOR},
-	{N_("Cyrillic (ISO-8859-5)"),             "ISO-8859-5",	           LG_CYRILLIC, CI_MINOR},
-	{N_("Cyrillic (ISO-IR-111)"),             "ISO-IR-111",	           LG_CYRILLIC, CI_MINOR},
-	{N_("Cyrillic (KOI8-R)"),                 "KOI8-R",                LG_CYRILLIC, CI_MINOR},
-	{N_("Cyrillic (MacCyrillic)"),            "x-mac-cyrillic",        LG_CYRILLIC, CI_MINOR},
-	{N_("Cyrillic (Windows-1251)"),           "windows-1251",          LG_CYRILLIC, CI_MINOR},
-	{N_("Russian (CP-866)"),                  "IBM866",                LG_CYRILLIC, CI_MINOR},
-	{N_("Ukrainian (KOI8-U)"),                "KOI8-U",                LG_CYRILLIC, CI_MINOR},
-	{N_("Ukrainian (MacUkrainian)"),          "x-mac-ukrainian",       LG_CYRILLIC, CI_MINOR},
-	{N_("English (ASCII)"),                   "ANSI_X3.4-1968#ASCII",  LG_WESTERN, CI_MAJOR},
-	{N_("Farsi (MacFarsi)"),                  "x-mac-farsi",           LG_OTHER, CI_MINOR},
-	{N_("Georgian (GEOSTD8)"),                "geostd8",               LG_OTHER, CI_MINOR},
-	{N_("Greek (ISO-8859-7)"),                "ISO-8859-7",            LG_GREEK, CI_MINOR},
-	{N_("Greek (MacGreek)"),                  "x-mac-greek",           LG_GREEK, CI_MINOR},
-	{N_("Greek (Windows-1253)"),              "windows-1253",          LG_GREEK, CI_MINOR},
-	{N_("Gujarati (MacGujarati)"),            "x-mac-gujarati",        LG_INDIAN, CI_MINOR},
-	{N_("Gurmukhi (MacGurmukhi)"),            "x-mac-gurmukhi",        LG_INDIAN, CI_MINOR},
-	{N_("Hebrew (IBM-862)"),                  "IBM862",                LG_HEBREW, CI_MINOR},
-	{N_("Hebrew (ISO-8859-8-E)"),             "ISO-8859-8-E",          LG_HEBREW, CI_MINOR},
-	{N_("Hebrew (ISO-8859-8-I)"),             "ISO-8859-8-I",          LG_HEBREW, CI_MINOR},
-	{N_("Hebrew (MacHebrew)"),                "x-mac-hebrew",          LG_HEBREW, CI_MINOR},
-	{N_("Hebrew (Windows-1255)"),             "windows-1255",          LG_HEBREW, CI_MINOR},
-	{N_("Hindi (MacDevanagari)"),             "x-mac-devanagari",      LG_INDIAN, CI_MINOR},
-	{N_("Icelandic (MacIcelandic)"),          "x-mac-icelandic",       LG_OTHER, CI_MINOR},
-	{N_("Japanese (EUC-JP)"),                 "EUC-JP",                LG_JAPANESE, CI_MINOR},
-	{N_("Japanese (ISO-2022-JP)"),            "ISO-2022-JP",           LG_JAPANESE, CI_MINOR},
-	{N_("Japanese (Shift_JIS)"),              "CP932",             LG_JAPANESE, CI_MINOR},
-	{N_("Korean (EUC-KR)"),                   "EUC-KR",                LG_KOREAN, CI_MINOR},
-	{N_("Korean (ISO-2022-KR)"),              "ISO-2022-KR",           LG_KOREAN, CI_MINOR},
-	{N_("Korean (JOHAB)"),                    "x-johab",               LG_KOREAN, CI_MINOR},
-	{N_("Korean (UHC)"),                      "x-windows-949",         LG_KOREAN, CI_MINOR},
-	{N_("Nordic (ISO-8859-10)"),              "ISO-8859-10",           LG_OTHER, CI_MINOR},
-	{N_("Romanian (MacRomanian)"),            "x-mac-romanian",        LG_OTHER, CI_MINOR},
-	{N_("Romanian (ISO-8859-16)"),            "ISO-8859-16",           LG_OTHER, CI_MINOR},
-	{N_("South European (ISO-8859-3)"),       "ISO-8859-3",            LG_OTHER, CI_MINOR},
-	{N_("Thai (TIS-620)"),                    "TIS-620",               LG_OTHER, CI_MINOR},
-	{N_("Turkish (IBM-857)"),                 "IBM857",                LG_TURKISH, CI_MINOR},
-	{N_("Turkish (ISO-8859-9)"),              "ISO-8859-9",            LG_TURKISH, CI_MINOR},
-	{N_("Turkish (MacTurkish)"),              "x-mac-turkish",         LG_TURKISH, CI_MINOR},
-	{N_("Turkish (Windows-1254)"),            "windows-1254",          LG_TURKISH, CI_MINOR},
-	{N_("Unicode (UTF-7)"),                   "UTF-7",                 LG_UNICODE, CI_MINOR},
-	{N_("Unicode (UTF-8)"),                   "UTF-8",                 LG_UNICODE, CI_MAJOR},
-	{N_("Unicode (UTF-16BE)"),                "UTF-16BE",              LG_UNICODE, CI_MINOR},
-	{N_("Unicode (UTF-16LE)"),                "UTF-16LE",              LG_UNICODE, CI_MINOR},
-	{N_("Unicode (UTF-32BE)"),                "UTF-32BE",              LG_UNICODE, CI_MINOR},
-	{N_("Unicode (UTF-32LE)"),                "UTF-32LE",              LG_UNICODE, CI_MINOR},
-	{N_("User Defined"),                      "x-user-defined",        LG_OTHER, CI_MINOR},
-	{N_("Vietnamese (TCVN)"),                 "x-viet-tcvn5712",       LG_VIETNAMESE, CI_MINOR},
-	{N_("Vietnamese (VISCII)"),               "VISCII",                LG_VIETNAMESE, CI_MINOR},
-	{N_("Vietnamese (VPS)"),                  "x-viet-vps",            LG_VIETNAMESE, CI_MINOR},
-	{N_("Vietnamese (Windows-1258)"),         "windows-1258",          LG_VIETNAMESE, CI_MINOR},
-	{N_("Visual Hebrew (ISO-8859-8)"),        "ISO-8859-8",            LG_HEBREW, CI_MINOR},
-	{N_("Western (IBM-850)"),                 "IBM850",                LG_WESTERN, CI_MINOR},
-	{N_("Western (ISO-8859-1)"),              "ISO-8859-1",            LG_WESTERN, CI_MAJOR},
-	{N_("Western (ISO-8859-15)"),             "ISO-8859-15",           LG_WESTERN, CI_MINOR},
-	{N_("Western (MacRoman)"),                "x-mac-roman",           LG_WESTERN, CI_MINOR},
-	{N_("Western (Windows-1252)"),            "windows-1252",          LG_WESTERN, CI_MINOR},
-	/* charsets without possibly translatable names */
-	{"T61.8bit",                              "T61.8bit",              LG_OTHER, CI_MINOR},
-	{"x-imap4-modified-utf7",                 "x-imap4-modified-utf7", LG_UNICODE, CI_MINOR},
-	{"x-u-escaped",                           "x-u-escaped",           LG_OTHER, CI_MINOR},
-	{NULL,                                    NULL,                    LG_LAST, 0}
-};
-
-static int
-charset_order (const void *_a, const void *_b)
+static CharsetInfo charset_trans_array[] =
+        {
+        { N_("Arabic (IBM-864)"), "IBM864", LG_ARABIC, CI_MINOR },
+        { N_("Arabic (IBM-864-I)"), "IBM864i", LG_ARABIC, CI_MINOR },
+        { N_("Arabic (ISO-8859-6)"), "ISO-8859-6", LG_ARABIC, CI_MINOR },
+        { N_("Arabic (ISO-8859-6-E)"), "ISO-8859-6-E", LG_ARABIC, CI_MINOR },
+
+        { N_("Arabic (ISO-8859-6-I)"), "ISO-8859-6-I", LG_ARABIC, CI_MINOR },
+        { N_("Arabic (MacArabic)"), "x-mac-arabic", LG_ARABIC, CI_MINOR },
+        { N_("Arabic (Windows-1256)"), "windows-1256", LG_ARABIC, CI_MINOR },
+        { N_("Armenian (ARMSCII-8)"), "armscii-8", LG_OTHER, CI_MINOR },
+        { N_("Baltic (ISO-8859-13)"), "ISO-8859-13", LG_BALTIC, CI_MINOR },
+        { N_("Baltic (ISO-8859-4)"), "ISO-8859-4", LG_BALTIC, CI_MINOR },
+        { N_("Baltic (Windows-1257)"), "windows-1257", LG_BALTIC, CI_MINOR },
+        { N_("Celtic (ISO-8859-14)"), "ISO-8859-14", LG_OTHER, CI_MINOR },
+        { N_("Central European (IBM-852)"), "IBM852", LG_CENTRAL_EUROPEAN,
+                CI_MINOR },
+        { N_("Central European (ISO-8859-2)"), "ISO-8859-2",
+                LG_CENTRAL_EUROPEAN, CI_MINOR },
+        { N_("Central European (MacCE)"), "x-mac-ce", LG_CENTRAL_EUROPEAN,
+                CI_MINOR },
+        { N_("Central European (Windows-1250)"), "windows-1250",
+                LG_CENTRAL_EUROPEAN, CI_MINOR },
+        { N_("Chinese Simplified (GB18030)"), "gb18030", LG_CHINESE, CI_MINOR },
+        { N_("Chinese Simplified (GB2312)"), "GB2312", LG_CHINESE, CI_MINOR },
+        { N_("Chinese Simplified (GBK)"), "x-gbk", LG_CHINESE, CI_MINOR },
+        { N_("Chinese Simplified (HZ)"), "HZ-GB-2312", LG_CHINESE, CI_MINOR },
+        { N_("Chinese Simplified (Windows-936)"), "windows-936", LG_CHINESE,
+                CI_MINOR },
+        { N_("Chinese Traditional (Big5)"), "Big5", LG_CHINESE, CI_MINOR },
+        { N_("Chinese Traditional (Big5-HKSCS)"), "Big5-HKSCS", LG_CHINESE,
+                CI_MINOR },
+                { N_("Chinese Traditional (EUC-TW)"), "x-euc-tw", LG_CHINESE,
+                        CI_MINOR },
+                { N_("Croatian (MacCroatian)"), "x-mac-croatian",
+                        LG_CENTRAL_EUROPEAN, CI_MINOR },
+                { N_("Cyrillic (IBM-855)"), "IBM855", LG_CYRILLIC, CI_MINOR },
+                { N_("Cyrillic (ISO-8859-5)"), "ISO-8859-5", LG_CYRILLIC,
+                        CI_MINOR },
+                { N_("Cyrillic (ISO-IR-111)"), "ISO-IR-111", LG_CYRILLIC,
+                        CI_MINOR },
+                { N_("Cyrillic (KOI8-R)"), "KOI8-R", LG_CYRILLIC, CI_MINOR },
+                { N_("Cyrillic (MacCyrillic)"), "x-mac-cyrillic", LG_CYRILLIC,
+                        CI_MINOR },
+                { N_("Cyrillic (Windows-1251)"), "windows-1251", LG_CYRILLIC,
+                        CI_MINOR },
+                { N_("Russian (CP-866)"), "IBM866", LG_CYRILLIC, CI_MINOR },
+                { N_("Ukrainian (KOI8-U)"), "KOI8-U", LG_CYRILLIC, CI_MINOR },
+                { N_("Ukrainian (MacUkrainian)"), "x-mac-ukrainian",
+                        LG_CYRILLIC, CI_MINOR },
+                { N_("English (ASCII)"), "ANSI_X3.4-1968#ASCII", LG_WESTERN,
+                        CI_MAJOR },
+                { N_("Farsi (MacFarsi)"), "x-mac-farsi", LG_OTHER, CI_MINOR },
+                { N_("Georgian (GEOSTD8)"), "geostd8", LG_OTHER, CI_MINOR },
+                { N_("Greek (ISO-8859-7)"), "ISO-8859-7", LG_GREEK, CI_MINOR },
+                { N_("Greek (MacGreek)"), "x-mac-greek", LG_GREEK, CI_MINOR },
+                { N_("Greek (Windows-1253)"), "windows-1253", LG_GREEK, CI_MINOR },
+                { N_("Gujarati (MacGujarati)"), "x-mac-gujarati", LG_INDIAN,
+                        CI_MINOR },
+                { N_("Gurmukhi (MacGurmukhi)"), "x-mac-gurmukhi", LG_INDIAN,
+                        CI_MINOR },
+                { N_("Hebrew (IBM-862)"), "IBM862", LG_HEBREW, CI_MINOR },
+                { N_("Hebrew (ISO-8859-8-E)"), "ISO-8859-8-E", LG_HEBREW,
+                        CI_MINOR },
+                { N_("Hebrew (ISO-8859-8-I)"), "ISO-8859-8-I", LG_HEBREW,
+                        CI_MINOR },
+                { N_("Hebrew (MacHebrew)"), "x-mac-hebrew", LG_HEBREW, CI_MINOR },
+                { N_("Hebrew (Windows-1255)"), "windows-1255", LG_HEBREW,
+                        CI_MINOR },
+                { N_("Hindi (MacDevanagari)"), "x-mac-devanagari", LG_INDIAN,
+                        CI_MINOR },
+                { N_("Icelandic (MacIcelandic)"), "x-mac-icelandic", LG_OTHER,
+                        CI_MINOR },
+                { N_("Japanese (EUC-JP)"), "EUC-JP", LG_JAPANESE, CI_MINOR },
+                { N_("Japanese (ISO-2022-JP)"), "ISO-2022-JP", LG_JAPANESE,
+                        CI_MINOR },
+                { N_("Japanese (Shift_JIS)"), "CP932", LG_JAPANESE, CI_MINOR },
+                { N_("Korean (EUC-KR)"), "EUC-KR", LG_KOREAN, CI_MINOR },
+                { N_("Korean (ISO-2022-KR)"), "ISO-2022-KR", LG_KOREAN, CI_MINOR },
+                { N_("Korean (JOHAB)"), "x-johab", LG_KOREAN, CI_MINOR },
+                { N_("Korean (UHC)"), "x-windows-949", LG_KOREAN, CI_MINOR },
+                { N_("Nordic (ISO-8859-10)"), "ISO-8859-10", LG_OTHER, CI_MINOR },
+                { N_("Romanian (MacRomanian)"), "x-mac-romanian", LG_OTHER,
+                        CI_MINOR },
+                { N_("Romanian (ISO-8859-16)"), "ISO-8859-16", LG_OTHER,
+                        CI_MINOR },
+                { N_("South European (ISO-8859-3)"), "ISO-8859-3", LG_OTHER,
+                        CI_MINOR },
+                { N_("Thai (TIS-620)"), "TIS-620", LG_OTHER, CI_MINOR },
+                { N_("Turkish (IBM-857)"), "IBM857", LG_TURKISH, CI_MINOR },
+                { N_("Turkish (ISO-8859-9)"), "ISO-8859-9", LG_TURKISH, CI_MINOR },
+                { N_("Turkish (MacTurkish)"), "x-mac-turkish", LG_TURKISH,
+                        CI_MINOR },
+                { N_("Turkish (Windows-1254)"), "windows-1254", LG_TURKISH,
+                        CI_MINOR },
+                { N_("Unicode (UTF-7)"), "UTF-7", LG_UNICODE, CI_MINOR },
+                { N_("Unicode (UTF-8)"), "UTF-8", LG_UNICODE, CI_MAJOR },
+                { N_("Unicode (UTF-16BE)"), "UTF-16BE", LG_UNICODE, CI_MINOR },
+                { N_("Unicode (UTF-16LE)"), "UTF-16LE", LG_UNICODE, CI_MINOR },
+                { N_("Unicode (UTF-32BE)"), "UTF-32BE", LG_UNICODE, CI_MINOR },
+                { N_("Unicode (UTF-32LE)"), "UTF-32LE", LG_UNICODE, CI_MINOR },
+                { N_("User Defined"), "x-user-defined", LG_OTHER, CI_MINOR },
+                { N_("Vietnamese (TCVN)"), "x-viet-tcvn5712", LG_VIETNAMESE,
+                        CI_MINOR },
+                { N_("Vietnamese (VISCII)"), "VISCII", LG_VIETNAMESE, CI_MINOR },
+                { N_("Vietnamese (VPS)"), "x-viet-vps", LG_VIETNAMESE, CI_MINOR },
+                { N_("Vietnamese (Windows-1258)"), "windows-1258",
+                        LG_VIETNAMESE, CI_MINOR },
+                { N_("Visual Hebrew (ISO-8859-8)"), "ISO-8859-8", LG_HEBREW,
+                        CI_MINOR },
+                { N_("Western (IBM-850)"), "IBM850", LG_WESTERN, CI_MINOR },
+                { N_("Western (ISO-8859-1)"), "ISO-8859-1", LG_WESTERN, CI_MAJOR },
+                { N_("Western (ISO-8859-15)"), "ISO-8859-15", LG_WESTERN,
+                        CI_MINOR },
+                { N_("Western (MacRoman)"), "x-mac-roman", LG_WESTERN, CI_MINOR },
+                { N_("Western (Windows-1252)"), "windows-1252", LG_WESTERN,
+                        CI_MINOR },
+                /* charsets without possibly translatable names */
+                { "T61.8bit", "T61.8bit", LG_OTHER, CI_MINOR },
+                { "x-imap4-modified-utf7", "x-imap4-modified-utf7", LG_UNICODE,
+                        CI_MINOR },
+                { "x-u-escaped", "x-u-escaped", LG_OTHER, CI_MINOR },
+                { NULL, NULL, LG_LAST, 0 } };
+
+static int charset_order(const void *_a, const void *_b)
 {
-	const CharsetInfo *a = (const CharsetInfo *)_a;
-	const CharsetInfo *b = (const CharsetInfo *)_b;
+    const CharsetInfo *a = (const CharsetInfo *) _a;
+    const CharsetInfo *b = (const CharsetInfo *) _b;
 
-	if (a->lgroup != b->lgroup)
-		return (int)b->lgroup - (int)a->lgroup;
+    if (a->lgroup != b->lgroup)
+        return (int) b->lgroup - (int) a->lgroup;
 
-	if (a->imp != b->imp)
-		return (int)b->imp - (int)a->imp;
+    if (a->imp != b->imp)
+        return (int) b->imp - (int) a->imp;
 
-	return strcmp (a->collate_key, b->collate_key);
+    return strcmp(a->collate_key, b->collate_key);
 }
 
 /* ------------------------------------------------------------------------- */
@@ -219,468 +253,466 @@ charset_order (const void *_a, const void *_b)
 /* name -> CharsetInfo* mapping */
 static GHashTable *encoding_hash;
 
-struct _GOCharmapSel {
-	GtkHBox box;
-	GOOptionMenu *encodings;
-	GtkMenu *encodings_menu;
-	GOCharmapSelTestDirection test;
+struct _GOCharmapSel
+{
+    GtkHBox box;
+    GOOptionMenu *encodings;
+    GtkMenu *encodings_menu;
+    GOCharmapSelTestDirection test;
 };
 
-typedef struct {
-	GtkHBoxClass parent_class;
+typedef struct
+{
+    GtkHBoxClass parent_class;
 
-	gboolean (* charmap_changed) (GOCharmapSel *cs, char const *new_charmap);
+    gboolean (*charmap_changed)(GOCharmapSel *cs, char const *new_charmap);
 } GOCharmapSelClass;
 
-
 typedef GOCharmapSel Cs;
 typedef GOCharmapSelClass CsClass;
 
 /* Signals we emit */
-enum {
-	CHARMAP_CHANGED,
-	LAST_SIGNAL
+enum
+{
+    CHARMAP_CHANGED, LAST_SIGNAL
 };
 
-enum {
-	PROP_0,
-	PROP_TEST_DIRECTION
+enum
+{
+    PROP_0, PROP_TEST_DIRECTION
 };
 
+static guint cs_signals[LAST_SIGNAL] =
+{ 0 };
 
+static void cs_set_property(GObject *object, guint prop_id, const GValue *value,
+        GParamSpec *pspec);
 
+static void cs_get_property(GObject *object, guint prop_id, GValue *value,
+        GParamSpec *pspec);
 
-static guint cs_signals[LAST_SIGNAL] = { 0 };
-
-static void cs_set_property      (GObject          *object,
-				  guint             prop_id,
-				  const GValue     *value,
-				  GParamSpec       *pspec);
-
-static void cs_get_property      (GObject          *object,
-				  guint             prop_id,
-				  GValue           *value,
-				  GParamSpec       *pspec);
-
-static gboolean
-iconv_supported (const char *to, const char *from)
+static gboolean iconv_supported(const char *to, const char *from)
 {
-	GIConv ic = g_iconv_open (to, from);
-	if (ic == NULL || ic == (GIConv)-1)
-		return FALSE;
+    GIConv ic = g_iconv_open(to, from);
+    if (ic == NULL || ic == (GIConv) -1)
+        return FALSE;
 
-	g_iconv_close (ic);
-	return TRUE;
+    g_iconv_close(ic);
+    return TRUE;
 }
 
 const char *
 go_charmap_sel_get_encoding_name (G_GNUC_UNUSED GOCharmapSel *cs,
-				  const char *encoding)
+        const char *encoding)
 {
-	CharsetInfo const *ci;
+    CharsetInfo const *ci;
 
-	g_return_val_if_fail (encoding != NULL, NULL);
+    g_return_val_if_fail (encoding != NULL, NULL);
 
-	ci = g_hash_table_lookup (encoding_hash, encoding);
-	return ci ? _(ci->charset_title) : NULL;
+    ci = g_hash_table_lookup (encoding_hash, encoding);
+    return ci ? _(ci->charset_title) : NULL;
 }
 
 static char const *
-get_locale_encoding_name (GOCharmapSel *cs)
+get_locale_encoding_name(GOCharmapSel *cs)
 {
-	char const *locale_encoding;
-	char const *name;
+    char const *locale_encoding;
+    char const *name;
 
-	g_get_charset (&locale_encoding);
-	name = go_charmap_sel_get_encoding_name (cs, locale_encoding);
-	return name ? name : locale_encoding;
+    g_get_charset(&locale_encoding);
+    name = go_charmap_sel_get_encoding_name(cs, locale_encoding);
+    return name ? name : locale_encoding;
 }
 
-static void
-encodings_changed_cb (GOOptionMenu *optionmenu, GOCharmapSel *cs)
+static void encodings_changed_cb(GOOptionMenu *optionmenu, GOCharmapSel *cs)
 {
-	g_return_if_fail (GO_IS_CHARMAP_SEL (cs));
-	g_return_if_fail (optionmenu == cs->encodings);
+    g_return_if_fail(GO_IS_CHARMAP_SEL(cs));
+    g_return_if_fail(optionmenu == cs->encodings);
 
-	g_signal_emit (G_OBJECT (cs),
-		       cs_signals[CHARMAP_CHANGED],
-		       0,
-		       go_charmap_sel_get_encoding (cs));
+    g_signal_emit(G_OBJECT(cs), cs_signals[CHARMAP_CHANGED], 0,
+            go_charmap_sel_get_encoding(cs));
 }
 
-static void
-set_menu_to_default (GOCharmapSel *cs, gint item)
+static void set_menu_to_default(GOCharmapSel *cs, gint item)
 {
-	GSList sel = { GINT_TO_POINTER (item - 1), NULL};
+    GSList sel =
+    { GINT_TO_POINTER(item - 1), NULL };
 
-	g_return_if_fail (cs != NULL && GO_IS_CHARMAP_SEL (cs));
+    g_return_if_fail(cs != NULL && GO_IS_CHARMAP_SEL(cs));
 
-	go_option_menu_set_history (cs->encodings, &sel);
+    go_option_menu_set_history(cs->encodings, &sel);
 }
 
-static gboolean
-cs_mnemonic_activate (GtkWidget *w, gboolean group_cycling)
+static gboolean cs_mnemonic_activate(GtkWidget *w, gboolean group_cycling)
 {
-	GOCharmapSel *cs = GO_CHARMAP_SEL (w);
-	gtk_widget_grab_focus (GTK_WIDGET (cs->encodings));
-	return TRUE;
+    GOCharmapSel *cs = GO_CHARMAP_SEL(w);
+    gtk_widget_grab_focus(GTK_WIDGET(cs->encodings));
+    return TRUE;
 }
 
-static void
-cs_emphasize_label (GtkLabel *label)
+static void cs_emphasize_label(GtkLabel *label)
 {
-	char *text = g_markup_printf_escaped ("<b>%s</b>",
-					      gtk_label_get_label (label));
-	gtk_label_set_use_markup (label, TRUE);
-	gtk_label_set_label (label, text);
-	g_free (text);
+    char *text = g_markup_printf_escaped("<b>%s</b>",
+            gtk_label_get_label(label));
+    gtk_label_set_use_markup(label, TRUE);
+    gtk_label_set_label(label, text);
+    g_free(text);
 }
 
-static void
-cs_init (GOCharmapSel *cs)
+static void cs_init(GOCharmapSel *cs)
 {
-	cs->test = GO_CHARMAP_SEL_TO_UTF8;
+    cs->test = GO_CHARMAP_SEL_TO_UTF8;
 
-	cs->encodings = GO_OPTION_MENU (go_option_menu_new ());
+    cs->encodings = GO_OPTION_MENU(go_option_menu_new());
 
-	g_signal_connect (G_OBJECT (cs->encodings), "changed",
-                          G_CALLBACK (encodings_changed_cb), cs);
-        gtk_box_pack_start (GTK_BOX (cs), GTK_WIDGET (cs->encodings),
-                            TRUE, TRUE, 0);
+    g_signal_connect(G_OBJECT(cs->encodings), "changed",
+            G_CALLBACK(encodings_changed_cb), cs);
+    gtk_box_pack_start(GTK_BOX(cs), GTK_WIDGET(cs->encodings), TRUE, TRUE, 0);
 }
 
-
-static void
-cs_build_menu (GOCharmapSel *cs)
+static void cs_build_menu(GOCharmapSel *cs)
 {
-        GtkWidget *item;
-	GtkMenu *menu;
-	LGroupInfo const *lgroup = lgroups;
-	gint lg_cnt = 0;
-
-        menu = GTK_MENU (gtk_menu_new ());
-
-	while (lgroup->group_name) {
-		CharsetInfo const *charset_trans;
-		GtkMenu *submenu = NULL;
-
-		charset_trans = charset_trans_array;
-
-		while (charset_trans->lgroup != LG_LAST) {
-			GtkWidget *subitem;
-			if (charset_trans->lgroup == lgroup->lgroup) {
-				const char *name = (cs->test == GO_CHARMAP_SEL_TO_UTF8)
-					? charset_trans->to_utf8_iconv_name
-					: charset_trans->from_utf8_iconv_name;
-				if (name) {
-					if (!submenu)
-						submenu = GTK_MENU (gtk_menu_new ());
-					subitem = gtk_check_menu_item_new_with_label
-						(_(charset_trans->charset_title));
-					gtk_check_menu_item_set_draw_as_radio (GTK_CHECK_MENU_ITEM (subitem), TRUE);
-					gtk_widget_show (subitem);
-					gtk_menu_shell_append (GTK_MENU_SHELL (submenu),  subitem);
-					if (charset_trans->imp == CI_MAJOR)
-						cs_emphasize_label (GTK_LABEL (gtk_bin_get_child (GTK_BIN (subitem))));
-					g_object_set_data (G_OBJECT (subitem), CHARMAP_NAME_KEY,
-							   (gpointer)name);
-				} else if (0) {
-					g_print ("Unsupported: %s\n", charset_trans->aliases);
-				}
-			}
-			charset_trans++;
-		}
-		if (submenu) {
-			GtkWidget *item =
-				gtk_menu_item_new_with_label (_(lgroup->group_name));
-
-			gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), GTK_WIDGET (submenu));
-			gtk_widget_show (item);
-			gtk_menu_shell_append (GTK_MENU_SHELL (menu),  item);
-			lg_cnt++;
-		}
-                lgroup++;
+    GtkWidget *item;
+    GtkMenu *menu;
+    LGroupInfo const *lgroup = lgroups;
+    gint lg_cnt = 0;
+
+    menu = GTK_MENU(gtk_menu_new());
+
+    while (lgroup->group_name)
+    {
+        CharsetInfo const *charset_trans;
+        GtkMenu *submenu = NULL;
+
+        charset_trans = charset_trans_array;
+
+        while (charset_trans->lgroup != LG_LAST)
+        {
+            GtkWidget *subitem;
+            if (charset_trans->lgroup == lgroup->lgroup)
+            {
+                const char *name =
+                        (cs->test == GO_CHARMAP_SEL_TO_UTF8) ?
+                                charset_trans->to_utf8_iconv_name :
+                                charset_trans->from_utf8_iconv_name;
+                if (name)
+                {
+                    if (!submenu)
+                        submenu = GTK_MENU(gtk_menu_new());
+                    subitem = gtk_check_menu_item_new_with_label(
+                            _(charset_trans->charset_title));
+                    gtk_check_menu_item_set_draw_as_radio(
+                            GTK_CHECK_MENU_ITEM(subitem), TRUE);
+                    gtk_widget_show(subitem);
+                    gtk_menu_shell_append(GTK_MENU_SHELL(submenu), subitem);
+                    if (charset_trans->imp == CI_MAJOR)
+                        cs_emphasize_label(
+                                GTK_LABEL(gtk_bin_get_child(GTK_BIN(subitem))));
+                    g_object_set_data(G_OBJECT(subitem), CHARMAP_NAME_KEY,
+                            (gpointer) name);
+                }
+                else if (0)
+                {
+                    g_print("Unsupported: %s\n", charset_trans->aliases);
+                }
+            }
+            charset_trans++;
         }
-	item = gtk_separator_menu_item_new ();
-	gtk_widget_show (item);
-	gtk_menu_shell_append (GTK_MENU_SHELL (menu),  item);
-	lg_cnt++;
-
-	{
-		char *locale_encoding_menu_title = g_strconcat (_("Locale: "),
-							      get_locale_encoding_name (cs),
-							      NULL);
-		item = gtk_check_menu_item_new_with_label (locale_encoding_menu_title);
-		gtk_check_menu_item_set_draw_as_radio (GTK_CHECK_MENU_ITEM (item), TRUE);
-		g_free (locale_encoding_menu_title);
-		gtk_widget_show (item);
-		gtk_menu_shell_append (GTK_MENU_SHELL (menu),  item);
-		lg_cnt++;
-		cs_emphasize_label (GTK_LABEL (gtk_bin_get_child (GTK_BIN (item))));
-	}
-
-	go_option_menu_set_menu (cs->encodings, GTK_WIDGET (menu));
-	cs->encodings_menu = menu;
-	set_menu_to_default (cs, lg_cnt);
+        if (submenu)
+        {
+            GtkWidget *item = gtk_menu_item_new_with_label(
+                    _(lgroup->group_name));
+
+            gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), GTK_WIDGET(submenu));
+            gtk_widget_show(item);
+            gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+            lg_cnt++;
+        }
+        lgroup++;
+    }
+    item = gtk_separator_menu_item_new();
+    gtk_widget_show(item);
+    gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+    lg_cnt++;
+
+    {
+        char *locale_encoding_menu_title = g_strconcat(_("Locale: "),
+                get_locale_encoding_name(cs),
+                NULL);
+        item = gtk_check_menu_item_new_with_label(locale_encoding_menu_title);
+        gtk_check_menu_item_set_draw_as_radio(GTK_CHECK_MENU_ITEM(item), TRUE);
+        g_free(locale_encoding_menu_title);
+        gtk_widget_show(item);
+        gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+        lg_cnt++;
+        cs_emphasize_label(GTK_LABEL(gtk_bin_get_child(GTK_BIN(item))));
+    }
+
+    go_option_menu_set_menu(cs->encodings, GTK_WIDGET(menu));
+    cs->encodings_menu = menu;
+    set_menu_to_default(cs, lg_cnt);
 }
 
 static void cs_class_init(GtkWidgetClass *widget_klass)
 {
-	CharsetInfo *ci;
-	size_t i;
-
-	GObjectClass *gobject_class = G_OBJECT_CLASS (widget_klass);
-	widget_klass->mnemonic_activate = cs_mnemonic_activate;
-
-	gobject_class->set_property = cs_set_property;
-	gobject_class->get_property = cs_get_property;
-
-	cs_signals[CHARMAP_CHANGED] =
-		g_signal_new ("charmap_changed",
-			      GO_TYPE_CHARMAP_SEL,
-			      G_SIGNAL_RUN_LAST,
-			      G_STRUCT_OFFSET (GOCharmapSelClass, charmap_changed),
-			      NULL, NULL,
-			      g_cclosure_marshal_VOID__POINTER,
-			      G_TYPE_NONE, 1, G_TYPE_POINTER);
-
-	g_object_class_install_property (gobject_class,
-					 PROP_TEST_DIRECTION,
-					 g_param_spec_uint ("TestDirection",
-							    _("Conversion Direction"),
-							    _("This value determines which iconv test to perform."),
-							    (guint)GO_CHARMAP_SEL_TO_UTF8,
-							    (guint)GO_CHARMAP_SEL_FROM_UTF8,
-							    (guint)GO_CHARMAP_SEL_TO_UTF8,
-							    G_PARAM_READWRITE));
-
-	/* ---------------------------------------- */
-	/* Sort the groups by translated name.  */
-
-	for (i = 0; i < G_N_ELEMENTS (lgroups) - 2; i++) {
-		const char *cgroup_name = lgroups[i].group_name;
-		const char *group_name = _(cgroup_name);
-		lgroups[i].collate_key = g_utf8_collate_key (group_name, -1);
-		if (!lgroups[i].collate_key) {
-			g_warning ("Failed to generate collation key for [%s] [%s]",
-				   cgroup_name, group_name);
-			lgroups[i].collate_key = g_strdup (group_name);
-		}
-	}
-	qsort (lgroups, G_N_ELEMENTS (lgroups) - 2, sizeof (lgroups[0]),
-	       lgroups_order);
-	for (i = 0; i < G_N_ELEMENTS (lgroups) - 2; i++) {
-		g_free (lgroups[i].collate_key);
-		lgroups[i].collate_key = NULL;
-	}
-
-	/* ---------------------------------------- */
-	/* Sort charsets by group/importance/title.  */
-
-	for (i = 0; i < G_N_ELEMENTS (charset_trans_array) - 1; i++) {
-		const char *ctitle = charset_trans_array[i].charset_title;
-		const char *title = _(ctitle);
-		charset_trans_array[i].collate_key = g_utf8_collate_key (title, -1);
-		if (!charset_trans_array[i].collate_key) {
-			g_warning ("Failed to generate collation key for [%s] [%s]",
-				   ctitle, title);
-			charset_trans_array[i].collate_key = g_strdup (title);
-		}
-	}
-	qsort (charset_trans_array, G_N_ELEMENTS (charset_trans_array) - 1,
-	       sizeof (charset_trans_array[0]), charset_order);
-	for (i = 0; i < G_N_ELEMENTS (charset_trans_array) - 1; i++) {
-		g_free (charset_trans_array[i].collate_key);
-		charset_trans_array[i].collate_key = NULL;
-	}
-
-	/* ---------------------------------------- */
-
-	encoding_hash =
-		g_hash_table_new_full (go_ascii_strcase_hash,
-				       go_ascii_strcase_equal,
-				       (GDestroyNotify)g_free,
-				       NULL);
-
-	for (ci = charset_trans_array; ci->charset_title; ci++) {
-		const char *aliases = ci->aliases;
-		char *autoaliases = NULL;
-
-		if (strchr (aliases, '#') == NULL) {
-			/* Sigh.  This sucks quite a lot.  */
-			if (strncmp (aliases, "ISO-", 4) == 0) {
-				autoaliases =
-					g_strconcat (aliases,
-						     "#ISO", aliases + 4,
-						     "#ISO_", aliases + 4,
-						     NULL);
-			}
-
-			if (autoaliases)
-				aliases = autoaliases;
-		}
-
-		ci->to_utf8_iconv_name = ci->from_utf8_iconv_name = NULL;
-		while (aliases) {
-			const char *sep = strchr (aliases, '#');
-			char *alias;
-
-			if (sep) {
-				alias = g_strndup (aliases, sep - aliases);
-				aliases = sep + 1;
-			} else {
-				alias = g_strdup (aliases);
-				aliases = NULL;
-			}
-
-			if (ci->to_utf8_iconv_name == NULL &&
-			    iconv_supported ("UTF-8", alias)) {
-				ci->to_utf8_iconv_name = g_strdup (alias);
-			}
-
-			if (ci->from_utf8_iconv_name == NULL &&
-			    iconv_supported (alias, "UTF-8")) {
-				ci->from_utf8_iconv_name = g_strdup (alias);
-			}
-
-			g_hash_table_insert (encoding_hash, alias, ci);
-		}
-
-		g_free (autoaliases);
-	}
+    CharsetInfo *ci;
+    size_t i;
+
+    GObjectClass *gobject_class = G_OBJECT_CLASS(widget_klass);
+    widget_klass->mnemonic_activate = cs_mnemonic_activate;
+
+    gobject_class->set_property = cs_set_property;
+    gobject_class->get_property = cs_get_property;
+
+    cs_signals[CHARMAP_CHANGED] = g_signal_new("charmap_changed",
+    GO_TYPE_CHARMAP_SEL, G_SIGNAL_RUN_LAST,
+            G_STRUCT_OFFSET(GOCharmapSelClass, charmap_changed),
+            NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+            G_TYPE_POINTER);
+
+    g_object_class_install_property(gobject_class, PROP_TEST_DIRECTION,
+            g_param_spec_uint("TestDirection", _("Conversion Direction"),
+                    _("This value determines which iconv test to perform."),
+                    (guint) GO_CHARMAP_SEL_TO_UTF8,
+                    (guint) GO_CHARMAP_SEL_FROM_UTF8,
+                    (guint) GO_CHARMAP_SEL_TO_UTF8, G_PARAM_READWRITE));
+
+    /* ---------------------------------------- */
+    /* Sort the groups by translated name.  */
+
+    for (i = 0; i < G_N_ELEMENTS(lgroups) - 2; i++)
+    {
+        const char *cgroup_name = lgroups[i].group_name;
+        const char *group_name = _(cgroup_name);
+        lgroups[i].collate_key = g_utf8_collate_key(group_name, -1);
+        if (!lgroups[i].collate_key)
+        {
+            g_warning("Failed to generate collation key for [%s] [%s]",
+                    cgroup_name, group_name);
+            lgroups[i].collate_key = g_strdup(group_name);
+        }
+    }
+    qsort(lgroups, G_N_ELEMENTS(lgroups) - 2, sizeof(lgroups[0]),
+            lgroups_order);
+    for (i = 0; i < G_N_ELEMENTS(lgroups) - 2; i++)
+    {
+        g_free(lgroups[i].collate_key);
+        lgroups[i].collate_key = NULL;
+    }
+
+    /* ---------------------------------------- */
+    /* Sort charsets by group/importance/title.  */
+
+    for (i = 0; i < G_N_ELEMENTS(charset_trans_array) - 1; i++)
+    {
+        const char *ctitle = charset_trans_array[i].charset_title;
+        const char *title = _(ctitle);
+        charset_trans_array[i].collate_key = g_utf8_collate_key(title, -1);
+        if (!charset_trans_array[i].collate_key)
+        {
+            g_warning("Failed to generate collation key for [%s] [%s]", ctitle,
+                    title);
+            charset_trans_array[i].collate_key = g_strdup(title);
+        }
+    }
+    qsort(charset_trans_array, G_N_ELEMENTS(charset_trans_array) - 1,
+            sizeof(charset_trans_array[0]), charset_order);
+    for (i = 0; i < G_N_ELEMENTS(charset_trans_array) - 1; i++)
+    {
+        g_free(charset_trans_array[i].collate_key);
+        charset_trans_array[i].collate_key = NULL;
+    }
+
+    /* ---------------------------------------- */
+
+    encoding_hash = g_hash_table_new_full(go_ascii_strcase_hash,
+            go_ascii_strcase_equal, (GDestroyNotify) g_free,
+            NULL);
+
+    for (ci = charset_trans_array; ci->charset_title; ci++)
+    {
+        const char *aliases = ci->aliases;
+        char *autoaliases = NULL;
+
+        if (strchr(aliases, '#') == NULL)
+        {
+            /* Sigh.  This sucks quite a lot.  */
+            if (strncmp(aliases, "ISO-", 4) == 0)
+            {
+                autoaliases = g_strconcat(aliases, "#ISO", aliases + 4, "#ISO_",
+                        aliases + 4,
+                        NULL);
+            }
+
+            if (autoaliases)
+                aliases = autoaliases;
+        }
+
+        ci->to_utf8_iconv_name = ci->from_utf8_iconv_name = NULL;
+        while (aliases)
+        {
+            const char *sep = strchr(aliases, '#');
+            char *alias;
+
+            if (sep)
+            {
+                alias = g_strndup(aliases, sep - aliases);
+                aliases = sep + 1;
+            }
+            else
+            {
+                alias = g_strdup(aliases);
+                aliases = NULL;
+            }
+
+            if (ci->to_utf8_iconv_name == NULL
+                    && iconv_supported("UTF-8", alias))
+            {
+                ci->to_utf8_iconv_name = g_strdup(alias);
+            }
+
+            if (ci->from_utf8_iconv_name == NULL
+                    && iconv_supported(alias, "UTF-8"))
+            {
+                ci->from_utf8_iconv_name = g_strdup(alias);
+            }
+
+            g_hash_table_insert(encoding_hash, alias, ci);
+        }
+
+        g_free(autoaliases);
+    }
 }
 
 GtkWidget *
-go_charmap_sel_new (GOCharmapSelTestDirection test)
+go_charmap_sel_new(GOCharmapSelTestDirection test)
 {
-	return g_object_new (GO_TYPE_CHARMAP_SEL, "TestDirection", test, NULL);
+    return g_object_new(GO_TYPE_CHARMAP_SEL, "TestDirection", test, NULL);
 }
 
 gchar const *
-go_charmap_sel_get_encoding (GOCharmapSel *cs)
+go_charmap_sel_get_encoding(GOCharmapSel *cs)
 {
-	GtkMenuItem *selection;
-	char const *locale_encoding;
-	char const *encoding;
+    GtkMenuItem *selection;
+    char const *locale_encoding;
+    char const *encoding;
 
-	g_get_charset (&locale_encoding);
+    g_get_charset(&locale_encoding);
 
- 	g_return_val_if_fail (GO_IS_CHARMAP_SEL (cs), locale_encoding);
+    g_return_val_if_fail(GO_IS_CHARMAP_SEL(cs), locale_encoding);
 
- 	selection = GTK_MENU_ITEM (go_option_menu_get_history (cs->encodings));
-	encoding = (char const *) g_object_get_data (G_OBJECT (selection),
-						     CHARMAP_NAME_KEY);
-	return encoding ? encoding : locale_encoding;
+    selection = GTK_MENU_ITEM(go_option_menu_get_history(cs->encodings));
+    encoding = (char const *) g_object_get_data(G_OBJECT(selection),
+    CHARMAP_NAME_KEY);
+    return encoding ? encoding : locale_encoding;
 }
 
-struct cb_find_entry {
-	const char *enc;
-	gboolean found;
-	int i;
-	GSList *path;
+struct cb_find_entry
+{
+    const char *enc;
+    gboolean found;
+    int i;
+    GSList *path;
 };
 
-static void
-cb_find_entry (GtkMenuItem *w, struct cb_find_entry *cl)
+static void cb_find_entry(GtkMenuItem *w, struct cb_find_entry *cl)
 {
-	GtkWidget *sub;
-
-	if (cl->found)
-		return;
-
-	sub = gtk_menu_item_get_submenu (w);
-	if (sub) {
-		GSList *tmp = cl->path = g_slist_prepend (cl->path, GINT_TO_POINTER (cl->i));
-		cl->i = 0;
-
-		gtk_container_foreach (GTK_CONTAINER (sub), (GtkCallback)cb_find_entry, cl);
-		if (cl->found)
-			return;
-
-		cl->i = GPOINTER_TO_INT (cl->path->data);
-		cl->path = cl->path->next;
-		g_slist_free_1 (tmp);
-	} else {
-		const char *this_enc =
-			g_object_get_data (G_OBJECT (w), CHARMAP_NAME_KEY);
-		if (this_enc && strcmp (this_enc, cl->enc) == 0) {
-			cl->found = TRUE;
-			cl->path = g_slist_prepend (cl->path, GINT_TO_POINTER (cl->i));
-			cl->path = g_slist_reverse (cl->path);
-			return;
-		}
-	}
-	cl->i++;
+    GtkWidget *sub;
+
+    if (cl->found)
+        return;
+
+    sub = gtk_menu_item_get_submenu(w);
+    if (sub)
+    {
+        GSList *tmp = cl->path = g_slist_prepend(cl->path,
+                GINT_TO_POINTER(cl->i));
+        cl->i = 0;
+
+        gtk_container_foreach(GTK_CONTAINER(sub), (GtkCallback) cb_find_entry,
+                cl);
+        if (cl->found)
+            return;
+
+        cl->i = GPOINTER_TO_INT(cl->path->data);
+        cl->path = cl->path->next;
+        g_slist_free_1(tmp);
+    }
+    else
+    {
+        const char *this_enc = g_object_get_data(G_OBJECT(w), CHARMAP_NAME_KEY);
+        if (this_enc && strcmp(this_enc, cl->enc) == 0)
+        {
+            cl->found = TRUE;
+            cl->path = g_slist_prepend(cl->path, GINT_TO_POINTER(cl->i));
+            cl->path = g_slist_reverse(cl->path);
+            return;
+        }
+    }
+    cl->i++;
 }
 
-gboolean
-go_charmap_sel_set_encoding (GOCharmapSel *cs, const char *enc)
+gboolean go_charmap_sel_set_encoding(GOCharmapSel *cs, const char *enc)
 {
-	struct cb_find_entry cl;
-	CharsetInfo const *ci;
+    struct cb_find_entry cl;
+    CharsetInfo const *ci;
 
-	g_return_val_if_fail (GO_IS_CHARMAP_SEL (cs), FALSE);
-	g_return_val_if_fail (enc != NULL, FALSE);
+    g_return_val_if_fail(GO_IS_CHARMAP_SEL(cs), FALSE);
+    g_return_val_if_fail(enc != NULL, FALSE);
 
-	ci = g_hash_table_lookup (encoding_hash, enc);
-	if (!ci)
-		return FALSE;
+    ci = g_hash_table_lookup(encoding_hash, enc);
+    if (!ci)
+        return FALSE;
 
-	enc = ci->to_utf8_iconv_name;
-	if (!enc)
-		return FALSE;
+    enc = ci->to_utf8_iconv_name;
+    if (!enc)
+        return FALSE;
 
-	cl.enc = enc;
-	cl.found = FALSE;
-	cl.i = 0;
-	cl.path = NULL;
+    cl.enc = enc;
+    cl.found = FALSE;
+    cl.i = 0;
+    cl.path = NULL;
 
-	gtk_container_foreach (GTK_CONTAINER (cs->encodings_menu),
-			       (GtkCallback)cb_find_entry,
-			       &cl);
-	if (!cl.found)
-		return FALSE;
+    gtk_container_foreach(GTK_CONTAINER(cs->encodings_menu),
+            (GtkCallback) cb_find_entry, &cl);
+    if (!cl.found)
+        return FALSE;
 
-	go_option_menu_set_history (cs->encodings, cl.path);
-	g_slist_free (cl.path);
+    go_option_menu_set_history(cs->encodings, cl.path);
+    g_slist_free(cl.path);
 
-	return TRUE;
+    return TRUE;
 }
 
-static void
-cs_set_property (GObject      *object,
-		 guint         prop_id,
-		 const GValue *value,
-		 GParamSpec   *pspec)
+static void cs_set_property(GObject *object, guint prop_id, const GValue *value,
+        GParamSpec *pspec)
 {
-	GOCharmapSel *cs = GO_CHARMAP_SEL (object);
-
-	switch (prop_id) {
-	case PROP_TEST_DIRECTION:
-		cs->test = g_value_get_uint (value);
-		cs_build_menu (cs);
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
-	}
+    GOCharmapSel *cs = GO_CHARMAP_SEL(object);
+
+    switch (prop_id)
+    {
+    case PROP_TEST_DIRECTION:
+        cs->test = g_value_get_uint(value);
+        cs_build_menu(cs);
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+        break;
+    }
 }
 
-
-static void
-cs_get_property (GObject     *object,
-		 guint        prop_id,
-		 GValue      *value,
-		 GParamSpec  *pspec)
+static void cs_get_property(GObject *object, guint prop_id, GValue *value,
+        GParamSpec *pspec)
 {
-	GOCharmapSel *cs = GO_CHARMAP_SEL (object);
-
-	switch (prop_id) {
-	case PROP_TEST_DIRECTION:
-		g_value_set_uint (value, (guint)cs->test);
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
-	}
+    GOCharmapSel *cs = GO_CHARMAP_SEL(object);
+
+    switch (prop_id)
+    {
+    case PROP_TEST_DIRECTION:
+        g_value_set_uint(value, (guint) cs->test);
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+        break;
+    }
 }
diff --git a/lib/goffice/go-charmap-sel.h b/lib/goffice/go-charmap-sel.h
index 15d8400..e9b931a 100644
--- a/lib/goffice/go-charmap-sel.h
+++ b/lib/goffice/go-charmap-sel.h
@@ -35,18 +35,19 @@ G_BEGIN_DECLS
 
 typedef struct _GOCharmapSel GOCharmapSel;
 
-typedef enum {
-	GO_CHARMAP_SEL_TO_UTF8 = 0,
-	GO_CHARMAP_SEL_FROM_UTF8
+typedef enum
+{
+    GO_CHARMAP_SEL_TO_UTF8 = 0,
+    GO_CHARMAP_SEL_FROM_UTF8
 } GOCharmapSelTestDirection;
 
-GType        go_charmap_sel_get_type (void);
-GtkWidget *  go_charmap_sel_new (GOCharmapSelTestDirection test);
+GType go_charmap_sel_get_type(void);
+GtkWidget * go_charmap_sel_new(GOCharmapSelTestDirection test);
 
-gchar const *go_charmap_sel_get_encoding (GOCharmapSel *cs);
-gboolean     go_charmap_sel_set_encoding (GOCharmapSel *cs, const char *enc);
+gchar const *go_charmap_sel_get_encoding(GOCharmapSel *cs);
+gboolean go_charmap_sel_set_encoding(GOCharmapSel *cs, const char *enc);
 
-const char  *go_charmap_sel_get_encoding_name (GOCharmapSel *cs, const char *enc);
+const char *go_charmap_sel_get_encoding_name(GOCharmapSel *cs, const char *enc);
 
 G_END_DECLS
 
diff --git a/lib/goffice/go-glib-extras.c b/lib/goffice/go-glib-extras.c
index 78bb837..e470e41 100644
--- a/lib/goffice/go-glib-extras.c
+++ b/lib/goffice/go-glib-extras.c
@@ -26,100 +26,114 @@
 #include <fcntl.h>
 #include <errno.h>
 
-
-gint
-go_ascii_strcase_equal (gconstpointer v1, gconstpointer v2)
+gint go_ascii_strcase_equal(gconstpointer v1, gconstpointer v2)
 {
-	return g_ascii_strcasecmp ((char const *) v1, (char const *)v2) == 0;
+    return g_ascii_strcasecmp((char const *) v1, (char const *) v2) == 0;
 }
 
 /* a char* hash function from ASU */
-guint
-go_ascii_strcase_hash (gconstpointer v)
+guint go_ascii_strcase_hash(gconstpointer v)
 {
-	unsigned const char *s = (unsigned const char *)v;
-	unsigned const char *p;
-	guint h = 0, g;
-
-	for(p = s; *p != '\0'; p += 1) {
-		h = ( h << 4 ) + g_ascii_tolower (*p);
-		if ( ( g = h & 0xf0000000 ) ) {
-			h = h ^ (g >> 24);
-			h = h ^ g;
-		}
-	}
-
-	return h /* % M */;
+    unsigned const char *s = (unsigned const char *) v;
+    unsigned const char *p;
+    guint h = 0, g;
+
+    for (p = s; *p != '\0'; p += 1)
+    {
+        h = (h << 4) + g_ascii_tolower(*p);
+        if ((g = h & 0xf0000000))
+        {
+            h = h ^ (g >> 24);
+            h = h ^ g;
+        }
+    }
+
+    return h /* % M */;
 }
 
-
 const char *
-go_guess_encoding (const char *raw, size_t len, const char *user_guess,
-		   char **utf8_str)
+go_guess_encoding(const char *raw, size_t len, const char *user_guess,
+        char **utf8_str)
 {
-	int try;
-	gboolean debug = FALSE;
-
-	g_return_val_if_fail (raw != NULL, NULL);
-
-	for (try = 1; 1; try++) {
-		char const *guess = NULL;
-		GError *error = NULL;
-		char *utf8_data;
-
-		switch (try) {
-		case 1: guess = user_guess; break;
-		case 2: g_get_charset (&guess); break;
-		case 3: {
-			xmlCharEncoding enc =
-				xmlDetectCharEncoding ((const unsigned char*)raw, len);
-			switch (enc) {
-			case XML_CHAR_ENCODING_ERROR:
-			case XML_CHAR_ENCODING_NONE:
-				break;
-			case XML_CHAR_ENCODING_UTF16LE:
-				/* Default would give "UTF-16".  */
-				guess = "UTF-16LE";
-				break;
-			case XML_CHAR_ENCODING_UTF16BE:
-				/* Default would give "UTF-16".  */
-				guess = "UTF-16BE";
-				break;
-			default:
-				guess = xmlGetCharEncodingName (enc);
-			}
-			break;
-		}
-		case 4: guess = "ASCII"; break;
-		case 5: guess = "ISO-8859-1"; break;
-		case 6: guess = "UTF-8"; break;
-		default: return NULL;
-		}
-
-		if (!guess)
-			continue;
-
-		if (debug)
-			g_print ("Trying %s as encoding.\n", guess);
-
-		utf8_data = g_convert (raw, len, "UTF-8", guess,
-				       NULL, NULL, &error);
-		if (!error) {
-			/*
-			 * We can actually fail this test when gues is UTF-8,
-			 * see #401588.
-			 */
-			if (!g_utf8_validate (utf8_data, -1, NULL))
-				continue;
-			if (debug)
-				g_print ("Guessed %s as encoding.\n", guess);
-			if (utf8_str)
-				*utf8_str = utf8_data;
-			else
-				g_free (utf8_data);
-			return guess;
-		}
-
-		g_error_free (error);
-	}
+    int try;
+    gboolean debug = FALSE;
+
+    g_return_val_if_fail(raw != NULL, NULL);
+
+    for (try = 1; 1; try++)
+    {
+        char const *guess = NULL;
+        GError *error = NULL;
+        char *utf8_data;
+
+        switch (try)
+        {
+        case 1:
+            guess = user_guess;
+            break;
+        case 2:
+            g_get_charset(&guess);
+            break;
+        case 3:
+        {
+            xmlCharEncoding enc = xmlDetectCharEncoding(
+                    (const unsigned char*) raw, len);
+            switch (enc)
+            {
+            case XML_CHAR_ENCODING_ERROR:
+            case XML_CHAR_ENCODING_NONE:
+                break;
+            case XML_CHAR_ENCODING_UTF16LE:
+                /* Default would give "UTF-16".  */
+                guess = "UTF-16LE";
+                break;
+            case XML_CHAR_ENCODING_UTF16BE:
+                /* Default would give "UTF-16".  */
+                guess = "UTF-16BE";
+                break;
+            default:
+                guess = xmlGetCharEncodingName(enc);
+            }
+            break;
+        }
+        case 4:
+            guess = "ASCII";
+            break;
+        case 5:
+            guess = "ISO-8859-1";
+            break;
+        case 6:
+            guess = "UTF-8";
+            break;
+        default:
+            return NULL;
+        }
+
+        if (!guess)
+            continue;
+
+        if (debug)
+            g_print("Trying %s as encoding.\n", guess);
+
+        utf8_data = g_convert(raw, len, "UTF-8", guess,
+        NULL, NULL, &error);
+        if (!error)
+        {
+            /*
+             * We can actually fail this test when guess is UTF-8,
+             * see #401588.
+             */
+            if (!g_utf8_validate(utf8_data, -1, NULL))
+                continue;
+            if (debug)
+                g_print("Guessed %s as encoding.\n", guess);
+            if (utf8_str)
+                *utf8_str = utf8_data;
+            else
+                g_free(utf8_data);
+            return guess;
+        }
+
+        g_error_free(error);
+    }
 }
diff --git a/lib/goffice/go-glib-extras.h b/lib/goffice/go-glib-extras.h
index b58ca81..a78d4c4 100644
--- a/lib/goffice/go-glib-extras.h
+++ b/lib/goffice/go-glib-extras.h
@@ -5,13 +5,11 @@
 
 G_BEGIN_DECLS
 
+guint go_ascii_strcase_hash(gconstpointer v);
+gint go_ascii_strcase_equal(gconstpointer v, gconstpointer v2);
 
-guint	    go_ascii_strcase_hash	(gconstpointer v);
-gint	    go_ascii_strcase_equal	(gconstpointer v, gconstpointer v2);
-
-char const *go_guess_encoding		(char const *raw, gsize len,
-					 char const *user_guess,
-					 char **utf8_str);
+char const *go_guess_encoding(char const *raw, gsize len,
+                              char const *user_guess, char **utf8_str);
 
 G_END_DECLS
 
diff --git a/lib/goffice/go-optionmenu.c b/lib/goffice/go-optionmenu.c
index daca835..9dbbc84 100644
--- a/lib/goffice/go-optionmenu.c
+++ b/lib/goffice/go-optionmenu.c
@@ -37,281 +37,271 @@
 #include <gdk/gdkkeysyms.h>
 #include <glib/gi18n-lib.h>
 
-
-enum {
-	CHANGED,
-	LAST_SIGNAL
+enum
+{
+    CHANGED, LAST_SIGNAL
 };
 
-enum {
-	PROP_0,
-	PROP_MENU
+enum
+{
+    PROP_0,
+    PROP_MENU
 };
 
 static GtkButtonClass *parent_class = NULL;
 static guint signals[LAST_SIGNAL] = { 0 };
 
-
 GtkWidget*
-go_option_menu_new (void)
+go_option_menu_new(void)
 {
-	return g_object_new (GO_TYPE_OPTION_MENU, NULL);
+    return g_object_new(GO_TYPE_OPTION_MENU, NULL);
 }
 
-static void
-go_option_menu_detacher (GtkWidget *widget, GtkMenu *menu)
+static void go_option_menu_detacher(GtkWidget *widget, GtkMenu *menu)
 {
 #if 0
-	GOOptionMenu *option_menu = GO_OPTION_MENU (widget);
-	/* What?  */
+    GOOptionMenu *option_menu = GO_OPTION_MENU (widget);
+    /* What?  */
 #endif
 }
 
-static void
-go_option_menu_update_contents (GOOptionMenu *option_menu)
+static void go_option_menu_update_contents(GOOptionMenu *option_menu)
 {
-	const char *text;
-	GtkWidget *w;
-	g_return_if_fail (GO_IS_OPTION_MENU (option_menu));
+    const char *text;
+    GtkWidget *w;
+    g_return_if_fail(GO_IS_OPTION_MENU(option_menu));
 
-	w = gtk_bin_get_child (GTK_BIN (option_menu->selected));
-	text = g_object_get_data (G_OBJECT (w), "option-menu-text");
+    w = gtk_bin_get_child(GTK_BIN(option_menu->selected));
+    text = g_object_get_data(G_OBJECT(w), "option-menu-text");
 
-	if (!text && GTK_IS_LABEL (w))
-		text = gtk_label_get_text (GTK_LABEL (w));
+    if (!text && GTK_IS_LABEL(w))
+        text = gtk_label_get_text(GTK_LABEL(w));
 
-	if (!text)
-		text = "";
+    if (!text)
+        text = "";
 
 #if 0
-	g_print ("text = \"%s\"\n", text);
+    g_print ("text = \"%s\"\n", text);
 #endif
 
-	gtk_label_set_text (option_menu->button_label, text);
+    gtk_label_set_text(option_menu->button_label, text);
 }
 
-static void
-go_option_menu_select_item (GOOptionMenu *option_menu, GtkMenuItem *item)
+static void go_option_menu_select_item(GOOptionMenu *option_menu,
+        GtkMenuItem *item)
 {
-	if (item == option_menu->selected)
-		return;
+    if (item == option_menu->selected)
+        return;
 
-	if (GTK_IS_CHECK_MENU_ITEM (option_menu->selected))
-		gtk_check_menu_item_set_active
-			(GTK_CHECK_MENU_ITEM (option_menu->selected),
-			 FALSE);
+    if (GTK_IS_CHECK_MENU_ITEM(option_menu->selected))
+        gtk_check_menu_item_set_active(
+                GTK_CHECK_MENU_ITEM(option_menu->selected), FALSE);
 
-	option_menu->selected = item;
+    option_menu->selected = item;
 
-	if (GTK_IS_CHECK_MENU_ITEM (item))
-		gtk_check_menu_item_set_active
-			(GTK_CHECK_MENU_ITEM (option_menu->selected),
-			 TRUE);
+    if (GTK_IS_CHECK_MENU_ITEM(item))
+        gtk_check_menu_item_set_active(
+                GTK_CHECK_MENU_ITEM(option_menu->selected), TRUE);
 
-	go_option_menu_update_contents (option_menu);
+    go_option_menu_update_contents(option_menu);
 }
 
-
-static void
-go_option_menu_position (GtkMenu  *menu,
-			 gint     *x,
-			 gint     *y,
-			 gboolean *push_in,
-			 gpointer  user_data)
+static void go_option_menu_position(GtkMenu *menu, gint *x, gint *y,
+        gboolean *push_in, gpointer user_data)
 {
-	GOOptionMenu *option_menu = user_data;
-	GtkWidget *widget;
-	GtkRequisition requisition;
-	GList *children;
-	gint screen_width;
-	gint menu_xpos;
-	gint menu_ypos;
-	gint menu_width;
-	GtkAllocation allocation;
-
-	widget = GTK_WIDGET (option_menu);
-
-	gtk_widget_get_child_requisition (GTK_WIDGET (menu), &requisition);
-	menu_width = requisition.width;
-
-	gdk_window_get_origin (gtk_widget_get_window (widget), &menu_xpos, &menu_ypos);
-
-	gtk_widget_get_allocation (widget, &allocation);
-	menu_xpos += allocation.x;
-	menu_ypos += allocation.y + allocation.height / 2 - 2;
-
-	children = gtk_container_get_children (GTK_CONTAINER (option_menu->menu));
-	while (children) {
-		GtkWidget *child = children->data;
-
-		if (GTK_IS_CHECK_MENU_ITEM (child) &&
-		    gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (child))) {
-			gtk_widget_get_child_requisition (child, &requisition);
-			menu_ypos -= requisition.height / 2;
-			break;
-		}
-
-		if (gtk_widget_get_visible (child)) {
-			gtk_widget_get_child_requisition (child, &requisition);
-			menu_ypos -= requisition.height;
-		}
-
-		children = children->next;
-	}
-
-	screen_width = gdk_screen_get_width (gtk_widget_get_screen (widget));
-
-	if (menu_xpos + menu_width > screen_width)
-		menu_xpos -= (menu_xpos + menu_width) - screen_width;
-	if (menu_xpos < 0)
-		menu_xpos = 0;
-
-	*x = menu_xpos;
-	*y = menu_ypos;
-	*push_in = TRUE;
+    GOOptionMenu *option_menu = user_data;
+    GtkWidget *widget;
+    GtkRequisition requisition;
+    GList *children;
+    gint screen_width;
+    gint menu_xpos;
+    gint menu_ypos;
+    gint menu_width;
+    GtkAllocation allocation;
+
+    widget = GTK_WIDGET(option_menu);
+
+    gtk_widget_get_child_requisition(GTK_WIDGET(menu), &requisition);
+    menu_width = requisition.width;
+
+    gdk_window_get_origin(gtk_widget_get_window(widget), &menu_xpos,
+            &menu_ypos);
+
+    gtk_widget_get_allocation(widget, &allocation);
+    menu_xpos += allocation.x;
+    menu_ypos += allocation.y + allocation.height / 2 - 2;
+
+    children = gtk_container_get_children(GTK_CONTAINER(option_menu->menu));
+    while (children)
+    {
+        GtkWidget *child = children->data;
+
+        if (GTK_IS_CHECK_MENU_ITEM(child)
+                && gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(child)))
+        {
+            gtk_widget_get_child_requisition(child, &requisition);
+            menu_ypos -= requisition.height / 2;
+            break;
+        }
+
+        if (gtk_widget_get_visible(child))
+        {
+            gtk_widget_get_child_requisition(child, &requisition);
+            menu_ypos -= requisition.height;
+        }
+
+        children = children->next;
+    }
+
+    screen_width = gdk_screen_get_width(gtk_widget_get_screen(widget));
+
+    if (menu_xpos + menu_width > screen_width)
+        menu_xpos -= (menu_xpos + menu_width) - screen_width;
+    if (menu_xpos < 0)
+        menu_xpos = 0;
+
+    *x = menu_xpos;
+    *y = menu_ypos;
+    *push_in = TRUE;
 }
 
-
-static gint
-go_option_menu_button_press (GtkWidget      *widget,
-			     GdkEventButton *event)
+static gint go_option_menu_button_press(GtkWidget *widget,
+        GdkEventButton *event)
 {
-	GOOptionMenu *option_menu;
+    GOOptionMenu *option_menu;
 
-	g_return_val_if_fail (GO_IS_OPTION_MENU (widget), FALSE);
-	g_return_val_if_fail (event != NULL, FALSE);
+    g_return_val_if_fail(GO_IS_OPTION_MENU(widget), FALSE);
+    g_return_val_if_fail(event != NULL, FALSE);
 
-	option_menu = GO_OPTION_MENU (widget);
+    option_menu = GO_OPTION_MENU(widget);
 
-	if (event->type == GDK_BUTTON_PRESS && event->button == 1) {
-		gtk_menu_popup (GTK_MENU (option_menu->menu), NULL, NULL,
-				go_option_menu_position, option_menu,
-				event->button, event->time);
+    if (event->type == GDK_BUTTON_PRESS && event->button == 1)
+    {
+        gtk_menu_popup(GTK_MENU(option_menu->menu), NULL, NULL,
+                go_option_menu_position, option_menu, event->button,
+                event->time);
 
-		return TRUE;
-	}
+        return TRUE;
+    }
 
-	return FALSE;
+    return FALSE;
 }
 
-static gint
-go_option_menu_key_press (GtkWidget   *widget,
-			  GdkEventKey *event)
+static gint go_option_menu_key_press(GtkWidget *widget, GdkEventKey *event)
 {
-	GOOptionMenu *option_menu = GO_OPTION_MENU (widget);
-
-	switch (event->keyval) {
-	case GDK_KP_Space:
-	case GDK_space:
-		gtk_menu_popup (GTK_MENU (option_menu->menu), NULL, NULL,
-				go_option_menu_position, option_menu,
-				0, event->time);
-		return TRUE;
-	}
-
-	return FALSE;
+    GOOptionMenu *option_menu = GO_OPTION_MENU(widget);
+
+    switch (event->keyval)
+    {
+    case GDK_KP_Space:
+    case GDK_space:
+        gtk_menu_popup(GTK_MENU(option_menu->menu), NULL, NULL,
+                go_option_menu_position, option_menu, 0, event->time);
+        return TRUE;
+    }
+
+    return FALSE;
 }
 
-
-static void
-cb_select (GtkMenuItem *item, GOOptionMenu *option_menu)
+static void cb_select(GtkMenuItem *item, GOOptionMenu *option_menu)
 {
-	go_option_menu_select_item (option_menu, item);
-	g_signal_emit (option_menu, signals[CHANGED], 0);
+    go_option_menu_select_item(option_menu, item);
+    g_signal_emit(option_menu, signals[CHANGED], 0);
 }
 
-static void
-handle_menu_signals (GOOptionMenu *option_menu, gboolean connect)
+static void handle_menu_signals(GOOptionMenu *option_menu, gboolean connect)
 {
-	GList *children = gtk_container_get_children
-		(GTK_CONTAINER (option_menu->menu));
-
-	while (children) {
-		GtkWidget *child = children->data;
-		children = g_list_remove (children, child);
-
-		if (GTK_IS_MENU_ITEM (child)) {
-			GtkWidget *sub =
-				gtk_menu_item_get_submenu (GTK_MENU_ITEM (child));
-
-			if (sub)
-				children = g_list_concat (children,
-							  gtk_container_get_children (GTK_CONTAINER (sub)));
-			else if (connect)
-				g_signal_connect (child, "activate",
-						  G_CALLBACK (cb_select),
-						  option_menu);
-
-			else
-				g_signal_handlers_disconnect_by_func
-					(child, G_CALLBACK (cb_select), option_menu);
-
-		}
-	}
+    GList *children = gtk_container_get_children(
+            GTK_CONTAINER(option_menu->menu));
+
+    while (children)
+    {
+        GtkWidget *child = children->data;
+        children = g_list_remove(children, child);
+
+        if (GTK_IS_MENU_ITEM(child))
+        {
+            GtkWidget *sub = gtk_menu_item_get_submenu(GTK_MENU_ITEM(child));
+
+            if (sub)
+                children = g_list_concat(children,
+                        gtk_container_get_children(GTK_CONTAINER(sub)));
+            else if (connect)
+                g_signal_connect(child, "activate", G_CALLBACK(cb_select),
+                        option_menu);
+
+            else
+                g_signal_handlers_disconnect_by_func(child,
+                        G_CALLBACK(cb_select), option_menu);
+
+        }
+    }
 }
 
-void
-go_option_menu_set_menu (GOOptionMenu *option_menu,
-			 GtkWidget *menu)
+void go_option_menu_set_menu(GOOptionMenu *option_menu, GtkWidget *menu)
 {
-	GtkMenuShell *shell;
+    GtkMenuShell *shell;
 
-	g_return_if_fail (GO_IS_OPTION_MENU (option_menu));
-	g_return_if_fail (GTK_IS_MENU_SHELL (menu));
+    g_return_if_fail(GO_IS_OPTION_MENU(option_menu));
+    g_return_if_fail(GTK_IS_MENU_SHELL(menu));
 
-	shell = (GtkMenuShell *)menu;
-	if (option_menu->menu == shell)
-		return;
+    shell = (GtkMenuShell *) menu;
+    if (option_menu->menu == shell)
+        return;
 
-	if (option_menu->menu) {
-		if (option_menu->menu->active)
-			gtk_menu_shell_cancel (option_menu->menu);
+    if (option_menu->menu)
+    {
+        if (option_menu->menu->active)
+            gtk_menu_shell_cancel(option_menu->menu);
 
-		handle_menu_signals (option_menu, FALSE);
+        handle_menu_signals(option_menu, FALSE);
 
-		gtk_menu_detach (GTK_MENU (option_menu->menu));
-		g_object_unref (option_menu->menu);
-	}
+        gtk_menu_detach(GTK_MENU(option_menu->menu));
+        g_object_unref(option_menu->menu);
+    }
 
-	option_menu->menu = shell;
+    option_menu->menu = shell;
 
-	if (shell) {
-		g_object_ref (shell);
+    if (shell)
+    {
+        g_object_ref(shell);
 
-		gtk_menu_attach_to_widget (GTK_MENU (shell),
-					   GTK_WIDGET (option_menu),
-					   go_option_menu_detacher);
+        gtk_menu_attach_to_widget(GTK_MENU(shell), GTK_WIDGET(option_menu),
+                go_option_menu_detacher);
 
-		handle_menu_signals (option_menu, TRUE);
+        handle_menu_signals(option_menu, TRUE);
 
-		go_option_menu_select_item (option_menu,
-					    GTK_MENU_ITEM (gtk_menu_get_active (GTK_MENU (shell))));
-	}
+        go_option_menu_select_item(option_menu,
+                GTK_MENU_ITEM(gtk_menu_get_active(GTK_MENU(shell))));
+    }
 
-	g_object_notify (G_OBJECT (option_menu), "menu");
+    g_object_notify(G_OBJECT(option_menu), "menu");
 }
 
-void
-go_option_menu_set_history (GOOptionMenu *option_menu, GSList *selection)
+void go_option_menu_set_history(GOOptionMenu *option_menu, GSList *selection)
 {
-	g_return_if_fail (selection != NULL);
-	g_return_if_fail (GO_IS_OPTION_MENU (option_menu));
-
-	if (option_menu->menu) {
-		GtkMenuShell *menu = option_menu->menu;
-
-		while (1) {
-			int n = GPOINTER_TO_INT (selection->data);
-			GtkMenuItem *item = g_list_nth_data (gtk_container_get_children (GTK_CONTAINER (menu)), n);
-			selection = selection->next;
-			if (selection)
-				menu = GTK_MENU_SHELL (gtk_menu_item_get_submenu (item));
-			else {
-				go_option_menu_select_item (option_menu, item);
-				break;
-			}
-		}
-	}
+    g_return_if_fail(selection != NULL);
+    g_return_if_fail(GO_IS_OPTION_MENU(option_menu));
+
+    if (option_menu->menu)
+    {
+        GtkMenuShell *menu = option_menu->menu;
+
+        while (1)
+        {
+            int n = GPOINTER_TO_INT(selection->data);
+            GtkMenuItem *item = g_list_nth_data(
+                    gtk_container_get_children(GTK_CONTAINER(menu)), n);
+            selection = selection->next;
+            if (selection)
+                menu = GTK_MENU_SHELL(gtk_menu_item_get_submenu(item));
+            else
+            {
+                go_option_menu_select_item(option_menu, item);
+                break;
+            }
+        }
+    }
 }
 
 /**
@@ -324,154 +314,132 @@ go_option_menu_set_history (GOOptionMenu *option_menu, GSList *selection)
  **/
 
 GtkWidget *
-go_option_menu_get_history (GOOptionMenu *option_menu)
+go_option_menu_get_history(GOOptionMenu *option_menu)
 {
-	return GTK_WIDGET (option_menu->selected);
+    return GTK_WIDGET(option_menu->selected);
 }
 
-
-static void
-go_option_menu_set_property (GObject            *object,
-			     guint               prop_id,
-			     const GValue       *value,
-			     GParamSpec         *pspec)
+static void go_option_menu_set_property(GObject *object, guint prop_id,
+        const GValue *value, GParamSpec *pspec)
 {
-	GOOptionMenu *option_menu = GO_OPTION_MENU (object);
-
-	switch (prop_id) {
-	case PROP_MENU:
-		go_option_menu_set_menu (option_menu, g_value_get_object (value));
-		break;
-
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
-	}
+    GOOptionMenu *option_menu = GO_OPTION_MENU(object);
+
+    switch (prop_id)
+    {
+    case PROP_MENU:
+        go_option_menu_set_menu(option_menu, g_value_get_object(value));
+        break;
+
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+        break;
+    }
 }
 
-static void
-go_option_menu_get_property (GObject            *object,
-			     guint               prop_id,
-			     GValue             *value,
-			     GParamSpec         *pspec)
+static void go_option_menu_get_property(GObject *object, guint prop_id,
+        GValue *value, GParamSpec *pspec)
 {
-	GOOptionMenu *option_menu = GO_OPTION_MENU (object);
-
-	switch (prop_id) {
-	case PROP_MENU:
-		g_value_set_object (value, option_menu->menu);
-		break;
-
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
-	}
+    GOOptionMenu *option_menu = GO_OPTION_MENU(object);
+
+    switch (prop_id)
+    {
+    case PROP_MENU:
+        g_value_set_object(value, option_menu->menu);
+        break;
+
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+        break;
+    }
 }
 
-static void
-go_option_menu_destroy (GtkObject *object)
+static void go_option_menu_destroy(GtkObject *object)
 {
-	GOOptionMenu *option_menu;
+    GOOptionMenu *option_menu;
 
-	g_return_if_fail (GO_IS_OPTION_MENU (object));
+    g_return_if_fail(GO_IS_OPTION_MENU(object));
 
-	option_menu = GO_OPTION_MENU (object);
+    option_menu = GO_OPTION_MENU(object);
 
-	if (option_menu->menu) {
-		gtk_widget_destroy (GTK_WIDGET (option_menu->menu));
-		g_object_unref (option_menu->menu);
-		option_menu->menu = NULL;
-	}
-	option_menu->selected = NULL;
+    if (option_menu->menu)
+    {
+        gtk_widget_destroy(GTK_WIDGET(option_menu->menu));
+        g_object_unref(option_menu->menu);
+        option_menu->menu = NULL;
+    }
+    option_menu->selected = NULL;
 
-	GTK_OBJECT_CLASS (parent_class)->destroy (object);
+    GTK_OBJECT_CLASS(parent_class)->destroy(object);
 }
 
-static void
-go_option_menu_class_init (GOOptionMenuClass *class)
+static void go_option_menu_class_init(GOOptionMenuClass *class)
 {
-	GObjectClass *gobject_class = (GObjectClass*) class;
-	GtkObjectClass *object_class = (GtkObjectClass*) class;
-	GtkWidgetClass *widget_class = (GtkWidgetClass*) class;
-
-	parent_class = g_type_class_peek_parent (class);
-
-	signals[CHANGED] =
-		g_signal_new ("changed",
-			      G_OBJECT_CLASS_TYPE (class),
-			      G_SIGNAL_RUN_LAST,
-			      G_STRUCT_OFFSET (GOOptionMenuClass, changed),
-			      NULL, NULL,
-			      g_cclosure_marshal_VOID__VOID,
-			      G_TYPE_NONE, 0);
-
-	gobject_class->set_property = go_option_menu_set_property;
-	gobject_class->get_property = go_option_menu_get_property;
-	object_class->destroy = go_option_menu_destroy;
-	widget_class->button_press_event = go_option_menu_button_press;
-	widget_class->key_press_event = go_option_menu_key_press;
-
-	g_object_class_install_property (gobject_class,
-					 PROP_MENU,
-					 g_param_spec_object ("menu",
-							      _("Menu"),
-							      _("The menu of options"),
-							      GTK_TYPE_MENU,
-							      G_PARAM_READABLE | G_PARAM_WRITABLE));
+    GObjectClass *gobject_class = (GObjectClass*) class;
+    GtkObjectClass *object_class = (GtkObjectClass*) class;
+    GtkWidgetClass *widget_class = (GtkWidgetClass*) class;
+
+    parent_class = g_type_class_peek_parent(class);
+
+    signals[CHANGED] = g_signal_new("changed", G_OBJECT_CLASS_TYPE(class),
+            G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(GOOptionMenuClass, changed),
+            NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
+
+    gobject_class->set_property = go_option_menu_set_property;
+    gobject_class->get_property = go_option_menu_get_property;
+    object_class->destroy = go_option_menu_destroy;
+    widget_class->button_press_event = go_option_menu_button_press;
+    widget_class->key_press_event = go_option_menu_key_press;
+
+    g_object_class_install_property(gobject_class, PROP_MENU,
+            g_param_spec_object("menu", _("Menu"), _("The menu of options"),
+                    GTK_TYPE_MENU, G_PARAM_READABLE | G_PARAM_WRITABLE));
 }
 
-static void
-go_option_menu_init (GOOptionMenu *option_menu)
+static void go_option_menu_init(GOOptionMenu *option_menu)
 {
-	GtkBox *box;
-	GtkWidget *arrow, *sep;
+    GtkBox *box;
+    GtkWidget *arrow, *sep;
 
-	gtk_widget_set_can_focus (GTK_WIDGET (option_menu), TRUE);
-	gtk_widget_set_can_default (GTK_WIDGET (option_menu), FALSE);
-	gtk_widget_set_receives_default (GTK_WIDGET (option_menu), FALSE);
+    gtk_widget_set_can_focus(GTK_WIDGET(option_menu), TRUE);
+    gtk_widget_set_can_default(GTK_WIDGET(option_menu), FALSE);
+    gtk_widget_set_receives_default(GTK_WIDGET(option_menu), FALSE);
 
-	box = GTK_BOX (gtk_hbox_new (FALSE, FALSE));
+    box = GTK_BOX(gtk_hbox_new(FALSE, FALSE));
 
-	option_menu->menu = NULL;
-	option_menu->selected = NULL;
+    option_menu->menu = NULL;
+    option_menu->selected = NULL;
 
-	option_menu->button_label = GTK_LABEL (gtk_label_new (""));
-	gtk_box_pack_start (box, GTK_WIDGET (option_menu->button_label),
-			    FALSE, TRUE, 0);
+    option_menu->button_label = GTK_LABEL(gtk_label_new(""));
+    gtk_box_pack_start(box, GTK_WIDGET(option_menu->button_label), FALSE, TRUE,
+            0);
 
-	arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
-	g_object_set (arrow, "xalign", 0.75, NULL);
-	gtk_box_pack_end (box, arrow, FALSE, FALSE, 0);
+    arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_NONE);
+    g_object_set(arrow, "xalign", 0.75, NULL);
+    gtk_box_pack_end(box, arrow, FALSE, FALSE, 0);
 
-	sep = gtk_vseparator_new ();
-	gtk_box_pack_end (box, sep, FALSE, FALSE, 0);
+    sep = gtk_vseparator_new();
+    gtk_box_pack_end(box, sep, FALSE, FALSE, 0);
 
-	gtk_container_add (GTK_CONTAINER (option_menu), GTK_WIDGET (box));
+    gtk_container_add(GTK_CONTAINER(option_menu), GTK_WIDGET(box));
 }
 
-GType
-go_option_menu_get_type (void)
+GType go_option_menu_get_type(void)
 {
-	static GType option_menu_type = 0;
-
-	if (!option_menu_type) {
-		static const GTypeInfo option_menu_info =
-			{
-				sizeof (GOOptionMenuClass),
-				NULL,		/* base_init */
-				NULL,		/* base_finalize */
-				(GClassInitFunc) go_option_menu_class_init,
-				NULL,		/* class_finalize */
-				NULL,		/* class_data */
-				sizeof (GOOptionMenu),
-				0,		/* n_preallocs */
-				(GInstanceInitFunc) go_option_menu_init,
-			};
-
-		option_menu_type =
-			g_type_register_static (GTK_TYPE_BUTTON, "GOOptionMenu",
-						&option_menu_info, 0);
-	}
-
-	return option_menu_type;
+    static GType option_menu_type = 0;
+
+    if (!option_menu_type)
+    {
+        static const GTypeInfo option_menu_info =
+        { sizeof(GOOptionMenuClass), NULL, /* base_init */
+        NULL, /* base_finalize */
+        (GClassInitFunc) go_option_menu_class_init, NULL, /* class_finalize */
+        NULL, /* class_data */
+        sizeof(GOOptionMenu), 0, /* n_preallocs */
+        (GInstanceInitFunc) go_option_menu_init, };
+
+        option_menu_type = g_type_register_static(GTK_TYPE_BUTTON,
+                "GOOptionMenu", &option_menu_info, 0);
+    }
+
+    return option_menu_type;
 }
diff --git a/lib/goffice/go-optionmenu.h b/lib/goffice/go-optionmenu.h
index 65252b3..6582c9c 100644
--- a/lib/goffice/go-optionmenu.h
+++ b/lib/goffice/go-optionmenu.h
@@ -44,41 +44,39 @@ G_BEGIN_DECLS
 #define GO_IS_OPTION_MENU_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GO_TYPE_OPTION_MENU))
 #define GO_OPTION_MENU_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), GO_TYPE_OPTION_MENU, GOOptionMenuClass))
 
-typedef struct _GOOptionMenu       GOOptionMenu;
-typedef struct _GOOptionMenuClass  GOOptionMenuClass;
+typedef struct _GOOptionMenu GOOptionMenu;
+typedef struct _GOOptionMenuClass GOOptionMenuClass;
 
 struct _GOOptionMenu
 {
-	GtkButton button;
+    GtkButton button;
 
-	/*< private >*/
+    /*< private >*/
 
-	GtkMenuShell *menu;
-	GtkMenuItem *selected;
-	GtkLabel *button_label;
+    GtkMenuShell *menu;
+    GtkMenuItem *selected;
+    GtkLabel *button_label;
 };
 
 struct _GOOptionMenuClass
 {
-  GtkButtonClass parent_class;
+    GtkButtonClass parent_class;
 
-  void (*changed) (GOOptionMenu *option_menu);
+    void (*changed)(GOOptionMenu *option_menu);
 
-  /* Padding for future expansion */
-  void (*_gtk_reserved1) (void);
-  void (*_gtk_reserved2) (void);
-  void (*_gtk_reserved3) (void);
-  void (*_gtk_reserved4) (void);
+    /* Padding for future expansion */
+    void (*_gtk_reserved1)(void);
+    void (*_gtk_reserved2)(void);
+    void (*_gtk_reserved3)(void);
+    void (*_gtk_reserved4)(void);
 };
 
-
-GType      go_option_menu_get_type    (void) G_GNUC_CONST;
-GtkWidget* go_option_menu_new         (void);
-void       go_option_menu_set_menu    (GOOptionMenu *option_menu,
-				       GtkWidget *menu);
-void       go_option_menu_set_history (GOOptionMenu *option_menu,
-				       GSList *selection);
-GtkWidget *go_option_menu_get_history (GOOptionMenu *option_menu);
+GType go_option_menu_get_type(void)
+G_GNUC_CONST;
+GtkWidget* go_option_menu_new(void);
+void go_option_menu_set_menu(GOOptionMenu *option_menu, GtkWidget *menu);
+void go_option_menu_set_history(GOOptionMenu *option_menu, GSList *selection);
+GtkWidget *go_option_menu_get_history(GOOptionMenu *option_menu);
 
 G_END_DECLS
 
diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.c b/src/import-export/csv-imp/assistant-csv-trans-import.c
index 9d7dfbb..751b394 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.c
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.c
@@ -234,7 +234,7 @@ csv_import_trans_load_settings (CsvImportTrans *info)
         gtk_adjustment_set_upper (adj, info->num_of_rows);
         gtk_spin_button_set_value (GTK_SPIN_BUTTON(info->end_row_spin), info->num_of_rows - info->settings_data->footer_rows);
 
-        // Set Aternate rows
+        // Set Alternate rows
         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(info->skip_rows), info->settings_data->skip_alt_rows);
 
         // Set Import Format
@@ -1043,7 +1043,7 @@ static void separated_or_fixed_selected (GtkToggleButton* csv_button, CsvImportT
 static void encoding_selected (GOCharmapSel* selector, const char* encoding,
                               CsvImportTrans* info)
 {
-    /* This gets called twice everytime a new encoding is selected. The
+    /* This gets called twice every time a new encoding is selected. The
      * second call actually passes the correct data; thus, we only do
      * something the second time this is called. */
 
@@ -2184,7 +2184,7 @@ csv_import_trans_assistant_preview_page_prepare (GtkAssistant *assistant,
         adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(info->start_row_spin));
         gtk_spin_button_set_value (GTK_SPIN_BUTTON(info->start_row_spin), 1);
 
-        /* Set spin buttons and settings combo hbox not sensative */
+        /* Set spin buttons and settings combo hbox not sensitive */
         gtk_widget_set_sensitive (info->combo_hbox, FALSE);
         gtk_widget_set_sensitive (info->start_row_spin, FALSE);
         gtk_widget_set_sensitive (info->end_row_spin, FALSE);
@@ -2300,7 +2300,7 @@ import_account_check_all (GtkTreeModel *model)
 
 
 /*****************************************************************
- * Parse the text spliting into a path and the last_part based on
+ * Parse the text splitting into a path and the last_part based on
  * account separator. If the path is valid, add the last_part and
  * return this. If the path is invalid, use the new separator path
  * with the last_part and return that so there is only one new
@@ -2601,7 +2601,7 @@ csv_import_trans_assistant_match_page_prepare (GtkAssistant *assistant,
 
         if (info->gnc_csv_importer_gui == NULL)
         {
-            /* Create the genereic transaction importer GUI. */
+            /* Create the generic transaction importer GUI. */
             info->gnc_csv_importer_gui = gnc_gen_trans_assist_new (info->match_page, NULL, FALSE, 42);
 
             /* Add the help button for the matcher */
@@ -3003,7 +3003,7 @@ csv_import_trans_assistant_create (CsvImportTrans *info)
         g_signal_connect (G_OBJECT(save_button), "clicked",
                          G_CALLBACK(csv_import_trans_save_settings_cb), (gpointer)info);
 
-        // Add Deelete Settings button
+        // Add Delete Settings button
         del_button = gtk_button_new_with_label (_("Delete Settings"));
         gtk_box_pack_start (GTK_BOX(info->combo_hbox), del_button, FALSE, FALSE, 6);
         gtk_widget_show (del_button);
@@ -3110,7 +3110,7 @@ csv_import_trans_assistant_create (CsvImportTrans *info)
     }
 
     /* Account page */
-    /* Initialise the Account Picker and add to the Assistant */
+    /* Initialize the Account Picker and add to the Assistant */
     info->account_page  = GTK_WIDGET(gtk_builder_get_object (builder, "account_page"));
     info->account_picker = gnc_import_account_assist_setup (info->account_page);
     info->account_label = GTK_WIDGET(gtk_builder_get_object (builder, "account_page_label"));
diff --git a/src/import-export/csv-imp/gnc-csv-model.c b/src/import-export/csv-imp/gnc-csv-model.c
index c496cdf..04244da 100644
--- a/src/import-export/csv-imp/gnc-csv-model.c
+++ b/src/import-export/csv-imp/gnc-csv-model.c
@@ -411,7 +411,7 @@ int gnc_csv_convert_encoding (GncCsvParseData* parse_data, const char* encoding,
 }
 
 /** Loads a file into a GncCsvParseData. This is the first function
- * that must be called after createing a new GncCsvParseData. If this
+ * that must be called after creating a new GncCsvParseData. If this
  * fails because the file couldn't be opened, no more functions can be
  * called on the parse data until this succeeds (or until it fails
  * because of an encoding guess error). If it fails because the
@@ -686,7 +686,7 @@ static gboolean trans_property_set (TransProperty* prop, char* str)
         switch (prop->list->currency_format)
         {
         case 0:
-            /* Currancy locale */
+            /* Currency locale */
             if (!(xaccParseAmount (str_dupe, TRUE, &val, &endptr)))
             {
                 g_free (str_dupe);
@@ -694,7 +694,7 @@ static gboolean trans_property_set (TransProperty* prop, char* str)
             }
             break;
         case 1:
-            /* Currancy decimal period */
+            /* Currency decimal period */
             if (!(xaccParseAmountExtended (str_dupe, TRUE, '-', '.', ',', "\003\003", "$+", &val, &endptr)))
             {
                 g_free (str_dupe);
@@ -702,7 +702,7 @@ static gboolean trans_property_set (TransProperty* prop, char* str)
             }
             break;
         case 2:
-            /* Currancy decimal comma */
+            /* Currency decimal comma */
             if (!(xaccParseAmountExtended (str_dupe, TRUE, '-', ',', '.', "\003\003", "$+", &val, &endptr)))
             {
                 g_free (str_dupe);
@@ -740,7 +740,7 @@ static TransPropertyList* trans_property_list_new (Account* account, int date_fo
  */
 static void trans_property_list_free (TransPropertyList* list)
 {
-    /* Free all of the properties in this list before freeeing the list itself. */
+    /* Free all of the properties in this list before freeing the list itself. */
     GList* properties_begin = list->properties;
     while (list->properties != NULL)
     {

commit d57a7ceed23f7fd60944a530a98003d521384b39
Author: Geert Janssens <geert at kobaltwit.be>
Date:   Sat Jan 14 17:24:13 2017 +0100

    Look for translatable strings in lib directory as well
    
    The Character Encoding menu imported from goffice comes with many such strings.
    I still need to find a nice way to copy the existing translations for them
    from goffice directly to gnucash.

diff --git a/make-gnucash-potfiles.in b/make-gnucash-potfiles.in
index ee54e21..12c6a10 100644
--- a/make-gnucash-potfiles.in
+++ b/make-gnucash-potfiles.in
@@ -46,7 +46,7 @@ sub sort_func
 }
 
 my @possible_files = sort sort_func
-                      `cd @-SRCDIR-@ && find src -name '*.c' \\
+                      `cd @-SRCDIR-@ && find src lib -name '*.c' \\
                       -o -name '*.cpp' -o -name '*.glade' \\
                       -o -name '*.desktop.in' -o -name '*.keys.in' \\
                       -o -name '*.gschema.xml.in.in' -o -name '*.scm'`;
diff --git a/po/POTFILES.in b/po/POTFILES.in
index dc5145c..34359fe 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -1,5 +1,12 @@
 # This is a list of files which contain translatable strings.
 # This file was generated by ../make-gnucash-potfiles.
+lib/goffice/go-charmap-sel.c
+lib/goffice/go-glib-extras.c
+lib/goffice/go-optionmenu.c
+lib/libc/libc-missing-noop.c
+lib/libc/setenv.c
+lib/libc/strfmon.c
+lib/libc/strptime.c
 src/app-utils/app-utils.scm
 src/app-utils/business-helpers.c
 src/app-utils/business-options.c

commit 0dc6fdc79cafd3325002d9a61d2fa33ba290e7ae
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sat Jan 14 14:36:41 2017 +0100

    Use internalized goffice parts in csv-import
    
    The dependency on goffice can't be eliminated completely yet at this point.
    The stf library we're currently using to parse csv files depends on it. This
    will be fixed in a future commit.

diff --git a/src/import-export/csv-imp/CMakeLists.txt b/src/import-export/csv-imp/CMakeLists.txt
index 7f90114..9ece8d0 100644
--- a/src/import-export/csv-imp/CMakeLists.txt
+++ b/src/import-export/csv-imp/CMakeLists.txt
@@ -14,6 +14,9 @@ SET(csv_import_SOURCES
   gnc-csv-gnumeric-popup.c
   gnc-csv-trans-settings.c
   ${CMAKE_SOURCE_DIR}/lib/stf/stf-parse.c
+  ${CMAKE_SOURCE_DIR}/lib/goffice/go-charmap-sel.c
+  ${CMAKE_SOURCE_DIR}/lib/goffice/go-optionmenu.c
+  ${CMAKE_SOURCE_DIR}/lib/goffice/go-glib-extras.c
 )
 
 # Add dependency on config.h
@@ -31,6 +34,9 @@ SET(csv_import_noinst_HEADERS
   gnc-csv-gnumeric-popup.h
   gnc-csv-trans-settings.h
   ${CMAKE_SOURCE_DIR}/lib/stf/stf-parse.h
+  ${CMAKE_SOURCE_DIR}/lib/goffice/go-charmap-sel.h
+  ${CMAKE_SOURCE_DIR}/lib/goffice/go-optionmenu.h
+  ${CMAKE_SOURCE_DIR}/lib/goffice/go-glib-extras.h
 )
 
 ADD_LIBRARY(gncmod-csv-import ${csv_import_noinst_HEADERS} ${csv_import_SOURCES})
diff --git a/src/import-export/csv-imp/Makefile.am b/src/import-export/csv-imp/Makefile.am
index 49eab04..9400ce8 100644
--- a/src/import-export/csv-imp/Makefile.am
+++ b/src/import-export/csv-imp/Makefile.am
@@ -36,6 +36,7 @@ libgncmod_csv_import_la_LIBADD = \
   ${top_builddir}/src/engine/libgncmod-engine.la \
   ${top_builddir}/src/core-utils/libgnc-core-utils.la \
   ${top_builddir}/src/gnc-module/libgnc-module.la \
+  ${top_builddir}/lib/goffice/libgnc-goffice.la \
   ${top_builddir}/lib/stf/libgnc-stf.la \
   ${top_builddir}/lib/libc/libc-missing.la \
   ${top_builddir}/src/libqof/qof/libgnc-qof.la \
diff --git a/src/import-export/csv-imp/assistant-csv-trans-import.c b/src/import-export/csv-imp/assistant-csv-trans-import.c
index 189acc4..9d7dfbb 100644
--- a/src/import-export/csv-imp/assistant-csv-trans-import.c
+++ b/src/import-export/csv-imp/assistant-csv-trans-import.c
@@ -50,7 +50,7 @@
 
 #include "gnc-csv-model.h"
 #include "gnc-csv-gnumeric-popup.h"
-#include <goffice/gtk/go-charmap-sel.h>
+#include <goffice/go-charmap-sel.h>
 
 #define MIN_COL_WIDTH 70
 #define GNC_PREFS_GROUP "dialogs.import.csv"
diff --git a/src/import-export/csv-imp/gnc-csv-account-map.h b/src/import-export/csv-imp/gnc-csv-account-map.h
index 6f5a669..976a661 100644
--- a/src/import-export/csv-imp/gnc-csv-account-map.h
+++ b/src/import-export/csv-imp/gnc-csv-account-map.h
@@ -27,6 +27,7 @@
 #ifndef GNC_CSV_ACCOUNT_MAP_H
 #define GNC_CSV_ACCOUNT_MAP_H
 
+#include <gtk/gtk.h>
 #include "Account.h"
 
 /** Enumeration for the mappings liststore */
diff --git a/src/import-export/csv-imp/gnc-csv-model.c b/src/import-export/csv-imp/gnc-csv-model.c
index a862f01..c496cdf 100644
--- a/src/import-export/csv-imp/gnc-csv-model.c
+++ b/src/import-export/csv-imp/gnc-csv-model.c
@@ -26,14 +26,7 @@
 #endif
 
 #include <glib/gi18n.h>
-
-#include <goffice/goffice-features.h>
-#if (GO_VERSION_EPOCH == 0) && (GO_VERSION_MAJOR == 7) && (GO_VERSION_MINOR == 8)
-/* For libgoffice-0.7.8, disable its internal inclusion of <regutf8.h>
-   so to avoid clashing symbol definitions with <regex.h> */
-# define GO_REGUTF8_H
-#endif
-#include <goffice/utils/go-glib-extras.h>
+#include <goffice/go-glib-extras.h>
 
 #include "gnc-csv-account-map.h"
 

commit baf10bb7a553a232498708b553c0e1e9def3ab1f
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Tue Jan 26 17:13:36 2016 +0100

    Fix build issues with goffice imported sources
    
    - Remove unused includes and functions
    - Some small cleanups

diff --git a/lib/goffice/Makefile.am b/lib/goffice/Makefile.am
index 29aafab..f6c25ae 100644
--- a/lib/goffice/Makefile.am
+++ b/lib/goffice/Makefile.am
@@ -10,8 +10,10 @@ REALHDRS = go-optionmenu.h \
 libgnc_goffice_la_SOURCES = ${REALSRCS}
 noinst_HEADERS = ${REALHDRS}
 
-libgnc_goffice_la_LIBADD = $(GTK_LIBS)
+libgnc_goffice_la_LIBADD = $(GTK_LIBS) \
+                           ${LIBXML2_LIBS}
 
-AM_CPPFLAGS = $(GTK_CFLAGS)
+AM_CPPFLAGS = $(GTK_CFLAGS) \
+              ${LIBXML2_CFLAGS}
 
 EXTRA_DIST = $(REALSRCS) $(REALHDRS)
diff --git a/lib/goffice/go-charmap-sel.c b/lib/goffice/go-charmap-sel.c
index 287133b..33eb744 100644
--- a/lib/goffice/go-charmap-sel.c
+++ b/lib/goffice/go-charmap-sel.c
@@ -23,12 +23,11 @@
  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
-#include <goffice/goffice-config.h>
+#include "config.h"
 #include "go-charmap-sel.h"
 #include "go-optionmenu.h"
-#include <goffice/utils/go-glib-extras.h>
+#include "go-glib-extras.h"
 #include <glib/gi18n-lib.h>
-#include <gsf/gsf-impl-utils.h>
 #include <string.h>
 #include <stdlib.h>
 
@@ -425,8 +424,7 @@ cs_build_menu (GOCharmapSel *cs)
 	set_menu_to_default (cs, lg_cnt);
 }
 
-static void
-cs_class_init (GtkWidgetClass *widget_klass)
+static void cs_class_init(GtkWidgetClass *widget_klass)
 {
 	CharsetInfo *ci;
 	size_t i;
@@ -552,9 +550,6 @@ cs_class_init (GtkWidgetClass *widget_klass)
 	}
 }
 
-GSF_CLASS (GOCharmapSel, go_charmap_sel,
-	   cs_class_init, cs_init, GTK_TYPE_HBOX)
-
 GtkWidget *
 go_charmap_sel_new (GOCharmapSelTestDirection test)
 {
diff --git a/lib/goffice/go-glib-extras.c b/lib/goffice/go-glib-extras.c
index e385483..78bb837 100644
--- a/lib/goffice/go-glib-extras.c
+++ b/lib/goffice/go-glib-extras.c
@@ -8,13 +8,10 @@
  *    Zbigniew Chyla (cyba at gnome.pl)
  *    Morten Welinder (terra at gnome.org)
  */
-#include <goffice/goffice-config.h>
+#include "config.h"
 #include "go-glib-extras.h"
-#include "go-locale.h"
-#include <goffice/app/go-cmd-context.h>
 
 #include <glib/gi18n-lib.h>
-#include <gsf/gsf-impl-utils.h>
 #include <libxml/encoding.h>
 
 #include <stdlib.h>
@@ -29,208 +26,6 @@
 #include <fcntl.h>
 #include <errno.h>
 
-static void
-cb_hash_collect_keys (gpointer key, gpointer value, GSList **accum)
-{
-	*accum = g_slist_prepend (*accum, key);
-}
-
-/**
- * go_hash_keys :
- * @hash : #GHashTable
- *
- * Collects an unordered list of the keys in @hash.
- *
- * Returns: a list which the caller needs to free.
- * 	The content has not additional references added
- **/
-GSList *
-go_hash_keys (GHashTable *hash)
-{
-	GSList *accum = NULL;
-	g_hash_table_foreach (hash,
-		(GHFunc )cb_hash_collect_keys, &accum);
-	return accum;
-}
-
-static void
-cb_hash_collect_values (gpointer key, gpointer value, GSList **accum)
-{
-	*accum = g_slist_prepend (*accum, value);
-}
-
-/**
- * go_hash_values :
- * @hash : #GHashTable
- *
- * Collects an unordered list of the values in @hash.
- *
- * Returns: a list which the caller needs to free.
- * 	The content has not additional references added
- **/
-GSList *
-go_hash_values (GHashTable *hash)
-{
-	GSList *accum = NULL;
-	g_hash_table_foreach (hash,
-		(GHFunc )cb_hash_collect_values, &accum);
-	return accum;
-}
-
-/***************************************************************************/
-void
-go_ptr_array_insert (GPtrArray *array, gpointer value, int index)
-{
-	if (index < (int)array->len) {
-		int i = array->len - 1;
-		gpointer last = g_ptr_array_index (array, i);
-		g_ptr_array_add (array, last);
-
-		while (i-- > index) {
-			gpointer tmp = g_ptr_array_index (array, i);
-			g_ptr_array_index (array, i + 1) = tmp;
-		}
-		g_ptr_array_index (array, index) = value;
-	} else
-		g_ptr_array_add (array, value);
-}
-
-/**
- * go_slist_create:
- * @item1: itionally %NULL
- * @Varargs : %NULL terminated list of additional items
- *
- * Creates a GList from NULL-terminated list of arguments.
- *
- * Returns: created list.
- **/
-GSList *
-go_slist_create (gpointer item1, ...)
-{
-	va_list args;
-	GSList *list = NULL;
-	gpointer item;
-
-	va_start (args, item1);
-	for (item = item1; item != NULL; item = va_arg (args, gpointer)) {
-		list = g_slist_prepend (list, item);
-	}
-	va_end (args);
-
-	return g_slist_reverse (list);
-}
-
-/**
- * go_slist_map:
- * @list        : list of some items
- * @map_func    : mapping function
- *
- **/
-GSList *
-go_slist_map (GSList const *list, GOMapFunc map_func)
-{
-	GSList *list_copy = NULL;
-
-	GO_SLIST_FOREACH (list, void, value,
-		GO_SLIST_PREPEND (list_copy, map_func (value))
-	);
-
-	return g_slist_reverse (list_copy);
-}
-
-/**
- * go_slist_free_custom:
- * @list: list of some items
- * @free_func: function freeing list item
- *
- * Clears a list, calling @free_func for each list item.
- **/
-void
-go_slist_free_custom (GSList *list, GFreeFunc free_func)
-{
-	GSList *l;
-
-	for (l = list; l != NULL; l = l->next) {
-		free_func (l->data);
-	}
-	g_slist_free (list);
-}
-
-gint
-go_list_index_custom (GList *list, gpointer data, GCompareFunc cmp_func)
-{
-	GList *l;
-	gint i;
-
-	for (l = list, i = 0; l != NULL; l = l->next, i++) {
-		if (cmp_func (l->data, data) == 0) {
-			return i;
-		}
-	}
-
-	return -1;
-}
-
-/**
- * go_list_free_custom:
- * @list: list of some items
- * @free_func: function freeing list item
- *
- * Clears a list, calling @free_func for each list item.
- *
- */
-void
-go_list_free_custom (GList *list, GFreeFunc free_func)
-{
-	GList *l;
-
-	for (l = list; l != NULL; l = l->next) {
-		free_func (l->data);
-	}
-	g_list_free (list);
-}
-
-/**
- * go_strsplit_to_slist:
- * @str: String to split
- * @delimiter: Token delimiter
- *
- * Splits up string into tokens at delim and returns a string list.
- *
- * Returns: string list which you should free after use using function
- * e_free_string_list().
- **/
-GSList *
-go_strsplit_to_slist (gchar const *string, gchar delimiter)
-{
-	gchar **token_v;
-	GSList *string_list = NULL;
-	char buf[2] = { '\0', '\0' };
-	gint i;
-
-	buf[0] = delimiter;
-	token_v = g_strsplit (string, buf, 0);
-	if (token_v != NULL) {
-		for (i = 0; token_v[i] != NULL; i++) {
-			string_list = g_slist_prepend (string_list, token_v[i]);
-		}
-		string_list = g_slist_reverse (string_list);
-		g_free (token_v);
-	}
-
-	return string_list;
-}
-
-gint
-go_utf8_collate_casefold (const char *a, const char *b)
-{
-	char *a2 = g_utf8_casefold (a, -1);
-	char *b2 = g_utf8_casefold (b, -1);
-	int res = g_utf8_collate (a2, b2);
-	g_free (a2);
-	g_free (b2);
-	return res;
-}
 
 gint
 go_ascii_strcase_equal (gconstpointer v1, gconstpointer v2)
@@ -257,441 +52,6 @@ go_ascii_strcase_hash (gconstpointer v)
 	return h /* % M */;
 }
 
-/* ------------------------------------------------------------------------- */
-
-/*
- * Escapes all backslashes and quotes in a string. It is based on glib's
- * g_strescape.
- *
- * Also adds quotes around the result.
- */
-void
-go_strescape (GString *target, char const *string)
-{
-	g_string_append_c (target, '"');
-	/* This loop should be UTF-8 safe.  */
-	for (; *string; string++) {
-		switch (*string) {
-		case '"':
-		case '\\':
-			g_string_append_c (target, '\\');
-		default:
-			g_string_append_c (target, *string);
-		}
-	}
-	g_string_append_c (target, '"');
-}
-
-/*
- * The reverse operation of go_strescape.  Returns a pointer to the
- * first char after the string on success or NULL on failure.
- *
- * First character of the string should be an ASCII character used
- * for quoting.
- */
-const char *
-go_strunescape (GString *target, const char *string)
-{
-	char quote = *string++;
-	size_t oldlen = target->len;
-
-	/* This should be UTF-8 safe as long as quote is ASCII.  */
-	while (*string != quote) {
-		if (*string == 0)
-			goto error;
-		else if (*string == '\\') {
-			string++;
-			if (*string == 0)
-				goto error;
-		}
-
-		g_string_append_c (target, *string);
-		string++;
-	}
-
-	return ++string;
-
- error:
-	g_string_truncate (target, oldlen);
-	return NULL;
-}
-
-void
-go_string_append_gstring (GString *target, const GString *source)
-{
-	g_string_append_len (target, source->str, source->len);
-}
-
-void
-go_string_append_c_n (GString *target, char c, gsize n)
-{
-	gsize len = target->len;
-	g_string_set_size (target, len + n);
-	memset (target->str + len, c, n);
-}
-
-void
-go_string_replace (GString *target,
-		   gsize pos, gssize oldlen,
-		   const char *txt, gssize newlen)
-{
-	gsize cplen;
-
-	g_return_if_fail (target != NULL);
-	g_return_if_fail (pos >= 0);
-	g_return_if_fail (pos <= target->len);
-
-	if (oldlen < 0)
-		oldlen = target->len - pos;
-	if (newlen < 0)
-		newlen = strlen (txt);
-
-	cplen = MIN (oldlen, newlen);
-	memcpy (target->str + pos, txt, cplen);
-
-	pos += cplen;
-	oldlen -= cplen;
-	txt += cplen;
-	newlen -= cplen;
-
-	/*
-	 * At least one of oldlen and newlen is zero now.  We could call
-	 * both erase and insert unconditionally, but erase does not appear
-	 * to handle zero length efficiently.
-	 */
-
-	if (oldlen > 0)
-		g_string_erase (target, pos, oldlen);
-	else if (newlen > 0)
-		g_string_insert_len (target, pos, txt, newlen);
-}
-
-/* ------------------------------------------------------------------------- */
-
-/**
- * go_utf8_strcapital:
- * @p: pointer to UTF-8 string
- * @len: length in bytes, or -1.
- *
- * Similar to g_utf8_strup and g_utf8_strup, except that this function
- * creates a string "Very Much Like: This, One".
- *
- * Return value: newly allocated string.
- **/
-char *
-go_utf8_strcapital (const char *p, gssize len)
-{
-	const char *pend = (len < 0 ? NULL : p + len);
-	GString *res = g_string_sized_new (len < 0 ? 1 : len + 1);
-	gboolean up = TRUE;
-
-	/*
-	 * This does a simple character-by-character mapping and probably
-	 * is not linguistically correct.
-	 */
-
-	for (; (len < 0 || p < pend) && *p; p = g_utf8_next_char (p)) {
-		gunichar c = g_utf8_get_char (p);
-
-		if (g_unichar_isalpha (c)) {
-			if (up ? g_unichar_isupper (c) : g_unichar_islower (c))
-				/* Correct case -- keep the char.  */
-				g_string_append_unichar (res, c);
-			else {
-				char *tmp = up
-					? g_utf8_strup (p, 1)
-					: g_utf8_strdown (p, 1);
-				g_string_append (res, tmp);
-				g_free (tmp);
-			}
-			up = FALSE;
-		} else {
-			g_string_append_unichar (res, c);
-			up = TRUE;
-		}
-	}
-
-	return g_string_free (res, FALSE);
-}
-
-/* ------------------------------------------------------------------------- */
-
-#undef DEBUG_CHUNK_ALLOCATOR
-
-typedef struct _go_mem_chunk_freeblock go_mem_chunk_freeblock;
-typedef struct _go_mem_chunk_block go_mem_chunk_block;
-
-struct _go_mem_chunk_freeblock {
-	go_mem_chunk_freeblock *next;
-};
-
-struct _go_mem_chunk_block {
-	gpointer data;
-	int freecount, nonalloccount;
-	go_mem_chunk_freeblock *freelist;
-#ifdef DEBUG_CHUNK_ALLOCATOR
-	int id;
-#endif
-};
-
-struct _GOMemChunk {
-	char *name;
-	size_t atom_size, user_atom_size, chunk_size, alignment;
-	int atoms_per_block;
-
-	/* List of all blocks.  */
-	GSList *blocklist;
-
-	/* List of blocks that are not full.  */
-	GList *freeblocks;
-
-#ifdef DEBUG_CHUNK_ALLOCATOR
-	int blockid;
-#endif
-};
-
-
-GOMemChunk *
-go_mem_chunk_new (char const *name, size_t user_atom_size, size_t chunk_size)
-{
-	int atoms_per_block;
-	GOMemChunk *res;
-	size_t user_alignment, alignment, atom_size;
-	size_t maxalign = 1 + ((sizeof (void *) - 1) |
-			       (sizeof (long) - 1) |
-			       (sizeof (double) - 1));
-
-	/*
-	 * The alignment that the caller can expect is 2^(lowest_bit_in_size).
-	 * The fancy bit math computes this.  Think it over.
-	 *
-	 * We don't go lower than pointer-size, so this always comes out as
-	 * 4 or 8.  Sometimes, when user_atom_size is a multiple of 8, this
-	 * alignment is bigger than really needed, but we don't know if the
-	 * structure has elements with 8-byte alignment.  In those cases we
-	 * waste memory.
-	 */
-	user_alignment = ((user_atom_size ^ (user_atom_size - 1)) + 1) / 2;
-	alignment = MIN (MAX (user_alignment, sizeof (go_mem_chunk_block *)), maxalign);
-	atom_size = alignment + MAX (user_atom_size, sizeof (go_mem_chunk_freeblock));
-	atoms_per_block = MAX (1, chunk_size / atom_size);
-	chunk_size = atoms_per_block * atom_size;
-
-#ifdef DEBUG_CHUNK_ALLOCATOR
-	g_print ("Created %s with alignment=%d, atom_size=%d (%d), chunk_size=%d.\n",
-		 name, alignment, atom_size, user_atom_size,
-		 chunk_size);
-#endif
-
-	res = g_new (GOMemChunk, 1);
-	res->alignment = alignment;
-	res->name = g_strdup (name);
-	res->user_atom_size = user_atom_size;
-	res->atom_size = atom_size;
-	res->chunk_size = chunk_size;
-	res->atoms_per_block = atoms_per_block;
-	res->blocklist = NULL;
-	res->freeblocks = NULL;
-#ifdef DEBUG_CHUNK_ALLOCATOR
-	res->blockid = 0;
-#endif
-
-	return res;
-}
-
-void
-go_mem_chunk_destroy (GOMemChunk *chunk, gboolean expect_leaks)
-{
-	GSList *l;
-
-	g_return_if_fail (chunk != NULL);
-
-#ifdef DEBUG_CHUNK_ALLOCATOR
-	g_print ("Destroying %s.\n", chunk->name);
-#endif
-	/*
-	 * Since this routine frees all memory allocated for the pool,
-	 * it is sometimes convenient not to free at all.  For such
-	 * cases, don't report leaks.
-	 */
-	if (!expect_leaks) {
-		GSList *l;
-		int leaked = 0;
-
-		for (l = chunk->blocklist; l; l = l->next) {
-			go_mem_chunk_block *block = l->data;
-			leaked += chunk->atoms_per_block - (block->freecount + block->nonalloccount);
-		}
-		if (leaked) {
-			g_warning ("Leaked %d nodes from %s.",
-				   leaked, chunk->name);
-		}
-	}
-
-	for (l = chunk->blocklist; l; l = l->next) {
-		go_mem_chunk_block *block = l->data;
-		g_free (block->data);
-		g_free (block);
-	}
-	g_slist_free (chunk->blocklist);
-	g_list_free (chunk->freeblocks);
-	g_free (chunk->name);
-	g_free (chunk);
-}
-
-gpointer
-go_mem_chunk_alloc (GOMemChunk *chunk)
-{
-	go_mem_chunk_block *block;
-	char *res;
-
-	/* First try the freelist.  */
-	if (chunk->freeblocks) {
-		go_mem_chunk_freeblock *res;
-
-		block = chunk->freeblocks->data;
-		res = block->freelist;
-		if (res) {
-			block->freelist = res->next;
-
-			block->freecount--;
-			if (block->freecount == 0 && block->nonalloccount == 0) {
-				/* Block turned full -- remove it from freeblocks.  */
-				chunk->freeblocks = g_list_delete_link (chunk->freeblocks,
-									chunk->freeblocks);
-			}
-			return res;
-		}
-		/*
-		 * If we get here, the block has free space that was never
-		 * allocated.
-		 */
-	} else {
-		block = g_new (go_mem_chunk_block, 1);
-#ifdef DEBUG_CHUNK_ALLOCATOR
-		block->id = chunk->blockid++;
-		g_print ("Allocating new chunk %d for %s.\n", block->id, chunk->name);
-#endif
-		block->nonalloccount = chunk->atoms_per_block;
-		block->freecount = 0;
-		block->data = g_malloc (chunk->chunk_size);
-		block->freelist = NULL;
-
-		chunk->blocklist = g_slist_prepend (chunk->blocklist, block);
-		chunk->freeblocks = g_list_prepend (chunk->freeblocks, block);
-	}
-
-	res = (char *)block->data +
-		(chunk->atoms_per_block - block->nonalloccount--) * chunk->atom_size;
-	*((go_mem_chunk_block **)res) = block;
-
-	if (block->nonalloccount == 0 && block->freecount == 0) {
-		/* Block turned full -- remove it from freeblocks.  */
-		chunk->freeblocks = g_list_delete_link (chunk->freeblocks, chunk->freeblocks);
-	}
-
-	return res + chunk->alignment;
-}
-
-gpointer
-go_mem_chunk_alloc0 (GOMemChunk *chunk)
-{
-	gpointer res = go_mem_chunk_alloc (chunk);
-	memset (res, 0, chunk->user_atom_size);
-	return res;
-}
-
-void
-go_mem_chunk_free (GOMemChunk *chunk, gpointer mem)
-{
-	go_mem_chunk_freeblock *fb = mem;
-	go_mem_chunk_block *block =
-		*((go_mem_chunk_block **)((char *)mem - chunk->alignment));
-
-#if 0
-	/*
-	 * This is useful when the exact location of a leak needs to be
-	 * pin-pointed.
-	 */
-	memset (mem, 0, chunk->user_atom_size);
-#endif
-
-	fb->next = block->freelist;
-	block->freelist = fb;
-	block->freecount++;
-
-	if (block->freecount == 1 && block->nonalloccount == 0) {
-		/* Block turned non-full.  */
-		chunk->freeblocks = g_list_prepend (chunk->freeblocks, block);
-	} else if (block->freecount == chunk->atoms_per_block) {
-		/* Block turned all-free.  */
-
-#ifdef DEBUG_CHUNK_ALLOCATOR
-		g_print ("Releasing chunk %d for %s.\n", block->id, chunk->name);
-#endif
-		/*
-		 * FIXME -- this could be faster if we rolled our own lists.
-		 * Hopefully, however, (a) the number of blocks is small,
-		 * and (b) the freed block might be near the beginning ("top")
-		 * of the stacks.
-		 */
-		chunk->blocklist = g_slist_remove (chunk->blocklist, block);
-		chunk->freeblocks = g_list_remove (chunk->freeblocks, block);
-
-		g_free (block->data);
-		g_free (block);
-	}
-}
-
-/*
- * Loop over all non-freed memory in the chunk.  It's safe to allocate or free
- * from the chunk in the callback.
- */
-void
-go_mem_chunk_foreach_leak (GOMemChunk *chunk, GFunc cb, gpointer user)
-{
-	GSList *l, *leaks = NULL;
-
-	for (l = chunk->blocklist; l; l = l->next) {
-		go_mem_chunk_block *block = l->data;
-		if (chunk->atoms_per_block - (block->freecount + block->nonalloccount) > 0) {
-			char *freed = g_new0 (char, chunk->atoms_per_block);
-			go_mem_chunk_freeblock *fb = block->freelist;
-			int i;
-
-			while (fb) {
-				char *atom = (char *)fb - chunk->alignment;
-				int no = (atom - (char *)block->data) / chunk->atom_size;
-				freed[no] = 1;
-				fb = fb->next;
-			}
-
-			for (i = chunk->atoms_per_block - block->nonalloccount - 1; i >= 0; i--) {
-				if (!freed[i]) {
-					char *atom = (char *)block->data + i * chunk->atom_size;
-					leaks = g_slist_prepend (leaks, atom + chunk->alignment);
-				}
-			}
-			g_free (freed);
-		}
-	}
-
-	g_slist_foreach (leaks, cb, user);
-	g_slist_free (leaks);
-}
-
-int
-go_str_compare (void const *x, void const *y)
-{
-	if (x == y)
-		return 0;
-
-	if (x == NULL || y == NULL)
-		return x ? -1 : 1;
-
-	return strcmp (x, y);
-}
-
 
 const char *
 go_guess_encoding (const char *raw, size_t len, const char *user_guess,
@@ -763,320 +123,3 @@ go_guess_encoding (const char *raw, size_t len, const char *user_guess,
 		g_error_free (error);
 	}
 }
-
-/**
- * go_get_real_name :
- *
- * Returns: a utf8 encoded string with the current user name.
- * 	Caller should _NOT_ free the result.
- **/
-char const *
-go_get_real_name (void)
-{
-	/* We will leak this.  */
-	static char *go_real_name = NULL;
-
-	if (go_real_name == NULL) {
-		char const *name = getenv ("NAME");
-		if (name == NULL)
-			name = g_get_real_name ();
-		if (name == NULL)
-			name = g_get_user_name ();
-		if (name != NULL)
-			(void) go_guess_encoding (name, strlen (name),
-				NULL, &go_real_name);
-		else
-			go_real_name = (char *)"unknown";
-	}
-	return go_real_name;
-}
-
-/**
- * go_destroy_password :
- * @passwd : The buffer to clear
- *
- * Overwrite a string holding a password.  This is a separate routine to
- * ensure that the compiler does not try to outsmart us.
- *
- * Note: this does not free the memory.
- **/
-void
-go_destroy_password (char *passwd)
-{
-	memset (passwd, 0, strlen (passwd));
-}
-
-
-/**
- * go_object_toggle:
- * @object : #GObject
- * @property_name : name
- *
- * Toggle a boolean object property.
- **/
-void
-go_object_toggle (gpointer object, const gchar *property_name)
-{
-	gboolean value = FALSE;
-	GParamSpec *pspec;
-
-	g_return_if_fail (G_IS_OBJECT (object));
-	g_return_if_fail (property_name != NULL);
-
-	pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object), property_name);
-	if (!pspec ||
-	    !G_IS_PARAM_SPEC_BOOLEAN (pspec) ||
-	    ((pspec->flags & (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)) !=
-	     G_PARAM_READWRITE)) {
-		g_warning ("%s: object class `%s' has no boolean property named `%s' that can be both read and written.",
-			   G_STRFUNC,
-			   G_OBJECT_TYPE_NAME (object),
-			   property_name);
-		return;
-	}
-
-	/* And now, the actual action.  */
-	g_object_get (object, property_name, &value, NULL);
-	g_object_set (object, property_name, !value, NULL);
-}
-
-
-gboolean
-go_object_set_property (GObject *obj, const char *property_name,
-			const char *user_prop_name, const char *value,
-			GError **err,
-			const char *error_template)
-{
-	GParamSpec *pspec;
-
-	if (err) *err = NULL;
-
-	g_return_val_if_fail (G_IS_OBJECT (obj), TRUE);
-	g_return_val_if_fail (property_name != NULL, TRUE);
-	g_return_val_if_fail (user_prop_name != NULL, TRUE);
-	g_return_val_if_fail (value != NULL, TRUE);
-
-	pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (obj),
-					      property_name);
-	g_return_val_if_fail (pspec != NULL, TRUE);
-
-	if (G_IS_PARAM_SPEC_STRING (pspec)) {
-		g_object_set (obj, property_name, value, NULL);
-		return FALSE;
-	}
-
-	if (G_IS_PARAM_SPEC_BOOLEAN (pspec)) {
-		gboolean b;
-
-		if (go_utf8_collate_casefold (value, go_locale_boolean_name (TRUE)) == 0 ||
-		    go_utf8_collate_casefold (value, _("yes")) == 0 ||
-		    g_ascii_strcasecmp (value, "TRUE") == 0 ||
-		    g_ascii_strcasecmp (value, "yes") == 0 ||
-		    strcmp (value, "1") == 0)
-			b = TRUE;
-		else if (go_utf8_collate_casefold (value, go_locale_boolean_name (FALSE)) == 0 ||
-		    go_utf8_collate_casefold (value, _("no")) == 0 ||
-		    g_ascii_strcasecmp (value, "FALSE") == 0 ||
-		    g_ascii_strcasecmp (value, "no") == 0 ||
-		    strcmp (value, "0") == 0)
-			b = FALSE;
-		else
-			goto error;
-
-		g_object_set (obj, property_name, b, NULL);
-		return FALSE;
-	}
-
-	if (G_IS_PARAM_SPEC_ENUM (pspec)) {
-		GEnumClass *eclass = ((GParamSpecEnum *)pspec)->enum_class;
-		GEnumValue *ev;
-
-		ev = g_enum_get_value_by_name (eclass, value);
-		if (!ev) ev = g_enum_get_value_by_nick (eclass, value);
-
-		if (!ev)
-			goto error;
-
-		g_object_set (obj, property_name, ev->value, NULL);
-		return FALSE;
-	}
-
-	error:
-		if (err)
-			*err = g_error_new (go_error_invalid (), 0,
-					    error_template,
-					    user_prop_name,
-					    value);
-		return TRUE;
-}
-
-
-
-
-/*
- * Collect all rw properties and their values.
- */
-GSList *
-go_object_properties_collect (GObject *obj)
-{
-	GSList *res = NULL;
-	guint n;
-	GParamSpec **pspecs =
-		g_object_class_list_properties (G_OBJECT_GET_CLASS (obj),
-						&n);
-
-	while (n--) {
-		GParamSpec *pspec = pspecs[n];
-		if ((pspec->flags & (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)) ==
-		    G_PARAM_READWRITE) {
-			GValue *value = g_new0 (GValue, 1);
-			g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec));
-			g_object_get_property (obj, pspec->name, value);
-			res = g_slist_prepend (res, value);
-			res = g_slist_prepend (res, pspec);
-		}
-	}
-
-	g_free (pspecs);
-	return res;
-}
-
-void
-go_object_properties_apply (GObject *obj, GSList *props, gboolean changed_only)
-{
-	GValue current = { 0, };
-
-	for (; props; props = props->next->next) {
-		GParamSpec *pspec = props->data;
-		const GValue *value = props->next->data;
-		gboolean doit;
-
-		if (changed_only) {
-			g_value_init (&current,
-				      G_PARAM_SPEC_VALUE_TYPE (pspec));
-			g_object_get_property (obj, pspec->name, &current);
-			doit = g_param_values_cmp (pspec, &current, value);
-#if 0
-			g_print ("%2d:  old: [%s]   new: [%s]\n",
-				 g_param_values_cmp (pspec, &current, value),
-				 g_strdup_value_contents (value),
-				 g_strdup_value_contents (&current));
-#endif
-			g_value_unset (&current);
-		} else
-			doit = TRUE;
-
-		if (doit)
-			g_object_set_property (obj, pspec->name, value);
-	}
-}
-
-void
-go_object_properties_free (GSList *props)
-{
-	GSList *l;
-
-	for (l = props; l; l = l->next->next) {
-		GValue *value = l->next->data;
-		g_value_unset (value);
-		g_free (value);
-	}
-
-	g_slist_free (props);
-}
-
-
-/**
- * go_parse_key_value:
- * @options: Options string.
- * @err: Reference to store GError if parsing fails.
- * @handler: Handler to call for each key-value pair.
- * @user: user pointer.
- */
-gboolean
-go_parse_key_value (const char *options,
-		    GError **err,
-		    gboolean (*handler) (const char *name,
-					 const char *value,
-					 GError **err,
-					 gpointer user),
-		    gpointer user)
-{
-	GString *sname = g_string_new (NULL);
-	GString *svalue = g_string_new (NULL);
-	gboolean res = FALSE;
-
-	if (err) *err = NULL;
-
-	while (1) {
-		const char *p;
-
-		g_string_truncate (sname, 0);
-		g_string_truncate (svalue, 0);
-
-		while (g_unichar_isspace (g_utf8_get_char (options)))
-			options = g_utf8_next_char (options);
-
-		if (*options == 0)
-			break;
-
-		if (*options == '"' || *options == '\'') {
-			options = go_strunescape (sname, options);
-			if (!options)
-				goto open_string;
-		} else {
-			p = options;
-			while (strchr ("-!_.,:;|/$%#@~", *options) ||
-			       g_unichar_isalnum (g_utf8_get_char (options)))
-				options = g_utf8_next_char (options);
-			g_string_append_len (sname, p, options - p);
-			if (p == options)
-				goto syntax;
-		}
-
-		while (g_unichar_isspace (g_utf8_get_char (options)))
-			options = g_utf8_next_char (options);
-		if (*options != '=')
-			goto syntax;
-		options++;
-		while (g_unichar_isspace (g_utf8_get_char (options)))
-			options = g_utf8_next_char (options);
-
-		if (*options == '"' || *options == '\'') {
-			options = go_strunescape (svalue, options);
-			if (!options)
-				goto open_string;
-		} else {
-			p = options;
-			while (*options && !
-			       g_unichar_isspace (g_utf8_get_char (options)))
-				options = g_utf8_next_char (options);
-			g_string_append_len (svalue, p, options - p);
-		}
-
-		if (handler (sname->str, svalue->str, err, user)) {
-			res = TRUE;
-			break;
-		}
-	}
-
-done:
-	g_string_free (sname, TRUE);
-	g_string_free (svalue, TRUE);
-
-	return res;
-
-open_string:
-	if (err)
-		*err = g_error_new (go_error_invalid (), 0,
-				    _("Quoted string not terminated"));
-	res = TRUE;
-	goto done;
-
-syntax:
-	if (err)
-		*err = g_error_new (go_error_invalid (), 0,
-				    _("Syntax error"));
-	res = TRUE;
-	goto done;
-}
diff --git a/lib/goffice/go-glib-extras.h b/lib/goffice/go-glib-extras.h
index 4733a5d..b58ca81 100644
--- a/lib/goffice/go-glib-extras.h
+++ b/lib/goffice/go-glib-extras.h
@@ -1,119 +1,18 @@
 #ifndef GO_GLIB_EXTRAS_H
 #define GO_GLIB_EXTRAS_H
 
-#include <goffice/goffice.h>
+#include <glib.h>
 
 G_BEGIN_DECLS
 
-/* Misc convenience routines that would be nice to have in glib */
 
-typedef gpointer (*GOMapFunc) (gpointer value);
-
-void	 go_ptr_array_insert	(GPtrArray *array, gpointer value, int index);
-
-GSList	*go_hash_keys		(GHashTable *hash);
-GSList	*go_hash_values		(GHashTable *hash);
-
-GSList	*go_slist_map		(GSList const *list, GOMapFunc map_func);
-GSList	*go_slist_create	(gpointer item1, ...);
-void	 go_slist_free_custom	(GSList *list, GFreeFunc free_func);
-#define	 go_string_slist_copy(list) go_slist_map (list, (GOMapFunc) g_strdup)
-GSList	*go_strsplit_to_slist	(char const *str, gchar delimiter);
-#define GO_SLIST_FOREACH(list,valtype,val,stmnt) \
-G_STMT_START { \
-	GSList const *go_l; \
-	for (go_l = (list); go_l != NULL; go_l = go_l->next) { \
-		valtype *val = go_l->data; \
-		stmnt \
-		; \
-	} \
-} G_STMT_END
-#define GO_SLIST_PREPEND(list,item) \
-	(list = g_slist_prepend (list, item))
-#define GO_SLIST_APPEND(list,item) \
-	(list = g_slist_append (list, item))
-#define GO_SLIST_REMOVE(list,item) \
-	(list = g_slist_remove (list, item))
-#define GO_SLIST_CONCAT(list_a,list_b) \
-	(list_a = g_slist_concat (list_a, list_b))
-#define GO_SLIST_REVERSE(list) \
-	(list = g_slist_reverse (list))
-#define GO_SLIST_SORT(list,cmp_func) \
-	(list = g_slist_sort (list, cmp_func))
-
-gint go_list_index_custom (GList *list, gpointer data, GCompareFunc cmp_func);
-void go_list_free_custom  (GList *list, GFreeFunc free_func);
-#define GO_LIST_FOREACH(list,valtype,val,stmnt) \
-G_STMT_START { \
-	GList *go_l; \
-	for (go_l = (list); go_l != NULL; go_l = go_l->next) { \
-		valtype *val = go_l->data; \
-		stmnt \
-		; \
-	} \
-} G_STMT_END
-#define GO_LIST_PREPEND(list,item) \
-	(list = g_list_prepend (list, item))
-#define GO_LIST_APPEND(list,item) \
-	(list = g_list_append (list, item))
-#define GO_LIST_REMOVE(list,item) \
-	(list = g_list_remove (list, item))
-#define GO_LIST_CONCAT(list_a,list_b) \
-	(list_a = g_list_concat (list_a, list_b))
-#define GO_LIST_REVERSE(list) \
-	(list = g_list_reverse (list))
-#define GO_LIST_SORT(list,cmp_func) \
-	(list = g_list_sort (list, cmp_func))
-
-int	    go_str_compare		(void const *x, void const *y);
 guint	    go_ascii_strcase_hash	(gconstpointer v);
 gint	    go_ascii_strcase_equal	(gconstpointer v, gconstpointer v2);
-gint	    go_utf8_collate_casefold	(char const *a, char const *b);
-char	   *go_utf8_strcapital		(char const *p, gssize len);
-void	    go_strescape		(GString *target, char const *str);
-char const *go_strunescape		(GString *target, char const *str);
-void	    go_string_append_gstring	(GString *target, const GString *src);
-void        go_string_append_c_n        (GString *target, char c, gsize n);
-void        go_string_replace           (GString *target,
-					 gsize pos, gssize oldlen,
-					 const char *txt, gssize newlen);
 
 char const *go_guess_encoding		(char const *raw, gsize len,
 					 char const *user_guess,
 					 char **utf8_str);
 
-char const *go_get_real_name		(void);
-void	    go_destroy_password	(char *passwd);
-
-GOMemChunk  *go_mem_chunk_new		(char const *name, gsize user_atom_size, gsize chunk_size);
-void	     go_mem_chunk_destroy	(GOMemChunk *chunk, gboolean expect_leaks);
-gpointer     go_mem_chunk_alloc		(GOMemChunk *chunk);
-gpointer     go_mem_chunk_alloc0	(GOMemChunk *chunk);
-void         go_mem_chunk_free		(GOMemChunk *chunk, gpointer mem);
-void         go_mem_chunk_foreach_leak	(GOMemChunk *chunk, GFunc cb, gpointer user);
-
-void	go_object_toggle             (gpointer object,
-				      const gchar *property_name);
-gboolean go_object_set_property (GObject *obj, const char *property_name,
-				 const char *user_prop_name, const char *value,
-				 GError **err,
-				 const char *error_template);
-GSList *go_object_properties_collect (GObject *obj);
-void    go_object_properties_apply   (GObject *obj,
-				      GSList *props,
-				      gboolean changed_only);
-void    go_object_properties_free    (GSList *props);
-
-typedef gboolean (*GOParseKeyValueFunc) (const char *name,
-		  const char *value,
-		  GError **err,
-		  gpointer user);
-
-gboolean go_parse_key_value (const char *options,
-			     GError **err,
-			     GOParseKeyValueFunc handler,
-			     gpointer user);
-
 G_END_DECLS
 
 #endif /* GO_GLIB_EXTRAS_H */
diff --git a/lib/goffice/go-optionmenu.c b/lib/goffice/go-optionmenu.c
index c5c3c30..daca835 100644
--- a/lib/goffice/go-optionmenu.c
+++ b/lib/goffice/go-optionmenu.c
@@ -31,9 +31,8 @@
  * USA.
  */
 
-#include <goffice/goffice-config.h>
+#include "config.h"
 #include "go-optionmenu.h"
-#include <goffice/gtk/go-gtk-compat.h>
 
 #include <gdk/gdkkeysyms.h>
 #include <glib/gi18n-lib.h>
@@ -265,7 +264,7 @@ go_option_menu_set_menu (GOOptionMenu *option_menu,
 		return;
 
 	if (option_menu->menu) {
-		if (gtk_menu_shell_get_active (option_menu->menu))
+		if (option_menu->menu->active)
 			gtk_menu_shell_cancel (option_menu->menu);
 
 		handle_menu_signals (option_menu, FALSE);

commit d858c7d51db343a4495721266cb8484548036ab0
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Thu Jan 28 09:12:43 2016 +0100

    Internalize parts of the goffice code
    
    We're using only a tiny fraction of that library.
    Adding it directly in the gnucash source saves a large dependency chain.
    
    Note this is a verbatim copy of these files from
    https://github.com/GNOME/goffice/tree/GOFFICE_0_8_17
    to preserve a clean link with it's origin.
    It will only build correctly after the next commit.

diff --git a/configure.ac b/configure.ac
index 10461de..2ce9c19 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1697,6 +1697,7 @@ AC_CONFIG_FILES(
   doc/Makefile
   doc/examples/Makefile
   lib/Makefile
+  lib/goffice/Makefile
   lib/libc/Makefile
   lib/stf/Makefile
   packaging/Makefile
diff --git a/lib/Makefile.am b/lib/Makefile.am
index a593f13..8f01526 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -1,5 +1,5 @@
 if GNUCASH_ENABLE_GUI
-SUBDIRS = libc stf
+SUBDIRS = libc stf goffice
 else
 SUBDIRS = libc
 endif
diff --git a/lib/goffice/Makefile.am b/lib/goffice/Makefile.am
new file mode 100644
index 0000000..29aafab
--- /dev/null
+++ b/lib/goffice/Makefile.am
@@ -0,0 +1,17 @@
+noinst_LTLIBRARIES = libgnc-goffice.la
+
+REALSRCS = go-optionmenu.c \
+           go-charmap-sel.c \
+           go-glib-extras.c
+REALHDRS = go-optionmenu.h \
+           go-charmap-sel.h \
+           go-glib-extras.h
+
+libgnc_goffice_la_SOURCES = ${REALSRCS}
+noinst_HEADERS = ${REALHDRS}
+
+libgnc_goffice_la_LIBADD = $(GTK_LIBS)
+
+AM_CPPFLAGS = $(GTK_CFLAGS)
+
+EXTRA_DIST = $(REALSRCS) $(REALHDRS)
diff --git a/lib/goffice/README b/lib/goffice/README
new file mode 100644
index 0000000..814e471
--- /dev/null
+++ b/lib/goffice/README
@@ -0,0 +1,13 @@
+This code is extracted from goffice.
+
+In particular,
+- go-charmap-sel is a widget to allow the user to select an encoding
+- go-option-menu is a widget used by go-charmap-sel to add manu items
+  with a radio button.
+- go-glib-extras is copied because we use a few functions from it:
+  - go_ascii_strcase_equal and go_ascii_strcase_hash
+  - go_guess_encoding
+
+This code is currently used by the csv importer, but could be useful
+in several interfaces that require the user to select an encoding
+(various importers and exporters mostly).
diff --git a/lib/goffice/go-charmap-sel.c b/lib/goffice/go-charmap-sel.c
new file mode 100644
index 0000000..287133b
--- /dev/null
+++ b/lib/goffice/go-charmap-sel.c
@@ -0,0 +1,691 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * A charmap selector widget.
+ *
+ *  Copyright (C) 2003-2005 Andreas J. Guelzow
+ *
+ *  based on code by:
+ *  Copyright (C) 2000 Marco Pesenti Gritti
+ *  from the galeon code base
+ *
+ *  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 2, 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, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include <goffice/goffice-config.h>
+#include "go-charmap-sel.h"
+#include "go-optionmenu.h"
+#include <goffice/utils/go-glib-extras.h>
+#include <glib/gi18n-lib.h>
+#include <gsf/gsf-impl-utils.h>
+#include <string.h>
+#include <stdlib.h>
+
+#define CS(x) GO_CHARMAP_SEL (x)
+
+#define CHARMAP_NAME_KEY "Name of Character Encoding"
+
+/* ------------------------------------------------------------------------- */
+
+typedef enum {
+	LG_ARABIC,
+	LG_BALTIC,
+	LG_CENTRAL_EUROPEAN,
+	LG_CHINESE,
+	LG_CYRILLIC,
+	LG_GREEK,
+	LG_HEBREW,
+	LG_INDIAN,
+	LG_JAPANESE,
+	LG_KOREAN,
+	LG_TURKISH,
+	LG_UNICODE,
+	LG_VIETNAMESE,
+	LG_WESTERN,
+	LG_OTHER,
+	LG_LAST
+} LanguageGroup;
+
+typedef struct {
+        char const *group_name;
+	LanguageGroup const lgroup;
+	/* Generated stuff follows.  */
+	char *collate_key;
+} LGroupInfo;
+
+static LGroupInfo lgroups[] = {
+	{N_("Arabic"), LG_ARABIC},
+	{N_("Baltic"), LG_BALTIC},
+	{N_("Central European"), LG_CENTRAL_EUROPEAN},
+	{N_("Chinese"), LG_CHINESE},
+	{N_("Cyrillic"), LG_CYRILLIC},
+	{N_("Greek"), LG_GREEK},
+	{N_("Hebrew"), LG_HEBREW},
+	{N_("Indian"), LG_INDIAN},
+	{N_("Japanese"), LG_JAPANESE},
+	{N_("Korean"), LG_KOREAN},
+	{N_("Turkish"), LG_TURKISH},
+	{N_("Unicode"), LG_UNICODE},
+	{N_("Vietnamese"), LG_VIETNAMESE},
+	{N_("Western"), LG_WESTERN},
+	{N_("Other"), LG_OTHER},
+	{NULL, LG_LAST}
+};
+
+static int
+lgroups_order (const void *_a, const void *_b)
+{
+	const LGroupInfo *a = (const LGroupInfo *)_a;
+	const LGroupInfo *b = (const LGroupInfo *)_b;
+
+	return strcmp (a->collate_key, b->collate_key);
+}
+
+/* ------------------------------------------------------------------------- */
+
+typedef enum {
+     CI_MINOR,
+     CI_MAJOR
+} CharsetImportance;
+
+typedef struct {
+	gchar const *charset_title;
+	gchar const *aliases;
+	LanguageGroup const lgroup;
+	CharsetImportance const imp;
+	/* Generated stuff follows.  */
+	char *collate_key;
+	char *to_utf8_iconv_name, *from_utf8_iconv_name;
+} CharsetInfo;
+
+static CharsetInfo charset_trans_array[] = {
+	{N_("Arabic (IBM-864)"),                  "IBM864",                LG_ARABIC, CI_MINOR},
+	{N_("Arabic (IBM-864-I)"),                "IBM864i",               LG_ARABIC, CI_MINOR},
+	{N_("Arabic (ISO-8859-6)"),               "ISO-8859-6",            LG_ARABIC, CI_MINOR},
+	{N_("Arabic (ISO-8859-6-E)"),             "ISO-8859-6-E",          LG_ARABIC, CI_MINOR},
+
+	{N_("Arabic (ISO-8859-6-I)"),             "ISO-8859-6-I",          LG_ARABIC, CI_MINOR},
+	{N_("Arabic (MacArabic)"),                "x-mac-arabic",          LG_ARABIC, CI_MINOR},
+	{N_("Arabic (Windows-1256)"),             "windows-1256",          LG_ARABIC, CI_MINOR},
+	{N_("Armenian (ARMSCII-8)"),              "armscii-8", 	           LG_OTHER, CI_MINOR},
+	{N_("Baltic (ISO-8859-13)"),              "ISO-8859-13",           LG_BALTIC, CI_MINOR},
+	{N_("Baltic (ISO-8859-4)"),               "ISO-8859-4",            LG_BALTIC, CI_MINOR},
+	{N_("Baltic (Windows-1257)"),             "windows-1257",          LG_BALTIC, CI_MINOR},
+	{N_("Celtic (ISO-8859-14)"),              "ISO-8859-14",           LG_OTHER, CI_MINOR},
+	{N_("Central European (IBM-852)"),        "IBM852",                LG_CENTRAL_EUROPEAN, CI_MINOR},
+	{N_("Central European (ISO-8859-2)"),     "ISO-8859-2",	           LG_CENTRAL_EUROPEAN, CI_MINOR},
+	{N_("Central European (MacCE)"),          "x-mac-ce",              LG_CENTRAL_EUROPEAN, CI_MINOR},
+	{N_("Central European (Windows-1250)"),   "windows-1250",          LG_CENTRAL_EUROPEAN, CI_MINOR},
+	{N_("Chinese Simplified (GB18030)"),      "gb18030",               LG_CHINESE, CI_MINOR},
+	{N_("Chinese Simplified (GB2312)"),       "GB2312",                LG_CHINESE, CI_MINOR},
+	{N_("Chinese Simplified (GBK)"),          "x-gbk",                 LG_CHINESE, CI_MINOR},
+	{N_("Chinese Simplified (HZ)"),           "HZ-GB-2312",	           LG_CHINESE, CI_MINOR},
+	{N_("Chinese Simplified (Windows-936)"),  "windows-936",           LG_CHINESE, CI_MINOR},
+	{N_("Chinese Traditional (Big5)"),        "Big5",                  LG_CHINESE, CI_MINOR},
+	{N_("Chinese Traditional (Big5-HKSCS)"),  "Big5-HKSCS",	           LG_CHINESE, CI_MINOR},
+	{N_("Chinese Traditional (EUC-TW)"),      "x-euc-tw",              LG_CHINESE, CI_MINOR},
+	{N_("Croatian (MacCroatian)"),            "x-mac-croatian",        LG_CENTRAL_EUROPEAN, CI_MINOR},
+	{N_("Cyrillic (IBM-855)"),                "IBM855",                LG_CYRILLIC, CI_MINOR},
+	{N_("Cyrillic (ISO-8859-5)"),             "ISO-8859-5",	           LG_CYRILLIC, CI_MINOR},
+	{N_("Cyrillic (ISO-IR-111)"),             "ISO-IR-111",	           LG_CYRILLIC, CI_MINOR},
+	{N_("Cyrillic (KOI8-R)"),                 "KOI8-R",                LG_CYRILLIC, CI_MINOR},
+	{N_("Cyrillic (MacCyrillic)"),            "x-mac-cyrillic",        LG_CYRILLIC, CI_MINOR},
+	{N_("Cyrillic (Windows-1251)"),           "windows-1251",          LG_CYRILLIC, CI_MINOR},
+	{N_("Russian (CP-866)"),                  "IBM866",                LG_CYRILLIC, CI_MINOR},
+	{N_("Ukrainian (KOI8-U)"),                "KOI8-U",                LG_CYRILLIC, CI_MINOR},
+	{N_("Ukrainian (MacUkrainian)"),          "x-mac-ukrainian",       LG_CYRILLIC, CI_MINOR},
+	{N_("English (ASCII)"),                   "ANSI_X3.4-1968#ASCII",  LG_WESTERN, CI_MAJOR},
+	{N_("Farsi (MacFarsi)"),                  "x-mac-farsi",           LG_OTHER, CI_MINOR},
+	{N_("Georgian (GEOSTD8)"),                "geostd8",               LG_OTHER, CI_MINOR},
+	{N_("Greek (ISO-8859-7)"),                "ISO-8859-7",            LG_GREEK, CI_MINOR},
+	{N_("Greek (MacGreek)"),                  "x-mac-greek",           LG_GREEK, CI_MINOR},
+	{N_("Greek (Windows-1253)"),              "windows-1253",          LG_GREEK, CI_MINOR},
+	{N_("Gujarati (MacGujarati)"),            "x-mac-gujarati",        LG_INDIAN, CI_MINOR},
+	{N_("Gurmukhi (MacGurmukhi)"),            "x-mac-gurmukhi",        LG_INDIAN, CI_MINOR},
+	{N_("Hebrew (IBM-862)"),                  "IBM862",                LG_HEBREW, CI_MINOR},
+	{N_("Hebrew (ISO-8859-8-E)"),             "ISO-8859-8-E",          LG_HEBREW, CI_MINOR},
+	{N_("Hebrew (ISO-8859-8-I)"),             "ISO-8859-8-I",          LG_HEBREW, CI_MINOR},
+	{N_("Hebrew (MacHebrew)"),                "x-mac-hebrew",          LG_HEBREW, CI_MINOR},
+	{N_("Hebrew (Windows-1255)"),             "windows-1255",          LG_HEBREW, CI_MINOR},
+	{N_("Hindi (MacDevanagari)"),             "x-mac-devanagari",      LG_INDIAN, CI_MINOR},
+	{N_("Icelandic (MacIcelandic)"),          "x-mac-icelandic",       LG_OTHER, CI_MINOR},
+	{N_("Japanese (EUC-JP)"),                 "EUC-JP",                LG_JAPANESE, CI_MINOR},
+	{N_("Japanese (ISO-2022-JP)"),            "ISO-2022-JP",           LG_JAPANESE, CI_MINOR},
+	{N_("Japanese (Shift_JIS)"),              "CP932",             LG_JAPANESE, CI_MINOR},
+	{N_("Korean (EUC-KR)"),                   "EUC-KR",                LG_KOREAN, CI_MINOR},
+	{N_("Korean (ISO-2022-KR)"),              "ISO-2022-KR",           LG_KOREAN, CI_MINOR},
+	{N_("Korean (JOHAB)"),                    "x-johab",               LG_KOREAN, CI_MINOR},
+	{N_("Korean (UHC)"),                      "x-windows-949",         LG_KOREAN, CI_MINOR},
+	{N_("Nordic (ISO-8859-10)"),              "ISO-8859-10",           LG_OTHER, CI_MINOR},
+	{N_("Romanian (MacRomanian)"),            "x-mac-romanian",        LG_OTHER, CI_MINOR},
+	{N_("Romanian (ISO-8859-16)"),            "ISO-8859-16",           LG_OTHER, CI_MINOR},
+	{N_("South European (ISO-8859-3)"),       "ISO-8859-3",            LG_OTHER, CI_MINOR},
+	{N_("Thai (TIS-620)"),                    "TIS-620",               LG_OTHER, CI_MINOR},
+	{N_("Turkish (IBM-857)"),                 "IBM857",                LG_TURKISH, CI_MINOR},
+	{N_("Turkish (ISO-8859-9)"),              "ISO-8859-9",            LG_TURKISH, CI_MINOR},
+	{N_("Turkish (MacTurkish)"),              "x-mac-turkish",         LG_TURKISH, CI_MINOR},
+	{N_("Turkish (Windows-1254)"),            "windows-1254",          LG_TURKISH, CI_MINOR},
+	{N_("Unicode (UTF-7)"),                   "UTF-7",                 LG_UNICODE, CI_MINOR},
+	{N_("Unicode (UTF-8)"),                   "UTF-8",                 LG_UNICODE, CI_MAJOR},
+	{N_("Unicode (UTF-16BE)"),                "UTF-16BE",              LG_UNICODE, CI_MINOR},
+	{N_("Unicode (UTF-16LE)"),                "UTF-16LE",              LG_UNICODE, CI_MINOR},
+	{N_("Unicode (UTF-32BE)"),                "UTF-32BE",              LG_UNICODE, CI_MINOR},
+	{N_("Unicode (UTF-32LE)"),                "UTF-32LE",              LG_UNICODE, CI_MINOR},
+	{N_("User Defined"),                      "x-user-defined",        LG_OTHER, CI_MINOR},
+	{N_("Vietnamese (TCVN)"),                 "x-viet-tcvn5712",       LG_VIETNAMESE, CI_MINOR},
+	{N_("Vietnamese (VISCII)"),               "VISCII",                LG_VIETNAMESE, CI_MINOR},
+	{N_("Vietnamese (VPS)"),                  "x-viet-vps",            LG_VIETNAMESE, CI_MINOR},
+	{N_("Vietnamese (Windows-1258)"),         "windows-1258",          LG_VIETNAMESE, CI_MINOR},
+	{N_("Visual Hebrew (ISO-8859-8)"),        "ISO-8859-8",            LG_HEBREW, CI_MINOR},
+	{N_("Western (IBM-850)"),                 "IBM850",                LG_WESTERN, CI_MINOR},
+	{N_("Western (ISO-8859-1)"),              "ISO-8859-1",            LG_WESTERN, CI_MAJOR},
+	{N_("Western (ISO-8859-15)"),             "ISO-8859-15",           LG_WESTERN, CI_MINOR},
+	{N_("Western (MacRoman)"),                "x-mac-roman",           LG_WESTERN, CI_MINOR},
+	{N_("Western (Windows-1252)"),            "windows-1252",          LG_WESTERN, CI_MINOR},
+	/* charsets without possibly translatable names */
+	{"T61.8bit",                              "T61.8bit",              LG_OTHER, CI_MINOR},
+	{"x-imap4-modified-utf7",                 "x-imap4-modified-utf7", LG_UNICODE, CI_MINOR},
+	{"x-u-escaped",                           "x-u-escaped",           LG_OTHER, CI_MINOR},
+	{NULL,                                    NULL,                    LG_LAST, 0}
+};
+
+static int
+charset_order (const void *_a, const void *_b)
+{
+	const CharsetInfo *a = (const CharsetInfo *)_a;
+	const CharsetInfo *b = (const CharsetInfo *)_b;
+
+	if (a->lgroup != b->lgroup)
+		return (int)b->lgroup - (int)a->lgroup;
+
+	if (a->imp != b->imp)
+		return (int)b->imp - (int)a->imp;
+
+	return strcmp (a->collate_key, b->collate_key);
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* name -> CharsetInfo* mapping */
+static GHashTable *encoding_hash;
+
+struct _GOCharmapSel {
+	GtkHBox box;
+	GOOptionMenu *encodings;
+	GtkMenu *encodings_menu;
+	GOCharmapSelTestDirection test;
+};
+
+typedef struct {
+	GtkHBoxClass parent_class;
+
+	gboolean (* charmap_changed) (GOCharmapSel *cs, char const *new_charmap);
+} GOCharmapSelClass;
+
+
+typedef GOCharmapSel Cs;
+typedef GOCharmapSelClass CsClass;
+
+/* Signals we emit */
+enum {
+	CHARMAP_CHANGED,
+	LAST_SIGNAL
+};
+
+enum {
+	PROP_0,
+	PROP_TEST_DIRECTION
+};
+
+
+
+
+static guint cs_signals[LAST_SIGNAL] = { 0 };
+
+static void cs_set_property      (GObject          *object,
+				  guint             prop_id,
+				  const GValue     *value,
+				  GParamSpec       *pspec);
+
+static void cs_get_property      (GObject          *object,
+				  guint             prop_id,
+				  GValue           *value,
+				  GParamSpec       *pspec);
+
+static gboolean
+iconv_supported (const char *to, const char *from)
+{
+	GIConv ic = g_iconv_open (to, from);
+	if (ic == NULL || ic == (GIConv)-1)
+		return FALSE;
+
+	g_iconv_close (ic);
+	return TRUE;
+}
+
+const char *
+go_charmap_sel_get_encoding_name (G_GNUC_UNUSED GOCharmapSel *cs,
+				  const char *encoding)
+{
+	CharsetInfo const *ci;
+
+	g_return_val_if_fail (encoding != NULL, NULL);
+
+	ci = g_hash_table_lookup (encoding_hash, encoding);
+	return ci ? _(ci->charset_title) : NULL;
+}
+
+static char const *
+get_locale_encoding_name (GOCharmapSel *cs)
+{
+	char const *locale_encoding;
+	char const *name;
+
+	g_get_charset (&locale_encoding);
+	name = go_charmap_sel_get_encoding_name (cs, locale_encoding);
+	return name ? name : locale_encoding;
+}
+
+static void
+encodings_changed_cb (GOOptionMenu *optionmenu, GOCharmapSel *cs)
+{
+	g_return_if_fail (GO_IS_CHARMAP_SEL (cs));
+	g_return_if_fail (optionmenu == cs->encodings);
+
+	g_signal_emit (G_OBJECT (cs),
+		       cs_signals[CHARMAP_CHANGED],
+		       0,
+		       go_charmap_sel_get_encoding (cs));
+}
+
+static void
+set_menu_to_default (GOCharmapSel *cs, gint item)
+{
+	GSList sel = { GINT_TO_POINTER (item - 1), NULL};
+
+	g_return_if_fail (cs != NULL && GO_IS_CHARMAP_SEL (cs));
+
+	go_option_menu_set_history (cs->encodings, &sel);
+}
+
+static gboolean
+cs_mnemonic_activate (GtkWidget *w, gboolean group_cycling)
+{
+	GOCharmapSel *cs = GO_CHARMAP_SEL (w);
+	gtk_widget_grab_focus (GTK_WIDGET (cs->encodings));
+	return TRUE;
+}
+
+static void
+cs_emphasize_label (GtkLabel *label)
+{
+	char *text = g_markup_printf_escaped ("<b>%s</b>",
+					      gtk_label_get_label (label));
+	gtk_label_set_use_markup (label, TRUE);
+	gtk_label_set_label (label, text);
+	g_free (text);
+}
+
+static void
+cs_init (GOCharmapSel *cs)
+{
+	cs->test = GO_CHARMAP_SEL_TO_UTF8;
+
+	cs->encodings = GO_OPTION_MENU (go_option_menu_new ());
+
+	g_signal_connect (G_OBJECT (cs->encodings), "changed",
+                          G_CALLBACK (encodings_changed_cb), cs);
+        gtk_box_pack_start (GTK_BOX (cs), GTK_WIDGET (cs->encodings),
+                            TRUE, TRUE, 0);
+}
+
+
+static void
+cs_build_menu (GOCharmapSel *cs)
+{
+        GtkWidget *item;
+	GtkMenu *menu;
+	LGroupInfo const *lgroup = lgroups;
+	gint lg_cnt = 0;
+
+        menu = GTK_MENU (gtk_menu_new ());
+
+	while (lgroup->group_name) {
+		CharsetInfo const *charset_trans;
+		GtkMenu *submenu = NULL;
+
+		charset_trans = charset_trans_array;
+
+		while (charset_trans->lgroup != LG_LAST) {
+			GtkWidget *subitem;
+			if (charset_trans->lgroup == lgroup->lgroup) {
+				const char *name = (cs->test == GO_CHARMAP_SEL_TO_UTF8)
+					? charset_trans->to_utf8_iconv_name
+					: charset_trans->from_utf8_iconv_name;
+				if (name) {
+					if (!submenu)
+						submenu = GTK_MENU (gtk_menu_new ());
+					subitem = gtk_check_menu_item_new_with_label
+						(_(charset_trans->charset_title));
+					gtk_check_menu_item_set_draw_as_radio (GTK_CHECK_MENU_ITEM (subitem), TRUE);
+					gtk_widget_show (subitem);
+					gtk_menu_shell_append (GTK_MENU_SHELL (submenu),  subitem);
+					if (charset_trans->imp == CI_MAJOR)
+						cs_emphasize_label (GTK_LABEL (gtk_bin_get_child (GTK_BIN (subitem))));
+					g_object_set_data (G_OBJECT (subitem), CHARMAP_NAME_KEY,
+							   (gpointer)name);
+				} else if (0) {
+					g_print ("Unsupported: %s\n", charset_trans->aliases);
+				}
+			}
+			charset_trans++;
+		}
+		if (submenu) {
+			GtkWidget *item =
+				gtk_menu_item_new_with_label (_(lgroup->group_name));
+
+			gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), GTK_WIDGET (submenu));
+			gtk_widget_show (item);
+			gtk_menu_shell_append (GTK_MENU_SHELL (menu),  item);
+			lg_cnt++;
+		}
+                lgroup++;
+        }
+	item = gtk_separator_menu_item_new ();
+	gtk_widget_show (item);
+	gtk_menu_shell_append (GTK_MENU_SHELL (menu),  item);
+	lg_cnt++;
+
+	{
+		char *locale_encoding_menu_title = g_strconcat (_("Locale: "),
+							      get_locale_encoding_name (cs),
+							      NULL);
+		item = gtk_check_menu_item_new_with_label (locale_encoding_menu_title);
+		gtk_check_menu_item_set_draw_as_radio (GTK_CHECK_MENU_ITEM (item), TRUE);
+		g_free (locale_encoding_menu_title);
+		gtk_widget_show (item);
+		gtk_menu_shell_append (GTK_MENU_SHELL (menu),  item);
+		lg_cnt++;
+		cs_emphasize_label (GTK_LABEL (gtk_bin_get_child (GTK_BIN (item))));
+	}
+
+	go_option_menu_set_menu (cs->encodings, GTK_WIDGET (menu));
+	cs->encodings_menu = menu;
+	set_menu_to_default (cs, lg_cnt);
+}
+
+static void
+cs_class_init (GtkWidgetClass *widget_klass)
+{
+	CharsetInfo *ci;
+	size_t i;
+
+	GObjectClass *gobject_class = G_OBJECT_CLASS (widget_klass);
+	widget_klass->mnemonic_activate = cs_mnemonic_activate;
+
+	gobject_class->set_property = cs_set_property;
+	gobject_class->get_property = cs_get_property;
+
+	cs_signals[CHARMAP_CHANGED] =
+		g_signal_new ("charmap_changed",
+			      GO_TYPE_CHARMAP_SEL,
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (GOCharmapSelClass, charmap_changed),
+			      NULL, NULL,
+			      g_cclosure_marshal_VOID__POINTER,
+			      G_TYPE_NONE, 1, G_TYPE_POINTER);
+
+	g_object_class_install_property (gobject_class,
+					 PROP_TEST_DIRECTION,
+					 g_param_spec_uint ("TestDirection",
+							    _("Conversion Direction"),
+							    _("This value determines which iconv test to perform."),
+							    (guint)GO_CHARMAP_SEL_TO_UTF8,
+							    (guint)GO_CHARMAP_SEL_FROM_UTF8,
+							    (guint)GO_CHARMAP_SEL_TO_UTF8,
+							    G_PARAM_READWRITE));
+
+	/* ---------------------------------------- */
+	/* Sort the groups by translated name.  */
+
+	for (i = 0; i < G_N_ELEMENTS (lgroups) - 2; i++) {
+		const char *cgroup_name = lgroups[i].group_name;
+		const char *group_name = _(cgroup_name);
+		lgroups[i].collate_key = g_utf8_collate_key (group_name, -1);
+		if (!lgroups[i].collate_key) {
+			g_warning ("Failed to generate collation key for [%s] [%s]",
+				   cgroup_name, group_name);
+			lgroups[i].collate_key = g_strdup (group_name);
+		}
+	}
+	qsort (lgroups, G_N_ELEMENTS (lgroups) - 2, sizeof (lgroups[0]),
+	       lgroups_order);
+	for (i = 0; i < G_N_ELEMENTS (lgroups) - 2; i++) {
+		g_free (lgroups[i].collate_key);
+		lgroups[i].collate_key = NULL;
+	}
+
+	/* ---------------------------------------- */
+	/* Sort charsets by group/importance/title.  */
+
+	for (i = 0; i < G_N_ELEMENTS (charset_trans_array) - 1; i++) {
+		const char *ctitle = charset_trans_array[i].charset_title;
+		const char *title = _(ctitle);
+		charset_trans_array[i].collate_key = g_utf8_collate_key (title, -1);
+		if (!charset_trans_array[i].collate_key) {
+			g_warning ("Failed to generate collation key for [%s] [%s]",
+				   ctitle, title);
+			charset_trans_array[i].collate_key = g_strdup (title);
+		}
+	}
+	qsort (charset_trans_array, G_N_ELEMENTS (charset_trans_array) - 1,
+	       sizeof (charset_trans_array[0]), charset_order);
+	for (i = 0; i < G_N_ELEMENTS (charset_trans_array) - 1; i++) {
+		g_free (charset_trans_array[i].collate_key);
+		charset_trans_array[i].collate_key = NULL;
+	}
+
+	/* ---------------------------------------- */
+
+	encoding_hash =
+		g_hash_table_new_full (go_ascii_strcase_hash,
+				       go_ascii_strcase_equal,
+				       (GDestroyNotify)g_free,
+				       NULL);
+
+	for (ci = charset_trans_array; ci->charset_title; ci++) {
+		const char *aliases = ci->aliases;
+		char *autoaliases = NULL;
+
+		if (strchr (aliases, '#') == NULL) {
+			/* Sigh.  This sucks quite a lot.  */
+			if (strncmp (aliases, "ISO-", 4) == 0) {
+				autoaliases =
+					g_strconcat (aliases,
+						     "#ISO", aliases + 4,
+						     "#ISO_", aliases + 4,
+						     NULL);
+			}
+
+			if (autoaliases)
+				aliases = autoaliases;
+		}
+
+		ci->to_utf8_iconv_name = ci->from_utf8_iconv_name = NULL;
+		while (aliases) {
+			const char *sep = strchr (aliases, '#');
+			char *alias;
+
+			if (sep) {
+				alias = g_strndup (aliases, sep - aliases);
+				aliases = sep + 1;
+			} else {
+				alias = g_strdup (aliases);
+				aliases = NULL;
+			}
+
+			if (ci->to_utf8_iconv_name == NULL &&
+			    iconv_supported ("UTF-8", alias)) {
+				ci->to_utf8_iconv_name = g_strdup (alias);
+			}
+
+			if (ci->from_utf8_iconv_name == NULL &&
+			    iconv_supported (alias, "UTF-8")) {
+				ci->from_utf8_iconv_name = g_strdup (alias);
+			}
+
+			g_hash_table_insert (encoding_hash, alias, ci);
+		}
+
+		g_free (autoaliases);
+	}
+}
+
+GSF_CLASS (GOCharmapSel, go_charmap_sel,
+	   cs_class_init, cs_init, GTK_TYPE_HBOX)
+
+GtkWidget *
+go_charmap_sel_new (GOCharmapSelTestDirection test)
+{
+	return g_object_new (GO_TYPE_CHARMAP_SEL, "TestDirection", test, NULL);
+}
+
+gchar const *
+go_charmap_sel_get_encoding (GOCharmapSel *cs)
+{
+	GtkMenuItem *selection;
+	char const *locale_encoding;
+	char const *encoding;
+
+	g_get_charset (&locale_encoding);
+
+ 	g_return_val_if_fail (GO_IS_CHARMAP_SEL (cs), locale_encoding);
+
+ 	selection = GTK_MENU_ITEM (go_option_menu_get_history (cs->encodings));
+	encoding = (char const *) g_object_get_data (G_OBJECT (selection),
+						     CHARMAP_NAME_KEY);
+	return encoding ? encoding : locale_encoding;
+}
+
+struct cb_find_entry {
+	const char *enc;
+	gboolean found;
+	int i;
+	GSList *path;
+};
+
+static void
+cb_find_entry (GtkMenuItem *w, struct cb_find_entry *cl)
+{
+	GtkWidget *sub;
+
+	if (cl->found)
+		return;
+
+	sub = gtk_menu_item_get_submenu (w);
+	if (sub) {
+		GSList *tmp = cl->path = g_slist_prepend (cl->path, GINT_TO_POINTER (cl->i));
+		cl->i = 0;
+
+		gtk_container_foreach (GTK_CONTAINER (sub), (GtkCallback)cb_find_entry, cl);
+		if (cl->found)
+			return;
+
+		cl->i = GPOINTER_TO_INT (cl->path->data);
+		cl->path = cl->path->next;
+		g_slist_free_1 (tmp);
+	} else {
+		const char *this_enc =
+			g_object_get_data (G_OBJECT (w), CHARMAP_NAME_KEY);
+		if (this_enc && strcmp (this_enc, cl->enc) == 0) {
+			cl->found = TRUE;
+			cl->path = g_slist_prepend (cl->path, GINT_TO_POINTER (cl->i));
+			cl->path = g_slist_reverse (cl->path);
+			return;
+		}
+	}
+	cl->i++;
+}
+
+gboolean
+go_charmap_sel_set_encoding (GOCharmapSel *cs, const char *enc)
+{
+	struct cb_find_entry cl;
+	CharsetInfo const *ci;
+
+	g_return_val_if_fail (GO_IS_CHARMAP_SEL (cs), FALSE);
+	g_return_val_if_fail (enc != NULL, FALSE);
+
+	ci = g_hash_table_lookup (encoding_hash, enc);
+	if (!ci)
+		return FALSE;
+
+	enc = ci->to_utf8_iconv_name;
+	if (!enc)
+		return FALSE;
+
+	cl.enc = enc;
+	cl.found = FALSE;
+	cl.i = 0;
+	cl.path = NULL;
+
+	gtk_container_foreach (GTK_CONTAINER (cs->encodings_menu),
+			       (GtkCallback)cb_find_entry,
+			       &cl);
+	if (!cl.found)
+		return FALSE;
+
+	go_option_menu_set_history (cs->encodings, cl.path);
+	g_slist_free (cl.path);
+
+	return TRUE;
+}
+
+static void
+cs_set_property (GObject      *object,
+		 guint         prop_id,
+		 const GValue *value,
+		 GParamSpec   *pspec)
+{
+	GOCharmapSel *cs = GO_CHARMAP_SEL (object);
+
+	switch (prop_id) {
+	case PROP_TEST_DIRECTION:
+		cs->test = g_value_get_uint (value);
+		cs_build_menu (cs);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+
+static void
+cs_get_property (GObject     *object,
+		 guint        prop_id,
+		 GValue      *value,
+		 GParamSpec  *pspec)
+{
+	GOCharmapSel *cs = GO_CHARMAP_SEL (object);
+
+	switch (prop_id) {
+	case PROP_TEST_DIRECTION:
+		g_value_set_uint (value, (guint)cs->test);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
diff --git a/lib/goffice/go-charmap-sel.h b/lib/goffice/go-charmap-sel.h
new file mode 100644
index 0000000..15d8400
--- /dev/null
+++ b/lib/goffice/go-charmap-sel.h
@@ -0,0 +1,53 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ *  Copyright (C) 2003 Andreas J. Guelzow
+ *
+ *  based on code by:
+ *  Copyright (C) 2000 Marco Pesenti Gritti
+ *  from the galeon code base
+ *
+ *  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 2, 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, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#ifndef _GO_CHARMAP_SEL_H_
+#define _GO_CHARMAP_SEL_H_
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GO_TYPE_CHARMAP_SEL        (go_charmap_sel_get_type ())
+#define GO_CHARMAP_SEL(obj)        (G_TYPE_CHECK_INSTANCE_CAST((obj), GO_TYPE_CHARMAP_SEL, GOCharmapSel))
+#define GO_IS_CHARMAP_SEL(obj)     (G_TYPE_CHECK_INSTANCE_TYPE((obj), GO_TYPE_CHARMAP_SEL))
+
+typedef struct _GOCharmapSel GOCharmapSel;
+
+typedef enum {
+	GO_CHARMAP_SEL_TO_UTF8 = 0,
+	GO_CHARMAP_SEL_FROM_UTF8
+} GOCharmapSelTestDirection;
+
+GType        go_charmap_sel_get_type (void);
+GtkWidget *  go_charmap_sel_new (GOCharmapSelTestDirection test);
+
+gchar const *go_charmap_sel_get_encoding (GOCharmapSel *cs);
+gboolean     go_charmap_sel_set_encoding (GOCharmapSel *cs, const char *enc);
+
+const char  *go_charmap_sel_get_encoding_name (GOCharmapSel *cs, const char *enc);
+
+G_END_DECLS
+
+#endif /* _GO_CHARMAP_SEL_H_ */
diff --git a/lib/goffice/go-glib-extras.c b/lib/goffice/go-glib-extras.c
new file mode 100644
index 0000000..e385483
--- /dev/null
+++ b/lib/goffice/go-glib-extras.c
@@ -0,0 +1,1082 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * utils.c:  Various utility routines that should have been in glib.
+ *
+ * Authors:
+ *    Miguel de Icaza (miguel at gnu.org)
+ *    Jukka-Pekka Iivonen (iivonen at iki.fi)
+ *    Zbigniew Chyla (cyba at gnome.pl)
+ *    Morten Welinder (terra at gnome.org)
+ */
+#include <goffice/goffice-config.h>
+#include "go-glib-extras.h"
+#include "go-locale.h"
+#include <goffice/app/go-cmd-context.h>
+
+#include <glib/gi18n-lib.h>
+#include <gsf/gsf-impl-utils.h>
+#include <libxml/encoding.h>
+
+#include <stdlib.h>
+#include <math.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <errno.h>
+
+static void
+cb_hash_collect_keys (gpointer key, gpointer value, GSList **accum)
+{
+	*accum = g_slist_prepend (*accum, key);
+}
+
+/**
+ * go_hash_keys :
+ * @hash : #GHashTable
+ *
+ * Collects an unordered list of the keys in @hash.
+ *
+ * Returns: a list which the caller needs to free.
+ * 	The content has not additional references added
+ **/
+GSList *
+go_hash_keys (GHashTable *hash)
+{
+	GSList *accum = NULL;
+	g_hash_table_foreach (hash,
+		(GHFunc )cb_hash_collect_keys, &accum);
+	return accum;
+}
+
+static void
+cb_hash_collect_values (gpointer key, gpointer value, GSList **accum)
+{
+	*accum = g_slist_prepend (*accum, value);
+}
+
+/**
+ * go_hash_values :
+ * @hash : #GHashTable
+ *
+ * Collects an unordered list of the values in @hash.
+ *
+ * Returns: a list which the caller needs to free.
+ * 	The content has not additional references added
+ **/
+GSList *
+go_hash_values (GHashTable *hash)
+{
+	GSList *accum = NULL;
+	g_hash_table_foreach (hash,
+		(GHFunc )cb_hash_collect_values, &accum);
+	return accum;
+}
+
+/***************************************************************************/
+void
+go_ptr_array_insert (GPtrArray *array, gpointer value, int index)
+{
+	if (index < (int)array->len) {
+		int i = array->len - 1;
+		gpointer last = g_ptr_array_index (array, i);
+		g_ptr_array_add (array, last);
+
+		while (i-- > index) {
+			gpointer tmp = g_ptr_array_index (array, i);
+			g_ptr_array_index (array, i + 1) = tmp;
+		}
+		g_ptr_array_index (array, index) = value;
+	} else
+		g_ptr_array_add (array, value);
+}
+
+/**
+ * go_slist_create:
+ * @item1: itionally %NULL
+ * @Varargs : %NULL terminated list of additional items
+ *
+ * Creates a GList from NULL-terminated list of arguments.
+ *
+ * Returns: created list.
+ **/
+GSList *
+go_slist_create (gpointer item1, ...)
+{
+	va_list args;
+	GSList *list = NULL;
+	gpointer item;
+
+	va_start (args, item1);
+	for (item = item1; item != NULL; item = va_arg (args, gpointer)) {
+		list = g_slist_prepend (list, item);
+	}
+	va_end (args);
+
+	return g_slist_reverse (list);
+}
+
+/**
+ * go_slist_map:
+ * @list        : list of some items
+ * @map_func    : mapping function
+ *
+ **/
+GSList *
+go_slist_map (GSList const *list, GOMapFunc map_func)
+{
+	GSList *list_copy = NULL;
+
+	GO_SLIST_FOREACH (list, void, value,
+		GO_SLIST_PREPEND (list_copy, map_func (value))
+	);
+
+	return g_slist_reverse (list_copy);
+}
+
+/**
+ * go_slist_free_custom:
+ * @list: list of some items
+ * @free_func: function freeing list item
+ *
+ * Clears a list, calling @free_func for each list item.
+ **/
+void
+go_slist_free_custom (GSList *list, GFreeFunc free_func)
+{
+	GSList *l;
+
+	for (l = list; l != NULL; l = l->next) {
+		free_func (l->data);
+	}
+	g_slist_free (list);
+}
+
+gint
+go_list_index_custom (GList *list, gpointer data, GCompareFunc cmp_func)
+{
+	GList *l;
+	gint i;
+
+	for (l = list, i = 0; l != NULL; l = l->next, i++) {
+		if (cmp_func (l->data, data) == 0) {
+			return i;
+		}
+	}
+
+	return -1;
+}
+
+/**
+ * go_list_free_custom:
+ * @list: list of some items
+ * @free_func: function freeing list item
+ *
+ * Clears a list, calling @free_func for each list item.
+ *
+ */
+void
+go_list_free_custom (GList *list, GFreeFunc free_func)
+{
+	GList *l;
+
+	for (l = list; l != NULL; l = l->next) {
+		free_func (l->data);
+	}
+	g_list_free (list);
+}
+
+/**
+ * go_strsplit_to_slist:
+ * @str: String to split
+ * @delimiter: Token delimiter
+ *
+ * Splits up string into tokens at delim and returns a string list.
+ *
+ * Returns: string list which you should free after use using function
+ * e_free_string_list().
+ **/
+GSList *
+go_strsplit_to_slist (gchar const *string, gchar delimiter)
+{
+	gchar **token_v;
+	GSList *string_list = NULL;
+	char buf[2] = { '\0', '\0' };
+	gint i;
+
+	buf[0] = delimiter;
+	token_v = g_strsplit (string, buf, 0);
+	if (token_v != NULL) {
+		for (i = 0; token_v[i] != NULL; i++) {
+			string_list = g_slist_prepend (string_list, token_v[i]);
+		}
+		string_list = g_slist_reverse (string_list);
+		g_free (token_v);
+	}
+
+	return string_list;
+}
+
+gint
+go_utf8_collate_casefold (const char *a, const char *b)
+{
+	char *a2 = g_utf8_casefold (a, -1);
+	char *b2 = g_utf8_casefold (b, -1);
+	int res = g_utf8_collate (a2, b2);
+	g_free (a2);
+	g_free (b2);
+	return res;
+}
+
+gint
+go_ascii_strcase_equal (gconstpointer v1, gconstpointer v2)
+{
+	return g_ascii_strcasecmp ((char const *) v1, (char const *)v2) == 0;
+}
+
+/* a char* hash function from ASU */
+guint
+go_ascii_strcase_hash (gconstpointer v)
+{
+	unsigned const char *s = (unsigned const char *)v;
+	unsigned const char *p;
+	guint h = 0, g;
+
+	for(p = s; *p != '\0'; p += 1) {
+		h = ( h << 4 ) + g_ascii_tolower (*p);
+		if ( ( g = h & 0xf0000000 ) ) {
+			h = h ^ (g >> 24);
+			h = h ^ g;
+		}
+	}
+
+	return h /* % M */;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/*
+ * Escapes all backslashes and quotes in a string. It is based on glib's
+ * g_strescape.
+ *
+ * Also adds quotes around the result.
+ */
+void
+go_strescape (GString *target, char const *string)
+{
+	g_string_append_c (target, '"');
+	/* This loop should be UTF-8 safe.  */
+	for (; *string; string++) {
+		switch (*string) {
+		case '"':
+		case '\\':
+			g_string_append_c (target, '\\');
+		default:
+			g_string_append_c (target, *string);
+		}
+	}
+	g_string_append_c (target, '"');
+}
+
+/*
+ * The reverse operation of go_strescape.  Returns a pointer to the
+ * first char after the string on success or NULL on failure.
+ *
+ * First character of the string should be an ASCII character used
+ * for quoting.
+ */
+const char *
+go_strunescape (GString *target, const char *string)
+{
+	char quote = *string++;
+	size_t oldlen = target->len;
+
+	/* This should be UTF-8 safe as long as quote is ASCII.  */
+	while (*string != quote) {
+		if (*string == 0)
+			goto error;
+		else if (*string == '\\') {
+			string++;
+			if (*string == 0)
+				goto error;
+		}
+
+		g_string_append_c (target, *string);
+		string++;
+	}
+
+	return ++string;
+
+ error:
+	g_string_truncate (target, oldlen);
+	return NULL;
+}
+
+void
+go_string_append_gstring (GString *target, const GString *source)
+{
+	g_string_append_len (target, source->str, source->len);
+}
+
+void
+go_string_append_c_n (GString *target, char c, gsize n)
+{
+	gsize len = target->len;
+	g_string_set_size (target, len + n);
+	memset (target->str + len, c, n);
+}
+
+void
+go_string_replace (GString *target,
+		   gsize pos, gssize oldlen,
+		   const char *txt, gssize newlen)
+{
+	gsize cplen;
+
+	g_return_if_fail (target != NULL);
+	g_return_if_fail (pos >= 0);
+	g_return_if_fail (pos <= target->len);
+
+	if (oldlen < 0)
+		oldlen = target->len - pos;
+	if (newlen < 0)
+		newlen = strlen (txt);
+
+	cplen = MIN (oldlen, newlen);
+	memcpy (target->str + pos, txt, cplen);
+
+	pos += cplen;
+	oldlen -= cplen;
+	txt += cplen;
+	newlen -= cplen;
+
+	/*
+	 * At least one of oldlen and newlen is zero now.  We could call
+	 * both erase and insert unconditionally, but erase does not appear
+	 * to handle zero length efficiently.
+	 */
+
+	if (oldlen > 0)
+		g_string_erase (target, pos, oldlen);
+	else if (newlen > 0)
+		g_string_insert_len (target, pos, txt, newlen);
+}
+
+/* ------------------------------------------------------------------------- */
+
+/**
+ * go_utf8_strcapital:
+ * @p: pointer to UTF-8 string
+ * @len: length in bytes, or -1.
+ *
+ * Similar to g_utf8_strup and g_utf8_strup, except that this function
+ * creates a string "Very Much Like: This, One".
+ *
+ * Return value: newly allocated string.
+ **/
+char *
+go_utf8_strcapital (const char *p, gssize len)
+{
+	const char *pend = (len < 0 ? NULL : p + len);
+	GString *res = g_string_sized_new (len < 0 ? 1 : len + 1);
+	gboolean up = TRUE;
+
+	/*
+	 * This does a simple character-by-character mapping and probably
+	 * is not linguistically correct.
+	 */
+
+	for (; (len < 0 || p < pend) && *p; p = g_utf8_next_char (p)) {
+		gunichar c = g_utf8_get_char (p);
+
+		if (g_unichar_isalpha (c)) {
+			if (up ? g_unichar_isupper (c) : g_unichar_islower (c))
+				/* Correct case -- keep the char.  */
+				g_string_append_unichar (res, c);
+			else {
+				char *tmp = up
+					? g_utf8_strup (p, 1)
+					: g_utf8_strdown (p, 1);
+				g_string_append (res, tmp);
+				g_free (tmp);
+			}
+			up = FALSE;
+		} else {
+			g_string_append_unichar (res, c);
+			up = TRUE;
+		}
+	}
+
+	return g_string_free (res, FALSE);
+}
+
+/* ------------------------------------------------------------------------- */
+
+#undef DEBUG_CHUNK_ALLOCATOR
+
+typedef struct _go_mem_chunk_freeblock go_mem_chunk_freeblock;
+typedef struct _go_mem_chunk_block go_mem_chunk_block;
+
+struct _go_mem_chunk_freeblock {
+	go_mem_chunk_freeblock *next;
+};
+
+struct _go_mem_chunk_block {
+	gpointer data;
+	int freecount, nonalloccount;
+	go_mem_chunk_freeblock *freelist;
+#ifdef DEBUG_CHUNK_ALLOCATOR
+	int id;
+#endif
+};
+
+struct _GOMemChunk {
+	char *name;
+	size_t atom_size, user_atom_size, chunk_size, alignment;
+	int atoms_per_block;
+
+	/* List of all blocks.  */
+	GSList *blocklist;
+
+	/* List of blocks that are not full.  */
+	GList *freeblocks;
+
+#ifdef DEBUG_CHUNK_ALLOCATOR
+	int blockid;
+#endif
+};
+
+
+GOMemChunk *
+go_mem_chunk_new (char const *name, size_t user_atom_size, size_t chunk_size)
+{
+	int atoms_per_block;
+	GOMemChunk *res;
+	size_t user_alignment, alignment, atom_size;
+	size_t maxalign = 1 + ((sizeof (void *) - 1) |
+			       (sizeof (long) - 1) |
+			       (sizeof (double) - 1));
+
+	/*
+	 * The alignment that the caller can expect is 2^(lowest_bit_in_size).
+	 * The fancy bit math computes this.  Think it over.
+	 *
+	 * We don't go lower than pointer-size, so this always comes out as
+	 * 4 or 8.  Sometimes, when user_atom_size is a multiple of 8, this
+	 * alignment is bigger than really needed, but we don't know if the
+	 * structure has elements with 8-byte alignment.  In those cases we
+	 * waste memory.
+	 */
+	user_alignment = ((user_atom_size ^ (user_atom_size - 1)) + 1) / 2;
+	alignment = MIN (MAX (user_alignment, sizeof (go_mem_chunk_block *)), maxalign);
+	atom_size = alignment + MAX (user_atom_size, sizeof (go_mem_chunk_freeblock));
+	atoms_per_block = MAX (1, chunk_size / atom_size);
+	chunk_size = atoms_per_block * atom_size;
+
+#ifdef DEBUG_CHUNK_ALLOCATOR
+	g_print ("Created %s with alignment=%d, atom_size=%d (%d), chunk_size=%d.\n",
+		 name, alignment, atom_size, user_atom_size,
+		 chunk_size);
+#endif
+
+	res = g_new (GOMemChunk, 1);
+	res->alignment = alignment;
+	res->name = g_strdup (name);
+	res->user_atom_size = user_atom_size;
+	res->atom_size = atom_size;
+	res->chunk_size = chunk_size;
+	res->atoms_per_block = atoms_per_block;
+	res->blocklist = NULL;
+	res->freeblocks = NULL;
+#ifdef DEBUG_CHUNK_ALLOCATOR
+	res->blockid = 0;
+#endif
+
+	return res;
+}
+
+void
+go_mem_chunk_destroy (GOMemChunk *chunk, gboolean expect_leaks)
+{
+	GSList *l;
+
+	g_return_if_fail (chunk != NULL);
+
+#ifdef DEBUG_CHUNK_ALLOCATOR
+	g_print ("Destroying %s.\n", chunk->name);
+#endif
+	/*
+	 * Since this routine frees all memory allocated for the pool,
+	 * it is sometimes convenient not to free at all.  For such
+	 * cases, don't report leaks.
+	 */
+	if (!expect_leaks) {
+		GSList *l;
+		int leaked = 0;
+
+		for (l = chunk->blocklist; l; l = l->next) {
+			go_mem_chunk_block *block = l->data;
+			leaked += chunk->atoms_per_block - (block->freecount + block->nonalloccount);
+		}
+		if (leaked) {
+			g_warning ("Leaked %d nodes from %s.",
+				   leaked, chunk->name);
+		}
+	}
+
+	for (l = chunk->blocklist; l; l = l->next) {
+		go_mem_chunk_block *block = l->data;
+		g_free (block->data);
+		g_free (block);
+	}
+	g_slist_free (chunk->blocklist);
+	g_list_free (chunk->freeblocks);
+	g_free (chunk->name);
+	g_free (chunk);
+}
+
+gpointer
+go_mem_chunk_alloc (GOMemChunk *chunk)
+{
+	go_mem_chunk_block *block;
+	char *res;
+
+	/* First try the freelist.  */
+	if (chunk->freeblocks) {
+		go_mem_chunk_freeblock *res;
+
+		block = chunk->freeblocks->data;
+		res = block->freelist;
+		if (res) {
+			block->freelist = res->next;
+
+			block->freecount--;
+			if (block->freecount == 0 && block->nonalloccount == 0) {
+				/* Block turned full -- remove it from freeblocks.  */
+				chunk->freeblocks = g_list_delete_link (chunk->freeblocks,
+									chunk->freeblocks);
+			}
+			return res;
+		}
+		/*
+		 * If we get here, the block has free space that was never
+		 * allocated.
+		 */
+	} else {
+		block = g_new (go_mem_chunk_block, 1);
+#ifdef DEBUG_CHUNK_ALLOCATOR
+		block->id = chunk->blockid++;
+		g_print ("Allocating new chunk %d for %s.\n", block->id, chunk->name);
+#endif
+		block->nonalloccount = chunk->atoms_per_block;
+		block->freecount = 0;
+		block->data = g_malloc (chunk->chunk_size);
+		block->freelist = NULL;
+
+		chunk->blocklist = g_slist_prepend (chunk->blocklist, block);
+		chunk->freeblocks = g_list_prepend (chunk->freeblocks, block);
+	}
+
+	res = (char *)block->data +
+		(chunk->atoms_per_block - block->nonalloccount--) * chunk->atom_size;
+	*((go_mem_chunk_block **)res) = block;
+
+	if (block->nonalloccount == 0 && block->freecount == 0) {
+		/* Block turned full -- remove it from freeblocks.  */
+		chunk->freeblocks = g_list_delete_link (chunk->freeblocks, chunk->freeblocks);
+	}
+
+	return res + chunk->alignment;
+}
+
+gpointer
+go_mem_chunk_alloc0 (GOMemChunk *chunk)
+{
+	gpointer res = go_mem_chunk_alloc (chunk);
+	memset (res, 0, chunk->user_atom_size);
+	return res;
+}
+
+void
+go_mem_chunk_free (GOMemChunk *chunk, gpointer mem)
+{
+	go_mem_chunk_freeblock *fb = mem;
+	go_mem_chunk_block *block =
+		*((go_mem_chunk_block **)((char *)mem - chunk->alignment));
+
+#if 0
+	/*
+	 * This is useful when the exact location of a leak needs to be
+	 * pin-pointed.
+	 */
+	memset (mem, 0, chunk->user_atom_size);
+#endif
+
+	fb->next = block->freelist;
+	block->freelist = fb;
+	block->freecount++;
+
+	if (block->freecount == 1 && block->nonalloccount == 0) {
+		/* Block turned non-full.  */
+		chunk->freeblocks = g_list_prepend (chunk->freeblocks, block);
+	} else if (block->freecount == chunk->atoms_per_block) {
+		/* Block turned all-free.  */
+
+#ifdef DEBUG_CHUNK_ALLOCATOR
+		g_print ("Releasing chunk %d for %s.\n", block->id, chunk->name);
+#endif
+		/*
+		 * FIXME -- this could be faster if we rolled our own lists.
+		 * Hopefully, however, (a) the number of blocks is small,
+		 * and (b) the freed block might be near the beginning ("top")
+		 * of the stacks.
+		 */
+		chunk->blocklist = g_slist_remove (chunk->blocklist, block);
+		chunk->freeblocks = g_list_remove (chunk->freeblocks, block);
+
+		g_free (block->data);
+		g_free (block);
+	}
+}
+
+/*
+ * Loop over all non-freed memory in the chunk.  It's safe to allocate or free
+ * from the chunk in the callback.
+ */
+void
+go_mem_chunk_foreach_leak (GOMemChunk *chunk, GFunc cb, gpointer user)
+{
+	GSList *l, *leaks = NULL;
+
+	for (l = chunk->blocklist; l; l = l->next) {
+		go_mem_chunk_block *block = l->data;
+		if (chunk->atoms_per_block - (block->freecount + block->nonalloccount) > 0) {
+			char *freed = g_new0 (char, chunk->atoms_per_block);
+			go_mem_chunk_freeblock *fb = block->freelist;
+			int i;
+
+			while (fb) {
+				char *atom = (char *)fb - chunk->alignment;
+				int no = (atom - (char *)block->data) / chunk->atom_size;
+				freed[no] = 1;
+				fb = fb->next;
+			}
+
+			for (i = chunk->atoms_per_block - block->nonalloccount - 1; i >= 0; i--) {
+				if (!freed[i]) {
+					char *atom = (char *)block->data + i * chunk->atom_size;
+					leaks = g_slist_prepend (leaks, atom + chunk->alignment);
+				}
+			}
+			g_free (freed);
+		}
+	}
+
+	g_slist_foreach (leaks, cb, user);
+	g_slist_free (leaks);
+}
+
+int
+go_str_compare (void const *x, void const *y)
+{
+	if (x == y)
+		return 0;
+
+	if (x == NULL || y == NULL)
+		return x ? -1 : 1;
+
+	return strcmp (x, y);
+}
+
+
+const char *
+go_guess_encoding (const char *raw, size_t len, const char *user_guess,
+		   char **utf8_str)
+{
+	int try;
+	gboolean debug = FALSE;
+
+	g_return_val_if_fail (raw != NULL, NULL);
+
+	for (try = 1; 1; try++) {
+		char const *guess = NULL;
+		GError *error = NULL;
+		char *utf8_data;
+
+		switch (try) {
+		case 1: guess = user_guess; break;
+		case 2: g_get_charset (&guess); break;
+		case 3: {
+			xmlCharEncoding enc =
+				xmlDetectCharEncoding ((const unsigned char*)raw, len);
+			switch (enc) {
+			case XML_CHAR_ENCODING_ERROR:
+			case XML_CHAR_ENCODING_NONE:
+				break;
+			case XML_CHAR_ENCODING_UTF16LE:
+				/* Default would give "UTF-16".  */
+				guess = "UTF-16LE";
+				break;
+			case XML_CHAR_ENCODING_UTF16BE:
+				/* Default would give "UTF-16".  */
+				guess = "UTF-16BE";
+				break;
+			default:
+				guess = xmlGetCharEncodingName (enc);
+			}
+			break;
+		}
+		case 4: guess = "ASCII"; break;
+		case 5: guess = "ISO-8859-1"; break;
+		case 6: guess = "UTF-8"; break;
+		default: return NULL;
+		}
+
+		if (!guess)
+			continue;
+
+		if (debug)
+			g_print ("Trying %s as encoding.\n", guess);
+
+		utf8_data = g_convert (raw, len, "UTF-8", guess,
+				       NULL, NULL, &error);
+		if (!error) {
+			/*
+			 * We can actually fail this test when gues is UTF-8,
+			 * see #401588.
+			 */
+			if (!g_utf8_validate (utf8_data, -1, NULL))
+				continue;
+			if (debug)
+				g_print ("Guessed %s as encoding.\n", guess);
+			if (utf8_str)
+				*utf8_str = utf8_data;
+			else
+				g_free (utf8_data);
+			return guess;
+		}
+
+		g_error_free (error);
+	}
+}
+
+/**
+ * go_get_real_name :
+ *
+ * Returns: a utf8 encoded string with the current user name.
+ * 	Caller should _NOT_ free the result.
+ **/
+char const *
+go_get_real_name (void)
+{
+	/* We will leak this.  */
+	static char *go_real_name = NULL;
+
+	if (go_real_name == NULL) {
+		char const *name = getenv ("NAME");
+		if (name == NULL)
+			name = g_get_real_name ();
+		if (name == NULL)
+			name = g_get_user_name ();
+		if (name != NULL)
+			(void) go_guess_encoding (name, strlen (name),
+				NULL, &go_real_name);
+		else
+			go_real_name = (char *)"unknown";
+	}
+	return go_real_name;
+}
+
+/**
+ * go_destroy_password :
+ * @passwd : The buffer to clear
+ *
+ * Overwrite a string holding a password.  This is a separate routine to
+ * ensure that the compiler does not try to outsmart us.
+ *
+ * Note: this does not free the memory.
+ **/
+void
+go_destroy_password (char *passwd)
+{
+	memset (passwd, 0, strlen (passwd));
+}
+
+
+/**
+ * go_object_toggle:
+ * @object : #GObject
+ * @property_name : name
+ *
+ * Toggle a boolean object property.
+ **/
+void
+go_object_toggle (gpointer object, const gchar *property_name)
+{
+	gboolean value = FALSE;
+	GParamSpec *pspec;
+
+	g_return_if_fail (G_IS_OBJECT (object));
+	g_return_if_fail (property_name != NULL);
+
+	pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object), property_name);
+	if (!pspec ||
+	    !G_IS_PARAM_SPEC_BOOLEAN (pspec) ||
+	    ((pspec->flags & (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)) !=
+	     G_PARAM_READWRITE)) {
+		g_warning ("%s: object class `%s' has no boolean property named `%s' that can be both read and written.",
+			   G_STRFUNC,
+			   G_OBJECT_TYPE_NAME (object),
+			   property_name);
+		return;
+	}
+
+	/* And now, the actual action.  */
+	g_object_get (object, property_name, &value, NULL);
+	g_object_set (object, property_name, !value, NULL);
+}
+
+
+gboolean
+go_object_set_property (GObject *obj, const char *property_name,
+			const char *user_prop_name, const char *value,
+			GError **err,
+			const char *error_template)
+{
+	GParamSpec *pspec;
+
+	if (err) *err = NULL;
+
+	g_return_val_if_fail (G_IS_OBJECT (obj), TRUE);
+	g_return_val_if_fail (property_name != NULL, TRUE);
+	g_return_val_if_fail (user_prop_name != NULL, TRUE);
+	g_return_val_if_fail (value != NULL, TRUE);
+
+	pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (obj),
+					      property_name);
+	g_return_val_if_fail (pspec != NULL, TRUE);
+
+	if (G_IS_PARAM_SPEC_STRING (pspec)) {
+		g_object_set (obj, property_name, value, NULL);
+		return FALSE;
+	}
+
+	if (G_IS_PARAM_SPEC_BOOLEAN (pspec)) {
+		gboolean b;
+
+		if (go_utf8_collate_casefold (value, go_locale_boolean_name (TRUE)) == 0 ||
+		    go_utf8_collate_casefold (value, _("yes")) == 0 ||
+		    g_ascii_strcasecmp (value, "TRUE") == 0 ||
+		    g_ascii_strcasecmp (value, "yes") == 0 ||
+		    strcmp (value, "1") == 0)
+			b = TRUE;
+		else if (go_utf8_collate_casefold (value, go_locale_boolean_name (FALSE)) == 0 ||
+		    go_utf8_collate_casefold (value, _("no")) == 0 ||
+		    g_ascii_strcasecmp (value, "FALSE") == 0 ||
+		    g_ascii_strcasecmp (value, "no") == 0 ||
+		    strcmp (value, "0") == 0)
+			b = FALSE;
+		else
+			goto error;
+
+		g_object_set (obj, property_name, b, NULL);
+		return FALSE;
+	}
+
+	if (G_IS_PARAM_SPEC_ENUM (pspec)) {
+		GEnumClass *eclass = ((GParamSpecEnum *)pspec)->enum_class;
+		GEnumValue *ev;
+
+		ev = g_enum_get_value_by_name (eclass, value);
+		if (!ev) ev = g_enum_get_value_by_nick (eclass, value);
+
+		if (!ev)
+			goto error;
+
+		g_object_set (obj, property_name, ev->value, NULL);
+		return FALSE;
+	}
+
+	error:
+		if (err)
+			*err = g_error_new (go_error_invalid (), 0,
+					    error_template,
+					    user_prop_name,
+					    value);
+		return TRUE;
+}
+
+
+
+
+/*
+ * Collect all rw properties and their values.
+ */
+GSList *
+go_object_properties_collect (GObject *obj)
+{
+	GSList *res = NULL;
+	guint n;
+	GParamSpec **pspecs =
+		g_object_class_list_properties (G_OBJECT_GET_CLASS (obj),
+						&n);
+
+	while (n--) {
+		GParamSpec *pspec = pspecs[n];
+		if ((pspec->flags & (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)) ==
+		    G_PARAM_READWRITE) {
+			GValue *value = g_new0 (GValue, 1);
+			g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec));
+			g_object_get_property (obj, pspec->name, value);
+			res = g_slist_prepend (res, value);
+			res = g_slist_prepend (res, pspec);
+		}
+	}
+
+	g_free (pspecs);
+	return res;
+}
+
+void
+go_object_properties_apply (GObject *obj, GSList *props, gboolean changed_only)
+{
+	GValue current = { 0, };
+
+	for (; props; props = props->next->next) {
+		GParamSpec *pspec = props->data;
+		const GValue *value = props->next->data;
+		gboolean doit;
+
+		if (changed_only) {
+			g_value_init (&current,
+				      G_PARAM_SPEC_VALUE_TYPE (pspec));
+			g_object_get_property (obj, pspec->name, &current);
+			doit = g_param_values_cmp (pspec, &current, value);
+#if 0
+			g_print ("%2d:  old: [%s]   new: [%s]\n",
+				 g_param_values_cmp (pspec, &current, value),
+				 g_strdup_value_contents (value),
+				 g_strdup_value_contents (&current));
+#endif
+			g_value_unset (&current);
+		} else
+			doit = TRUE;
+
+		if (doit)
+			g_object_set_property (obj, pspec->name, value);
+	}
+}
+
+void
+go_object_properties_free (GSList *props)
+{
+	GSList *l;
+
+	for (l = props; l; l = l->next->next) {
+		GValue *value = l->next->data;
+		g_value_unset (value);
+		g_free (value);
+	}
+
+	g_slist_free (props);
+}
+
+
+/**
+ * go_parse_key_value:
+ * @options: Options string.
+ * @err: Reference to store GError if parsing fails.
+ * @handler: Handler to call for each key-value pair.
+ * @user: user pointer.
+ */
+gboolean
+go_parse_key_value (const char *options,
+		    GError **err,
+		    gboolean (*handler) (const char *name,
+					 const char *value,
+					 GError **err,
+					 gpointer user),
+		    gpointer user)
+{
+	GString *sname = g_string_new (NULL);
+	GString *svalue = g_string_new (NULL);
+	gboolean res = FALSE;
+
+	if (err) *err = NULL;
+
+	while (1) {
+		const char *p;
+
+		g_string_truncate (sname, 0);
+		g_string_truncate (svalue, 0);
+
+		while (g_unichar_isspace (g_utf8_get_char (options)))
+			options = g_utf8_next_char (options);
+
+		if (*options == 0)
+			break;
+
+		if (*options == '"' || *options == '\'') {
+			options = go_strunescape (sname, options);
+			if (!options)
+				goto open_string;
+		} else {
+			p = options;
+			while (strchr ("-!_.,:;|/$%#@~", *options) ||
+			       g_unichar_isalnum (g_utf8_get_char (options)))
+				options = g_utf8_next_char (options);
+			g_string_append_len (sname, p, options - p);
+			if (p == options)
+				goto syntax;
+		}
+
+		while (g_unichar_isspace (g_utf8_get_char (options)))
+			options = g_utf8_next_char (options);
+		if (*options != '=')
+			goto syntax;
+		options++;
+		while (g_unichar_isspace (g_utf8_get_char (options)))
+			options = g_utf8_next_char (options);
+
+		if (*options == '"' || *options == '\'') {
+			options = go_strunescape (svalue, options);
+			if (!options)
+				goto open_string;
+		} else {
+			p = options;
+			while (*options && !
+			       g_unichar_isspace (g_utf8_get_char (options)))
+				options = g_utf8_next_char (options);
+			g_string_append_len (svalue, p, options - p);
+		}
+
+		if (handler (sname->str, svalue->str, err, user)) {
+			res = TRUE;
+			break;
+		}
+	}
+
+done:
+	g_string_free (sname, TRUE);
+	g_string_free (svalue, TRUE);
+
+	return res;
+
+open_string:
+	if (err)
+		*err = g_error_new (go_error_invalid (), 0,
+				    _("Quoted string not terminated"));
+	res = TRUE;
+	goto done;
+
+syntax:
+	if (err)
+		*err = g_error_new (go_error_invalid (), 0,
+				    _("Syntax error"));
+	res = TRUE;
+	goto done;
+}
diff --git a/lib/goffice/go-glib-extras.h b/lib/goffice/go-glib-extras.h
new file mode 100644
index 0000000..4733a5d
--- /dev/null
+++ b/lib/goffice/go-glib-extras.h
@@ -0,0 +1,119 @@
+#ifndef GO_GLIB_EXTRAS_H
+#define GO_GLIB_EXTRAS_H
+
+#include <goffice/goffice.h>
+
+G_BEGIN_DECLS
+
+/* Misc convenience routines that would be nice to have in glib */
+
+typedef gpointer (*GOMapFunc) (gpointer value);
+
+void	 go_ptr_array_insert	(GPtrArray *array, gpointer value, int index);
+
+GSList	*go_hash_keys		(GHashTable *hash);
+GSList	*go_hash_values		(GHashTable *hash);
+
+GSList	*go_slist_map		(GSList const *list, GOMapFunc map_func);
+GSList	*go_slist_create	(gpointer item1, ...);
+void	 go_slist_free_custom	(GSList *list, GFreeFunc free_func);
+#define	 go_string_slist_copy(list) go_slist_map (list, (GOMapFunc) g_strdup)
+GSList	*go_strsplit_to_slist	(char const *str, gchar delimiter);
+#define GO_SLIST_FOREACH(list,valtype,val,stmnt) \
+G_STMT_START { \
+	GSList const *go_l; \
+	for (go_l = (list); go_l != NULL; go_l = go_l->next) { \
+		valtype *val = go_l->data; \
+		stmnt \
+		; \
+	} \
+} G_STMT_END
+#define GO_SLIST_PREPEND(list,item) \
+	(list = g_slist_prepend (list, item))
+#define GO_SLIST_APPEND(list,item) \
+	(list = g_slist_append (list, item))
+#define GO_SLIST_REMOVE(list,item) \
+	(list = g_slist_remove (list, item))
+#define GO_SLIST_CONCAT(list_a,list_b) \
+	(list_a = g_slist_concat (list_a, list_b))
+#define GO_SLIST_REVERSE(list) \
+	(list = g_slist_reverse (list))
+#define GO_SLIST_SORT(list,cmp_func) \
+	(list = g_slist_sort (list, cmp_func))
+
+gint go_list_index_custom (GList *list, gpointer data, GCompareFunc cmp_func);
+void go_list_free_custom  (GList *list, GFreeFunc free_func);
+#define GO_LIST_FOREACH(list,valtype,val,stmnt) \
+G_STMT_START { \
+	GList *go_l; \
+	for (go_l = (list); go_l != NULL; go_l = go_l->next) { \
+		valtype *val = go_l->data; \
+		stmnt \
+		; \
+	} \
+} G_STMT_END
+#define GO_LIST_PREPEND(list,item) \
+	(list = g_list_prepend (list, item))
+#define GO_LIST_APPEND(list,item) \
+	(list = g_list_append (list, item))
+#define GO_LIST_REMOVE(list,item) \
+	(list = g_list_remove (list, item))
+#define GO_LIST_CONCAT(list_a,list_b) \
+	(list_a = g_list_concat (list_a, list_b))
+#define GO_LIST_REVERSE(list) \
+	(list = g_list_reverse (list))
+#define GO_LIST_SORT(list,cmp_func) \
+	(list = g_list_sort (list, cmp_func))
+
+int	    go_str_compare		(void const *x, void const *y);
+guint	    go_ascii_strcase_hash	(gconstpointer v);
+gint	    go_ascii_strcase_equal	(gconstpointer v, gconstpointer v2);
+gint	    go_utf8_collate_casefold	(char const *a, char const *b);
+char	   *go_utf8_strcapital		(char const *p, gssize len);
+void	    go_strescape		(GString *target, char const *str);
+char const *go_strunescape		(GString *target, char const *str);
+void	    go_string_append_gstring	(GString *target, const GString *src);
+void        go_string_append_c_n        (GString *target, char c, gsize n);
+void        go_string_replace           (GString *target,
+					 gsize pos, gssize oldlen,
+					 const char *txt, gssize newlen);
+
+char const *go_guess_encoding		(char const *raw, gsize len,
+					 char const *user_guess,
+					 char **utf8_str);
+
+char const *go_get_real_name		(void);
+void	    go_destroy_password	(char *passwd);
+
+GOMemChunk  *go_mem_chunk_new		(char const *name, gsize user_atom_size, gsize chunk_size);
+void	     go_mem_chunk_destroy	(GOMemChunk *chunk, gboolean expect_leaks);
+gpointer     go_mem_chunk_alloc		(GOMemChunk *chunk);
+gpointer     go_mem_chunk_alloc0	(GOMemChunk *chunk);
+void         go_mem_chunk_free		(GOMemChunk *chunk, gpointer mem);
+void         go_mem_chunk_foreach_leak	(GOMemChunk *chunk, GFunc cb, gpointer user);
+
+void	go_object_toggle             (gpointer object,
+				      const gchar *property_name);
+gboolean go_object_set_property (GObject *obj, const char *property_name,
+				 const char *user_prop_name, const char *value,
+				 GError **err,
+				 const char *error_template);
+GSList *go_object_properties_collect (GObject *obj);
+void    go_object_properties_apply   (GObject *obj,
+				      GSList *props,
+				      gboolean changed_only);
+void    go_object_properties_free    (GSList *props);
+
+typedef gboolean (*GOParseKeyValueFunc) (const char *name,
+		  const char *value,
+		  GError **err,
+		  gpointer user);
+
+gboolean go_parse_key_value (const char *options,
+			     GError **err,
+			     GOParseKeyValueFunc handler,
+			     gpointer user);
+
+G_END_DECLS
+
+#endif /* GO_GLIB_EXTRAS_H */
diff --git a/lib/goffice/go-optionmenu.c b/lib/goffice/go-optionmenu.c
new file mode 100644
index 0000000..c5c3c30
--- /dev/null
+++ b/lib/goffice/go-optionmenu.c
@@ -0,0 +1,478 @@
+/*
+ * go-optionmenu.c
+ *
+ * Copyright (C) 2002 Andreas J. Guelzow <aguelzow at taliesin.ca>
+ * Copyright (C) 2006 Morten Welinder (terra at gnome.org)
+ *
+ * based extensively on:
+ *
+ * GTK - The GIMP Toolkit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * Modified by the GTK+ Team and others 1997-2000.  See the GTK AUTHORS
+ * file for a list of people on the GTK+ Team.  See the ChangeLog
+ * files for a list of changes.  These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
+ * USA.
+ */
+
+#include <goffice/goffice-config.h>
+#include "go-optionmenu.h"
+#include <goffice/gtk/go-gtk-compat.h>
+
+#include <gdk/gdkkeysyms.h>
+#include <glib/gi18n-lib.h>
+
+
+enum {
+	CHANGED,
+	LAST_SIGNAL
+};
+
+enum {
+	PROP_0,
+	PROP_MENU
+};
+
+static GtkButtonClass *parent_class = NULL;
+static guint signals[LAST_SIGNAL] = { 0 };
+
+
+GtkWidget*
+go_option_menu_new (void)
+{
+	return g_object_new (GO_TYPE_OPTION_MENU, NULL);
+}
+
+static void
+go_option_menu_detacher (GtkWidget *widget, GtkMenu *menu)
+{
+#if 0
+	GOOptionMenu *option_menu = GO_OPTION_MENU (widget);
+	/* What?  */
+#endif
+}
+
+static void
+go_option_menu_update_contents (GOOptionMenu *option_menu)
+{
+	const char *text;
+	GtkWidget *w;
+	g_return_if_fail (GO_IS_OPTION_MENU (option_menu));
+
+	w = gtk_bin_get_child (GTK_BIN (option_menu->selected));
+	text = g_object_get_data (G_OBJECT (w), "option-menu-text");
+
+	if (!text && GTK_IS_LABEL (w))
+		text = gtk_label_get_text (GTK_LABEL (w));
+
+	if (!text)
+		text = "";
+
+#if 0
+	g_print ("text = \"%s\"\n", text);
+#endif
+
+	gtk_label_set_text (option_menu->button_label, text);
+}
+
+static void
+go_option_menu_select_item (GOOptionMenu *option_menu, GtkMenuItem *item)
+{
+	if (item == option_menu->selected)
+		return;
+
+	if (GTK_IS_CHECK_MENU_ITEM (option_menu->selected))
+		gtk_check_menu_item_set_active
+			(GTK_CHECK_MENU_ITEM (option_menu->selected),
+			 FALSE);
+
+	option_menu->selected = item;
+
+	if (GTK_IS_CHECK_MENU_ITEM (item))
+		gtk_check_menu_item_set_active
+			(GTK_CHECK_MENU_ITEM (option_menu->selected),
+			 TRUE);
+
+	go_option_menu_update_contents (option_menu);
+}
+
+
+static void
+go_option_menu_position (GtkMenu  *menu,
+			 gint     *x,
+			 gint     *y,
+			 gboolean *push_in,
+			 gpointer  user_data)
+{
+	GOOptionMenu *option_menu = user_data;
+	GtkWidget *widget;
+	GtkRequisition requisition;
+	GList *children;
+	gint screen_width;
+	gint menu_xpos;
+	gint menu_ypos;
+	gint menu_width;
+	GtkAllocation allocation;
+
+	widget = GTK_WIDGET (option_menu);
+
+	gtk_widget_get_child_requisition (GTK_WIDGET (menu), &requisition);
+	menu_width = requisition.width;
+
+	gdk_window_get_origin (gtk_widget_get_window (widget), &menu_xpos, &menu_ypos);
+
+	gtk_widget_get_allocation (widget, &allocation);
+	menu_xpos += allocation.x;
+	menu_ypos += allocation.y + allocation.height / 2 - 2;
+
+	children = gtk_container_get_children (GTK_CONTAINER (option_menu->menu));
+	while (children) {
+		GtkWidget *child = children->data;
+
+		if (GTK_IS_CHECK_MENU_ITEM (child) &&
+		    gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (child))) {
+			gtk_widget_get_child_requisition (child, &requisition);
+			menu_ypos -= requisition.height / 2;
+			break;
+		}
+
+		if (gtk_widget_get_visible (child)) {
+			gtk_widget_get_child_requisition (child, &requisition);
+			menu_ypos -= requisition.height;
+		}
+
+		children = children->next;
+	}
+
+	screen_width = gdk_screen_get_width (gtk_widget_get_screen (widget));
+
+	if (menu_xpos + menu_width > screen_width)
+		menu_xpos -= (menu_xpos + menu_width) - screen_width;
+	if (menu_xpos < 0)
+		menu_xpos = 0;
+
+	*x = menu_xpos;
+	*y = menu_ypos;
+	*push_in = TRUE;
+}
+
+
+static gint
+go_option_menu_button_press (GtkWidget      *widget,
+			     GdkEventButton *event)
+{
+	GOOptionMenu *option_menu;
+
+	g_return_val_if_fail (GO_IS_OPTION_MENU (widget), FALSE);
+	g_return_val_if_fail (event != NULL, FALSE);
+
+	option_menu = GO_OPTION_MENU (widget);
+
+	if (event->type == GDK_BUTTON_PRESS && event->button == 1) {
+		gtk_menu_popup (GTK_MENU (option_menu->menu), NULL, NULL,
+				go_option_menu_position, option_menu,
+				event->button, event->time);
+
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+static gint
+go_option_menu_key_press (GtkWidget   *widget,
+			  GdkEventKey *event)
+{
+	GOOptionMenu *option_menu = GO_OPTION_MENU (widget);
+
+	switch (event->keyval) {
+	case GDK_KP_Space:
+	case GDK_space:
+		gtk_menu_popup (GTK_MENU (option_menu->menu), NULL, NULL,
+				go_option_menu_position, option_menu,
+				0, event->time);
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+
+static void
+cb_select (GtkMenuItem *item, GOOptionMenu *option_menu)
+{
+	go_option_menu_select_item (option_menu, item);
+	g_signal_emit (option_menu, signals[CHANGED], 0);
+}
+
+static void
+handle_menu_signals (GOOptionMenu *option_menu, gboolean connect)
+{
+	GList *children = gtk_container_get_children
+		(GTK_CONTAINER (option_menu->menu));
+
+	while (children) {
+		GtkWidget *child = children->data;
+		children = g_list_remove (children, child);
+
+		if (GTK_IS_MENU_ITEM (child)) {
+			GtkWidget *sub =
+				gtk_menu_item_get_submenu (GTK_MENU_ITEM (child));
+
+			if (sub)
+				children = g_list_concat (children,
+							  gtk_container_get_children (GTK_CONTAINER (sub)));
+			else if (connect)
+				g_signal_connect (child, "activate",
+						  G_CALLBACK (cb_select),
+						  option_menu);
+
+			else
+				g_signal_handlers_disconnect_by_func
+					(child, G_CALLBACK (cb_select), option_menu);
+
+		}
+	}
+}
+
+void
+go_option_menu_set_menu (GOOptionMenu *option_menu,
+			 GtkWidget *menu)
+{
+	GtkMenuShell *shell;
+
+	g_return_if_fail (GO_IS_OPTION_MENU (option_menu));
+	g_return_if_fail (GTK_IS_MENU_SHELL (menu));
+
+	shell = (GtkMenuShell *)menu;
+	if (option_menu->menu == shell)
+		return;
+
+	if (option_menu->menu) {
+		if (gtk_menu_shell_get_active (option_menu->menu))
+			gtk_menu_shell_cancel (option_menu->menu);
+
+		handle_menu_signals (option_menu, FALSE);
+
+		gtk_menu_detach (GTK_MENU (option_menu->menu));
+		g_object_unref (option_menu->menu);
+	}
+
+	option_menu->menu = shell;
+
+	if (shell) {
+		g_object_ref (shell);
+
+		gtk_menu_attach_to_widget (GTK_MENU (shell),
+					   GTK_WIDGET (option_menu),
+					   go_option_menu_detacher);
+
+		handle_menu_signals (option_menu, TRUE);
+
+		go_option_menu_select_item (option_menu,
+					    GTK_MENU_ITEM (gtk_menu_get_active (GTK_MENU (shell))));
+	}
+
+	g_object_notify (G_OBJECT (option_menu), "menu");
+}
+
+void
+go_option_menu_set_history (GOOptionMenu *option_menu, GSList *selection)
+{
+	g_return_if_fail (selection != NULL);
+	g_return_if_fail (GO_IS_OPTION_MENU (option_menu));
+
+	if (option_menu->menu) {
+		GtkMenuShell *menu = option_menu->menu;
+
+		while (1) {
+			int n = GPOINTER_TO_INT (selection->data);
+			GtkMenuItem *item = g_list_nth_data (gtk_container_get_children (GTK_CONTAINER (menu)), n);
+			selection = selection->next;
+			if (selection)
+				menu = GTK_MENU_SHELL (gtk_menu_item_get_submenu (item));
+			else {
+				go_option_menu_select_item (option_menu, item);
+				break;
+			}
+		}
+	}
+}
+
+/**
+ * go_option_menu_get_history:
+ * @option_menu: a #GOOptionMenu
+ *
+ * Retrieves the currently selected menu item.
+ *
+ * Return value: the selected menu_item
+ **/
+
+GtkWidget *
+go_option_menu_get_history (GOOptionMenu *option_menu)
+{
+	return GTK_WIDGET (option_menu->selected);
+}
+
+
+static void
+go_option_menu_set_property (GObject            *object,
+			     guint               prop_id,
+			     const GValue       *value,
+			     GParamSpec         *pspec)
+{
+	GOOptionMenu *option_menu = GO_OPTION_MENU (object);
+
+	switch (prop_id) {
+	case PROP_MENU:
+		go_option_menu_set_menu (option_menu, g_value_get_object (value));
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+go_option_menu_get_property (GObject            *object,
+			     guint               prop_id,
+			     GValue             *value,
+			     GParamSpec         *pspec)
+{
+	GOOptionMenu *option_menu = GO_OPTION_MENU (object);
+
+	switch (prop_id) {
+	case PROP_MENU:
+		g_value_set_object (value, option_menu->menu);
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+go_option_menu_destroy (GtkObject *object)
+{
+	GOOptionMenu *option_menu;
+
+	g_return_if_fail (GO_IS_OPTION_MENU (object));
+
+	option_menu = GO_OPTION_MENU (object);
+
+	if (option_menu->menu) {
+		gtk_widget_destroy (GTK_WIDGET (option_menu->menu));
+		g_object_unref (option_menu->menu);
+		option_menu->menu = NULL;
+	}
+	option_menu->selected = NULL;
+
+	GTK_OBJECT_CLASS (parent_class)->destroy (object);
+}
+
+static void
+go_option_menu_class_init (GOOptionMenuClass *class)
+{
+	GObjectClass *gobject_class = (GObjectClass*) class;
+	GtkObjectClass *object_class = (GtkObjectClass*) class;
+	GtkWidgetClass *widget_class = (GtkWidgetClass*) class;
+
+	parent_class = g_type_class_peek_parent (class);
+
+	signals[CHANGED] =
+		g_signal_new ("changed",
+			      G_OBJECT_CLASS_TYPE (class),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (GOOptionMenuClass, changed),
+			      NULL, NULL,
+			      g_cclosure_marshal_VOID__VOID,
+			      G_TYPE_NONE, 0);
+
+	gobject_class->set_property = go_option_menu_set_property;
+	gobject_class->get_property = go_option_menu_get_property;
+	object_class->destroy = go_option_menu_destroy;
+	widget_class->button_press_event = go_option_menu_button_press;
+	widget_class->key_press_event = go_option_menu_key_press;
+
+	g_object_class_install_property (gobject_class,
+					 PROP_MENU,
+					 g_param_spec_object ("menu",
+							      _("Menu"),
+							      _("The menu of options"),
+							      GTK_TYPE_MENU,
+							      G_PARAM_READABLE | G_PARAM_WRITABLE));
+}
+
+static void
+go_option_menu_init (GOOptionMenu *option_menu)
+{
+	GtkBox *box;
+	GtkWidget *arrow, *sep;
+
+	gtk_widget_set_can_focus (GTK_WIDGET (option_menu), TRUE);
+	gtk_widget_set_can_default (GTK_WIDGET (option_menu), FALSE);
+	gtk_widget_set_receives_default (GTK_WIDGET (option_menu), FALSE);
+
+	box = GTK_BOX (gtk_hbox_new (FALSE, FALSE));
+
+	option_menu->menu = NULL;
+	option_menu->selected = NULL;
+
+	option_menu->button_label = GTK_LABEL (gtk_label_new (""));
+	gtk_box_pack_start (box, GTK_WIDGET (option_menu->button_label),
+			    FALSE, TRUE, 0);
+
+	arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
+	g_object_set (arrow, "xalign", 0.75, NULL);
+	gtk_box_pack_end (box, arrow, FALSE, FALSE, 0);
+
+	sep = gtk_vseparator_new ();
+	gtk_box_pack_end (box, sep, FALSE, FALSE, 0);
+
+	gtk_container_add (GTK_CONTAINER (option_menu), GTK_WIDGET (box));
+}
+
+GType
+go_option_menu_get_type (void)
+{
+	static GType option_menu_type = 0;
+
+	if (!option_menu_type) {
+		static const GTypeInfo option_menu_info =
+			{
+				sizeof (GOOptionMenuClass),
+				NULL,		/* base_init */
+				NULL,		/* base_finalize */
+				(GClassInitFunc) go_option_menu_class_init,
+				NULL,		/* class_finalize */
+				NULL,		/* class_data */
+				sizeof (GOOptionMenu),
+				0,		/* n_preallocs */
+				(GInstanceInitFunc) go_option_menu_init,
+			};
+
+		option_menu_type =
+			g_type_register_static (GTK_TYPE_BUTTON, "GOOptionMenu",
+						&option_menu_info, 0);
+	}
+
+	return option_menu_type;
+}
diff --git a/lib/goffice/go-optionmenu.h b/lib/goffice/go-optionmenu.h
new file mode 100644
index 0000000..65252b3
--- /dev/null
+++ b/lib/goffice/go-optionmenu.h
@@ -0,0 +1,85 @@
+/*
+ * go-optionmenu.h
+ *
+ * Copyright (C) 2002-2005 Andreas J. Guelzow <aguelzow at taliesin.ca>
+ *
+ * based extensively on:
+ *
+ * GTK - The GIMP Toolkit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * Modified by the GTK+ Team and others 1997-2000.  See the GTK AUTHORS
+ * file for a list of people on the GTK+ Team.  See the ChangeLog
+ * files for a list of changes.  These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
+ * USA.
+ */
+
+#ifndef _GO_OPTIONMENU_H_
+#define _GO_OPTIONMENU_H_
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GO_TYPE_OPTION_MENU              (go_option_menu_get_type ())
+#define GO_OPTION_MENU(obj)              (G_TYPE_CHECK_INSTANCE_CAST ((obj), GO_TYPE_OPTION_MENU, GOOptionMenu))
+#define GO_OPTION_MENU_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), GO_TYPE_OPTION_MENU, GOOptionMenuClass))
+#define GO_IS_OPTION_MENU(obj)           (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GO_TYPE_OPTION_MENU))
+#define GO_IS_OPTION_MENU_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GO_TYPE_OPTION_MENU))
+#define GO_OPTION_MENU_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), GO_TYPE_OPTION_MENU, GOOptionMenuClass))
+
+typedef struct _GOOptionMenu       GOOptionMenu;
+typedef struct _GOOptionMenuClass  GOOptionMenuClass;
+
+struct _GOOptionMenu
+{
+	GtkButton button;
+
+	/*< private >*/
+
+	GtkMenuShell *menu;
+	GtkMenuItem *selected;
+	GtkLabel *button_label;
+};
+
+struct _GOOptionMenuClass
+{
+  GtkButtonClass parent_class;
+
+  void (*changed) (GOOptionMenu *option_menu);
+
+  /* Padding for future expansion */
+  void (*_gtk_reserved1) (void);
+  void (*_gtk_reserved2) (void);
+  void (*_gtk_reserved3) (void);
+  void (*_gtk_reserved4) (void);
+};
+
+
+GType      go_option_menu_get_type    (void) G_GNUC_CONST;
+GtkWidget* go_option_menu_new         (void);
+void       go_option_menu_set_menu    (GOOptionMenu *option_menu,
+				       GtkWidget *menu);
+void       go_option_menu_set_history (GOOptionMenu *option_menu,
+				       GSList *selection);
+GtkWidget *go_option_menu_get_history (GOOptionMenu *option_menu);
+
+G_END_DECLS
+
+#endif /* _GO_OPTIONMENU_H_ */

commit 20eecb05da4a02d10b5b9a0592a8903bfe102291
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Wed Jan 27 20:20:43 2016 +0100

    Small amendements to gnc-csv-account-map
    
    - add to POTFILES.in
    - const correctness fixup (c++ is picky about this)

diff --git a/src/import-export/csv-imp/gnc-csv-account-map.c b/src/import-export/csv-imp/gnc-csv-account-map.c
index e53fffe..29d7c6b 100644
--- a/src/import-export/csv-imp/gnc-csv-account-map.c
+++ b/src/import-export/csv-imp/gnc-csv-account-map.c
@@ -62,7 +62,7 @@ account_imap_destroy (GncImportMatchMap *imap)
  * search the existing mappings for the account
  * linked to the import string.
  **************************************************/
-Account * gnc_csv_account_map_search (gchar *map_string)
+Account * gnc_csv_account_map_search (const gchar *map_string)
 {
     Account *root, *account = NULL;
     GList   *accts, *ptr;
@@ -144,7 +144,7 @@ gnc_csv_account_map_load_mappings (GtkTreeModel *mappings_store)
  * change the existing mappings
  **************************************************/
 void
-gnc_csv_account_map_change_mappings (Account *old_account, Account *new_account, gchar *map_string)
+gnc_csv_account_map_change_mappings (Account *old_account, Account *new_account, const gchar *map_string)
 {
     GncImportMatchMap *tmp_imap;
 
diff --git a/src/import-export/csv-imp/gnc-csv-account-map.h b/src/import-export/csv-imp/gnc-csv-account-map.h
index 217b07b..6f5a669 100644
--- a/src/import-export/csv-imp/gnc-csv-account-map.h
+++ b/src/import-export/csv-imp/gnc-csv-account-map.h
@@ -40,12 +40,12 @@ void gnc_csv_account_map_load_mappings (GtkTreeModel *mappings_store);
 /** Update the import mappings.
  *
  */
-void gnc_csv_account_map_change_mappings (Account *old_account, Account *new_account, gchar *map_string);
+void gnc_csv_account_map_change_mappings (Account *old_account, Account *new_account, const gchar *map_string);
 
 /** Returns a pointer to the account that matches the import string.
  *
  * @return A pointer to an account.
  */
-Account * gnc_csv_account_map_search (gchar *map_string);
+Account * gnc_csv_account_map_search (const gchar *map_string);
 
 #endif



Summary of changes:
 CMakeLists.txt                                     |    5 +-
 configure.ac                                       |    7 +-
 lib/Makefile.am                                    |    2 +-
 lib/goffice/Makefile.am                            |   19 +
 lib/goffice/README                                 |   13 +
 lib/goffice/go-charmap-sel.c                       |  746 +++++
 lib/goffice/go-charmap-sel.h                       |   54 +
 lib/goffice/go-glib-extras.c                       |  139 +
 lib/goffice/go-glib-extras.h                       |   16 +
 lib/goffice/go-optionmenu.c                        |  445 +++
 lib/goffice/go-optionmenu.h                        |   83 +
 lib/stf/Makefile.am                                |   13 -
 lib/stf/README                                     |    2 -
 lib/stf/stf-parse.c                                | 1414 ---------
 lib/stf/stf-parse.h                                |  112 -
 make-gnucash-potfiles.in                           |    2 +-
 po/POTFILES.in                                     |   22 +-
 src/app-utils/gnc-ui-util.c                        |    2 +-
 src/app-utils/gnc-ui-util.h                        |    2 +-
 src/import-export/csv-exp/Makefile.am              |    5 +-
 .../csv-exp/csv-transactions-export.c              |  346 +--
 src/import-export/csv-imp/CMakeLists.txt           |   39 +-
 src/import-export/csv-imp/Makefile.am              |   38 +-
 .../csv-imp/assistant-csv-fixed-trans-import.c     |  797 -----
 .../csv-imp/assistant-csv-fixed-trans-import.glade |  502 ----
 .../csv-imp/assistant-csv-fixed-trans-import.h     |   73 -
 .../csv-imp/assistant-csv-trans-import.c           | 3176 --------------------
 .../csv-imp/assistant-csv-trans-import.cpp         | 2013 +++++++++++++
 .../csv-imp/assistant-csv-trans-import.glade       | 1232 ++++----
 src/import-export/csv-imp/csv-fixed-trans-import.c |  695 -----
 src/import-export/csv-imp/csv-fixed-trans-import.h |   53 -
 src/import-export/csv-imp/gnc-csv-account-map.c    |    4 +-
 src/import-export/csv-imp/gnc-csv-account-map.h    |    5 +-
 src/import-export/csv-imp/gnc-csv-model.c          | 1352 ---------
 src/import-export/csv-imp/gnc-csv-model.h          |  149 -
 src/import-export/csv-imp/gnc-csv-tokenizer.cpp    |   67 +
 src/import-export/csv-imp/gnc-csv-tokenizer.hpp    |   64 +
 src/import-export/csv-imp/gnc-csv-trans-settings.c |  353 ---
 .../csv-imp/gnc-csv-trans-settings.cpp             |  399 +++
 src/import-export/csv-imp/gnc-csv-trans-settings.h |  100 -
 .../csv-imp/gnc-csv-trans-settings.hpp             |  119 +
 src/import-export/csv-imp/gnc-dummy-tokenizer.cpp  |   31 +
 src/import-export/csv-imp/gnc-dummy-tokenizer.hpp  |   62 +
 src/import-export/csv-imp/gnc-fw-tokenizer.cpp     |  195 ++
 src/import-export/csv-imp/gnc-fw-tokenizer.hpp     |   83 +
 .../csv-imp/gnc-plugin-csv-import-ui.xml           |    1 -
 src/import-export/csv-imp/gnc-plugin-csv-import.c  |   14 -
 src/import-export/csv-imp/gnc-tokenizer.cpp        |   95 +
 src/import-export/csv-imp/gnc-tokenizer.hpp        |   91 +
 src/import-export/csv-imp/gnc-trans-props.cpp      |  755 +++++
 src/import-export/csv-imp/gnc-trans-props.hpp      |  190 ++
 src/import-export/csv-imp/gnc-tx-import.cpp        |  925 ++++++
 src/import-export/csv-imp/gnc-tx-import.hpp        |  198 ++
 src/import-export/csv-imp/test/CMakeLists.txt      |   28 +-
 src/import-export/csv-imp/test/Makefile.am         |   78 +-
 src/import-export/csv-imp/test/test-csv-imp.c      |   60 -
 src/import-export/csv-imp/test/test-tokenizer.cpp  |  246 ++
 src/import-export/csv-imp/test/test-tx-import.cpp  |  211 ++
 .../csv-imp/test/utest-gnc-csv-model.c             |  462 ---
 src/libqof/qof/test/test-qofbackend.c              |    2 +-
 60 files changed, 8237 insertions(+), 10169 deletions(-)
 create mode 100644 lib/goffice/Makefile.am
 create mode 100644 lib/goffice/README
 create mode 100644 lib/goffice/go-charmap-sel.c
 create mode 100644 lib/goffice/go-charmap-sel.h
 create mode 100644 lib/goffice/go-glib-extras.c
 create mode 100644 lib/goffice/go-glib-extras.h
 create mode 100644 lib/goffice/go-optionmenu.c
 create mode 100644 lib/goffice/go-optionmenu.h
 delete mode 100644 lib/stf/Makefile.am
 delete mode 100644 lib/stf/README
 delete mode 100644 lib/stf/stf-parse.c
 delete mode 100644 lib/stf/stf-parse.h
 delete mode 100644 src/import-export/csv-imp/assistant-csv-fixed-trans-import.c
 delete mode 100644 src/import-export/csv-imp/assistant-csv-fixed-trans-import.glade
 delete mode 100644 src/import-export/csv-imp/assistant-csv-fixed-trans-import.h
 delete mode 100644 src/import-export/csv-imp/assistant-csv-trans-import.c
 create mode 100644 src/import-export/csv-imp/assistant-csv-trans-import.cpp
 delete mode 100644 src/import-export/csv-imp/csv-fixed-trans-import.c
 delete mode 100644 src/import-export/csv-imp/csv-fixed-trans-import.h
 delete mode 100644 src/import-export/csv-imp/gnc-csv-model.c
 delete mode 100644 src/import-export/csv-imp/gnc-csv-model.h
 create mode 100644 src/import-export/csv-imp/gnc-csv-tokenizer.cpp
 create mode 100644 src/import-export/csv-imp/gnc-csv-tokenizer.hpp
 delete mode 100644 src/import-export/csv-imp/gnc-csv-trans-settings.c
 create mode 100644 src/import-export/csv-imp/gnc-csv-trans-settings.cpp
 delete mode 100644 src/import-export/csv-imp/gnc-csv-trans-settings.h
 create mode 100644 src/import-export/csv-imp/gnc-csv-trans-settings.hpp
 create mode 100644 src/import-export/csv-imp/gnc-dummy-tokenizer.cpp
 create mode 100644 src/import-export/csv-imp/gnc-dummy-tokenizer.hpp
 create mode 100644 src/import-export/csv-imp/gnc-fw-tokenizer.cpp
 create mode 100644 src/import-export/csv-imp/gnc-fw-tokenizer.hpp
 create mode 100644 src/import-export/csv-imp/gnc-tokenizer.cpp
 create mode 100644 src/import-export/csv-imp/gnc-tokenizer.hpp
 create mode 100644 src/import-export/csv-imp/gnc-trans-props.cpp
 create mode 100644 src/import-export/csv-imp/gnc-trans-props.hpp
 create mode 100644 src/import-export/csv-imp/gnc-tx-import.cpp
 create mode 100644 src/import-export/csv-imp/gnc-tx-import.hpp
 delete mode 100644 src/import-export/csv-imp/test/test-csv-imp.c
 create mode 100644 src/import-export/csv-imp/test/test-tokenizer.cpp
 create mode 100644 src/import-export/csv-imp/test/test-tx-import.cpp
 delete mode 100644 src/import-export/csv-imp/test/utest-gnc-csv-model.c



More information about the gnucash-changes mailing list